mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
26 Commits
v33.0.0-be
...
v31.0.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d41e6ea9b | ||
|
|
e282ff6e4a | ||
|
|
282ffa10e5 | ||
|
|
72d85e90ac | ||
|
|
2fc89a6b2c | ||
|
|
12bc4dc6c0 | ||
|
|
cdf508a734 | ||
|
|
1dd99250c3 | ||
|
|
d2cb956a75 | ||
|
|
004ca01049 | ||
|
|
ec4ffa99b6 | ||
|
|
844265303c | ||
|
|
aee588b650 | ||
|
|
8f51e8fb5d | ||
|
|
773777f209 | ||
|
|
9437dd3ea8 | ||
|
|
fb5ffd2876 | ||
|
|
2d36065a15 | ||
|
|
e19963b0ac | ||
|
|
9c631ea744 | ||
|
|
9ab77e77b1 | ||
|
|
eb56416cb5 | ||
|
|
703241e1e7 | ||
|
|
0a8ed258da | ||
|
|
7ef5402599 | ||
|
|
a799513b2e |
@@ -27,7 +27,8 @@
|
||||
]
|
||||
},
|
||||
"vscode": {
|
||||
"extensions": ["joeleinbinder.mojom-language",
|
||||
"extensions": [
|
||||
"joeleinbinder.mojom-language",
|
||||
"rafaelmaiolla.diff",
|
||||
"surajbarkale.ninja",
|
||||
"ms-vscode.cpptools",
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:3d8d44d0f15b05bef6149e448f9cc522111847e9
|
||||
image: ghcr.io/electron/devcontainer:9a43c14f5c19be0359843299f79e736521373adc
|
||||
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
@@ -39,7 +39,6 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
write_config() {
|
||||
echo "
|
||||
{
|
||||
\"goma\": \"$1\",
|
||||
\"root\": \"/workspaces/gclient\",
|
||||
\"remotes\": {
|
||||
\"electron\": {
|
||||
@@ -49,7 +48,7 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
\"gen\": {
|
||||
\"args\": [
|
||||
\"import(\\\"//electron/build/args/testing.gn\\\")\",
|
||||
\"import(\\\"/home/builduser/.electron_build_tools/third_party/goma.gn\\\")\"
|
||||
\"use_remoteexec = true\"
|
||||
],
|
||||
\"out\": \"Testing\"
|
||||
},
|
||||
@@ -57,26 +56,18 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
|
||||
\"CHROMIUM_BUILDTOOLS_PATH\": \"/workspaces/gclient/src/buildtools\",
|
||||
\"GIT_CACHE_PATH\": \"/workspaces/gclient/.git-cache\"
|
||||
},
|
||||
\"\$schema\": \"file:///home/builduser/.electron_build_tools/evm-config.schema.json\"
|
||||
\"\$schema\": \"file:///home/builduser/.electron_build_tools/evm-config.schema.json\",
|
||||
\"configValidationLevel\": \"strict\",
|
||||
\"reclient\": \"$1\",
|
||||
\"goma\": \"none\",
|
||||
\"preserveXcode\": 5
|
||||
}
|
||||
" >$buildtools/configs/evm.testing.json
|
||||
}
|
||||
|
||||
# Start out as cache only
|
||||
write_config cache-only
|
||||
write_config remote_exec
|
||||
|
||||
e use testing
|
||||
|
||||
# Attempt to auth to the goma service via codespaces tokens
|
||||
# if it works we can use the goma cluster
|
||||
export NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN
|
||||
if e d goma_auth login; then
|
||||
echo "$GITHUB_USER has GOMA access - switching to cluster mode"
|
||||
write_config cluster
|
||||
fi
|
||||
e use testing
|
||||
else
|
||||
echo "build-tools testing config already exists"
|
||||
|
||||
# Re-auth with the goma cluster regardless.
|
||||
NOTGOMA_CODESPACES_TOKEN=$GITHUB_TOKEN e d goma_auth login || true
|
||||
fi
|
||||
|
||||
15
BUILD.gn
15
BUILD.gn
@@ -81,18 +81,11 @@ if (is_linux) {
|
||||
]
|
||||
}
|
||||
|
||||
# Generates electron_gtk_stubs.h header which contains
|
||||
# stubs for extracting function ptrs from the gtk library.
|
||||
# Function signatures for which stubs are required should be
|
||||
# declared in electron_gtk.sigs, currently this file contains
|
||||
# signatures for the functions used with native file chooser
|
||||
# implementation. In future, this file can be extended to contain
|
||||
# gtk4 stubs to switch gtk version in runtime.
|
||||
# Generates headers which contain stubs for extracting function ptrs
|
||||
# from the gtk library. Function signatures for which stubs are
|
||||
# required should be declared in the sig files.
|
||||
generate_stubs("electron_gtk_stubs") {
|
||||
sigs = [
|
||||
"shell/browser/ui/electron_gdk_pixbuf.sigs",
|
||||
"shell/browser/ui/electron_gtk.sigs",
|
||||
]
|
||||
sigs = [ "shell/browser/ui/electron_gdk_pixbuf.sigs" ]
|
||||
extra_header = "shell/browser/ui/electron_gtk.fragment"
|
||||
output_name = "electron_gtk_stubs"
|
||||
public_deps = [ "//ui/gtk:gtk_config" ]
|
||||
|
||||
@@ -50,10 +50,6 @@ is_cfi = false
|
||||
# TODO: fix this once sysroots have been updated.
|
||||
use_qt = false
|
||||
|
||||
# https://chromium-review.googlesource.com/c/chromium/src/+/4365718
|
||||
# TODO(codebytere): fix perfetto incompatibility with Node.js.
|
||||
use_perfetto_client_library = false
|
||||
|
||||
# Disables the builtins PGO for V8
|
||||
v8_builtins_profiling_log_file = ""
|
||||
|
||||
|
||||
@@ -600,7 +600,7 @@ Perhaps there are 15 pixels of controls on the left edge, 25 pixels of controls
|
||||
on the right edge and 50 pixels of controls below the player. In order to
|
||||
maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within
|
||||
the player itself we would call this function with arguments of 16/9 and
|
||||
{ width: 40, height: 50 }. The second argument doesn't care where the extra width and height
|
||||
\{ width: 40, height: 50 \}. The second argument doesn't care where the extra width and height
|
||||
are within the content view--only that they exist. Sum any extra width and
|
||||
height areas you have within the overall content view.
|
||||
|
||||
|
||||
@@ -723,7 +723,7 @@ Perhaps there are 15 pixels of controls on the left edge, 25 pixels of controls
|
||||
on the right edge and 50 pixels of controls below the player. In order to
|
||||
maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within
|
||||
the player itself we would call this function with arguments of 16/9 and
|
||||
{ width: 40, height: 50 }. The second argument doesn't care where the extra width and height
|
||||
\{ width: 40, height: 50 \}. The second argument doesn't care where the extra width and height
|
||||
are within the content view--only that they exist. Sum any extra width and
|
||||
height areas you have within the overall content view.
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ dialog.showOpenDialog(mainWindow, {
|
||||
* `dontAddToRecent` _Windows_ - Do not add the item being saved to the recent documents list.
|
||||
* `securityScopedBookmarks` boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||
|
||||
Returns `string | undefined`, the path of the file chosen by the user; if the dialog is cancelled it returns `undefined`.
|
||||
Returns `string`, the path of the file chosen by the user; if the dialog is cancelled it returns an empty string.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -207,7 +207,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
||||
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
|
||||
* `canceled` boolean - whether or not the dialog was canceled.
|
||||
* `filePath` string (optional) - If the dialog is canceled, this will be `undefined`.
|
||||
* `filePath` string - If the dialog is canceled, this will be an empty string.
|
||||
* `bookmark` string (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. (For return values, see [table here](#bookmarks-array).)
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
@@ -66,7 +66,7 @@ requests according to the specified protocol scheme in the `options` object.
|
||||
### `net.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & \{ bypassCustomProtocolHandlers?: boolean \} (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
|
||||
@@ -695,7 +695,7 @@ Returns `Promise<void>` - Resolves when all connections are closed.
|
||||
#### `ses.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & \{ bypassCustomProtocolHandlers?: boolean \} (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ Objects created with `new View` have the following instance methods:
|
||||
* `index` Integer (optional) - Index at which to insert the child view.
|
||||
Defaults to adding the child at the end of the child list.
|
||||
|
||||
If the same View is added to a parent which already contains it, it will be reordered such that
|
||||
it becomes the topmost view.
|
||||
|
||||
#### `view.removeChildView(view)`
|
||||
|
||||
* `view` View - Child view to remove.
|
||||
|
||||
@@ -110,22 +110,51 @@ $ export CHROMIUM_BUILDTOOLS_PATH=`pwd`/buildtools
|
||||
On Windows:
|
||||
|
||||
```sh
|
||||
# cmd
|
||||
$ cd src
|
||||
$ set CHROMIUM_BUILDTOOLS_PATH=%cd%\buildtools
|
||||
|
||||
# PowerShell
|
||||
$ cd src
|
||||
$ $env:CHROMIUM_BUILDTOOLS_PATH = "$(Get-Location)\buildtools"
|
||||
```
|
||||
|
||||
**To generate Testing build config of Electron:**
|
||||
|
||||
On Linux & MacOS
|
||||
|
||||
```sh
|
||||
$ gn gen out/Testing --args="import(\"//electron/build/args/testing.gn\")"
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```sh
|
||||
# cmd
|
||||
$ gn gen out/Testing --args="import(\"//electron/build/args/testing.gn\")"
|
||||
|
||||
# PowerShell
|
||||
gn gen out/Testing --args="import(\`"//electron/build/args/testing.gn\`")"
|
||||
```
|
||||
|
||||
**To generate Release build config of Electron:**
|
||||
|
||||
On Linux & MacOS
|
||||
|
||||
```sh
|
||||
$ gn gen out/Release --args="import(\"//electron/build/args/release.gn\")"
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```sh
|
||||
# cmd
|
||||
$ gn gen out/Release --args="import(\"//electron/build/args/release.gn\")"
|
||||
|
||||
# PowerShell
|
||||
$ gn gen out/Release --args="import(\`"//electron/build/args/release.gn\`")"
|
||||
```
|
||||
|
||||
**Note:** This will generate a `out/Testing` or `out/Release` build directory under `src/` with the testing or release build depending upon the configuration passed above. You can replace `Testing|Release` with another names, but it should be a subdirectory of `out`.
|
||||
|
||||
Also you shouldn't have to run `gn gen` again—if you want to change the build arguments, you can run `gn args out/Testing` to bring up an editor. To see the list of available build configuration options, run `gn args out/Testing --list`.
|
||||
|
||||
@@ -15,7 +15,7 @@ calls, and other compiler optimizations. The only workaround is to build an
|
||||
unoptimized local build.
|
||||
|
||||
The official symbol server URL for Electron is
|
||||
<https://symbols.electronjs.org>.
|
||||
[https://symbols.electronjs.org](https://symbols.electronjs.org).
|
||||
You cannot visit this URL directly, you must add it to the symbol path of your
|
||||
debugging tool. In the examples below, a local cache directory is used to avoid
|
||||
repeatedly fetching the PDB from the server. Replace `c:\code\symbols` with an
|
||||
|
||||
@@ -78,7 +78,8 @@ JavaScript transpilers (e.g. Babel, TypeScript) have historically supported ES M
|
||||
syntax before Node.js supported ESM imports by turning these calls to CommonJS
|
||||
`require` calls.
|
||||
|
||||
<details><summary>Example: @babel/plugin-transform-modules-commonjs</summary>
|
||||
<details>
|
||||
<summary>Example: @babel/plugin-transform-modules-commonjs</summary>
|
||||
|
||||
The `@babel/plugin-transform-modules-commonjs` plugin will transform
|
||||
ESM imports down to `require` calls. The exact syntax will depend on the
|
||||
|
||||
@@ -121,7 +121,7 @@ need to install Node.js themselves as a prerequisite to running your app.
|
||||
|
||||
To check which version of Node.js is running in your app, you can access the global
|
||||
[`process.versions`][] variable in the main process or preload script. You can also reference
|
||||
<https://releases.electronjs.org/releases.json>.
|
||||
[https://releases.electronjs.org/releases.json](https://releases.electronjs.org/releases.json).
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -222,7 +222,8 @@ with CommonJS module syntax:
|
||||
- [app][app], which controls your application's event lifecycle.
|
||||
- [BrowserWindow][browser-window], which creates and manages app windows.
|
||||
|
||||
<details><summary>Module capitalization conventions</summary>
|
||||
<details>
|
||||
<summary>Module capitalization conventions</summary>
|
||||
|
||||
You might have noticed the capitalization difference between the **a**pp
|
||||
and **B**rowser**W**indow modules. Electron follows typical JavaScript conventions here,
|
||||
@@ -231,7 +232,8 @@ Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRende
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Typed import aliases</summary>
|
||||
<details>
|
||||
<summary>Typed import aliases</summary>
|
||||
|
||||
For better type checking when writing TypeScript code, you can choose to import
|
||||
main process modules from `electron/main`.
|
||||
|
||||
@@ -152,7 +152,7 @@ command that can handle the version bumping and tagging for you.
|
||||
#### Bonus: Publishing in GitHub Actions
|
||||
|
||||
Publishing locally can be painful, especially because you can only create distributables
|
||||
for your host operating system (i.e. you can't publish a Window `.exe` file from macOS).
|
||||
for your host operating system (i.e. you can't publish a Windows `.exe` file from macOS).
|
||||
|
||||
A solution for this would be to publish your app via automation workflows
|
||||
such as [GitHub Actions][], which can run tasks in the
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
If you want to embed (third-party) web content in an Electron `BrowserWindow`,
|
||||
there are three options available to you: `<iframe>` tags, `<webview>` tags,
|
||||
and `BrowserViews`. Each one offers slightly different functionality and is
|
||||
and `WebContentsView`. Each one offers slightly different functionality and is
|
||||
useful in different situations. To help you choose between these, this guide
|
||||
explains the differences and capabilities of each option.
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ filenames = {
|
||||
"shell/browser/notifications/linux/notification_presenter_linux.h",
|
||||
"shell/browser/relauncher_linux.cc",
|
||||
"shell/browser/ui/electron_desktop_window_tree_host_linux.cc",
|
||||
"shell/browser/ui/file_dialog_gtk.cc",
|
||||
"shell/browser/ui/file_dialog_linux.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
|
||||
@@ -18,7 +18,7 @@ function makeStreamFromPipe (pipe: any): ReadableStream {
|
||||
try {
|
||||
const rv = await pipe.read(buf);
|
||||
if (rv > 0) {
|
||||
controller.enqueue(buf.subarray(0, rv));
|
||||
controller.enqueue(buf.slice(0, rv));
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ crash_allow_setting_more_options.patch
|
||||
upload_list_add_loadsync_method.patch
|
||||
allow_setting_secondary_label_via_simplemenumodel.patch
|
||||
feat_add_streaming-protocol_registry_to_multibuffer_data_source.patch
|
||||
fix_patch_out_profile_refs_in_accessibility_ui.patch
|
||||
adjust_accessibility_ui_for_electron.patch
|
||||
skip_atk_toolchain_check.patch
|
||||
worker_feat_add_hook_to_notify_script_ready.patch
|
||||
chore_provide_iswebcontentscreationoverridden_with_full_params.patch
|
||||
@@ -128,3 +128,5 @@ fix_getcursorscreenpoint_wrongly_returns_0_0.patch
|
||||
fix_add_support_for_skipping_first_2_no-op_refreshes_in_thumb_cap.patch
|
||||
refactor_expose_file_system_access_blocklist.patch
|
||||
revert_power_update_trace_counter_in_power_monitor.patch
|
||||
cherry-pick-b2cc7b7ac538.patch
|
||||
feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch
|
||||
|
||||
@@ -1,47 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 6 Jul 2020 13:46:06 -0700
|
||||
Subject: fix: patch out Profile refs in accessibility_ui
|
||||
Subject: Adjust accessibility_ui for Electron
|
||||
|
||||
This tweaks Chrome's Accessibility support at chrome://accessibility
|
||||
to make it usable from Electron by removing Profile references.
|
||||
to make it usable from Electron by replacing use of the Profile PrefService
|
||||
with Electron's own PrefService in ElectronBrowserContext. It also removes
|
||||
usage of BrowserList and Browser as we subclass related methods and use our
|
||||
WindowList.
|
||||
|
||||
diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
|
||||
index 45b085ebb5096b2cdd083337371539a249c7a0a8..404a0a8d99eb68a35c0c06fe1b1dbfa3086e8794 100644
|
||||
index 45b085ebb5096b2cdd083337371539a249c7a0a8..9a0a6cb8b37c60641c2c51aa489fbf22cdb123cb 100644
|
||||
--- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
|
||||
+++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
|
||||
@@ -23,7 +23,10 @@
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/chromeos_buildflags.h"
|
||||
+#if 0
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
+#endif
|
||||
+#include "chrome/common/chrome_features.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "chrome/common/webui_url_constants.h"
|
||||
#include "chrome/grit/accessibility_resources.h"
|
||||
@@ -33,6 +36,7 @@
|
||||
#include "content/public/browser/ax_event_notification_details.h"
|
||||
#include "content/public/browser/ax_inspect_factory.h"
|
||||
#include "content/public/browser/browser_accessibility_state.h"
|
||||
+#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/favicon_status.h"
|
||||
#include "content/public/browser/navigation_entry.h"
|
||||
@@ -52,9 +56,11 @@
|
||||
#include "ui/views/accessibility/view_accessibility.h"
|
||||
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
+#if 0
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/browser/ui/browser_list.h"
|
||||
#include "chrome/browser/ui/browser_window.h"
|
||||
+#endif
|
||||
#include "ui/views/accessibility/widget_ax_tree_id_map.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
@@ -169,7 +175,7 @@ base::Value::Dict BuildTargetDescriptor(content::RenderViewHost* rvh) {
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_delegate.h"
|
||||
#include "content/public/browser/web_ui_data_source.h"
|
||||
+#include "electron/shell/browser/electron_browser_context.h"
|
||||
#include "ui/accessibility/accessibility_features.h"
|
||||
#include "ui/accessibility/platform/ax_platform_node.h"
|
||||
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
|
||||
@@ -169,7 +170,7 @@ base::Value::Dict BuildTargetDescriptor(content::RenderViewHost* rvh) {
|
||||
accessibility_mode);
|
||||
}
|
||||
|
||||
@@ -50,27 +30,25 @@ index 45b085ebb5096b2cdd083337371539a249c7a0a8..404a0a8d99eb68a35c0c06fe1b1dbfa3
|
||||
base::Value::Dict BuildTargetDescriptor(Browser* browser) {
|
||||
base::Value::Dict target_data;
|
||||
target_data.Set(kSessionIdField, browser->session_id().id());
|
||||
@@ -203,7 +209,9 @@ void HandleAccessibilityRequestCallback(
|
||||
@@ -203,7 +204,7 @@ void HandleAccessibilityRequestCallback(
|
||||
DCHECK(ShouldHandleAccessibilityRequestCallback(path));
|
||||
|
||||
base::Value::Dict data;
|
||||
+#if 0
|
||||
PrefService* pref = Profile::FromBrowserContext(current_context)->GetPrefs();
|
||||
+#endif
|
||||
- PrefService* pref = Profile::FromBrowserContext(current_context)->GetPrefs();
|
||||
+ PrefService* pref = static_cast<electron::ElectronBrowserContext*>(current_context)->prefs();
|
||||
ui::AXMode mode =
|
||||
content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode();
|
||||
bool is_native_enabled = content::BrowserAccessibilityState::GetInstance()
|
||||
@@ -235,8 +243,7 @@ void HandleAccessibilityRequestCallback(
|
||||
// enabled.
|
||||
@@ -236,7 +237,7 @@ void HandleAccessibilityRequestCallback(
|
||||
data.Set(kViewsAccessibility, features::IsAccessibilityTreeForViewsEnabled());
|
||||
|
||||
- std::string pref_api_type =
|
||||
std::string pref_api_type =
|
||||
- pref->GetString(prefs::kShownAccessibilityApiType);
|
||||
+ std::string pref_api_type = "blink";
|
||||
+ std::string(pref->GetString(prefs::kShownAccessibilityApiType));
|
||||
bool pref_api_type_supported = false;
|
||||
|
||||
std::vector<ui::AXApiType::Type> supported_api_types =
|
||||
@@ -303,11 +310,11 @@ void HandleAccessibilityRequestCallback(
|
||||
@@ -303,11 +304,11 @@ void HandleAccessibilityRequestCallback(
|
||||
data.Set(kPagesField, std::move(page_list));
|
||||
|
||||
base::Value::List browser_list;
|
||||
@@ -80,36 +58,31 @@ index 45b085ebb5096b2cdd083337371539a249c7a0a8..404a0a8d99eb68a35c0c06fe1b1dbfa3
|
||||
browser_list.Append(BuildTargetDescriptor(browser));
|
||||
}
|
||||
-#endif // !BUILDFLAG(IS_ANDROID)
|
||||
+#endif // !BUILDFLAG(IS_ANDROID)
|
||||
+#endif
|
||||
data.Set(kBrowsersField, std::move(browser_list));
|
||||
|
||||
base::Value::List widgets_list;
|
||||
@@ -647,8 +654,10 @@ void AccessibilityUIMessageHandler::SetGlobalString(
|
||||
@@ -647,7 +648,8 @@ void AccessibilityUIMessageHandler::SetGlobalString(
|
||||
const std::string value = CheckJSValue(data.FindString(kValueField));
|
||||
|
||||
if (string_name == kApiTypeField) {
|
||||
+#if 0
|
||||
PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
- PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
+ PrefService* pref = static_cast<electron::ElectronBrowserContext*>(
|
||||
+ web_ui()->GetWebContents()->GetBrowserContext())->prefs();
|
||||
pref->SetString(prefs::kShownAccessibilityApiType, value);
|
||||
+#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,9 +709,13 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree(
|
||||
@@ -700,7 +702,8 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree(
|
||||
AXPropertyFilter::ALLOW_EMPTY);
|
||||
AddPropertyFilters(property_filters, deny, AXPropertyFilter::DENY);
|
||||
|
||||
+#if 0
|
||||
PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
- PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
+ PrefService* pref = static_cast<electron::ElectronBrowserContext*>(
|
||||
+ web_contents->GetBrowserContext())->prefs();
|
||||
ui::AXApiType::Type api_type =
|
||||
ui::AXApiType::From(pref->GetString(prefs::kShownAccessibilityApiType));
|
||||
+#else
|
||||
+ ui::AXApiType::Type api_type = ui::AXApiType::kBlink;
|
||||
+#endif
|
||||
std::string accessibility_contents =
|
||||
web_contents->DumpAccessibilityTree(api_type, property_filters);
|
||||
result.Set(kTreeField, accessibility_contents);
|
||||
@@ -727,6 +740,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
@@ -727,6 +730,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
AXPropertyFilter::ALLOW_EMPTY);
|
||||
AddPropertyFilters(property_filters, deny, AXPropertyFilter::DENY);
|
||||
|
||||
@@ -117,7 +90,7 @@ index 45b085ebb5096b2cdd083337371539a249c7a0a8..404a0a8d99eb68a35c0c06fe1b1dbfa3
|
||||
for (Browser* browser : *BrowserList::GetInstance()) {
|
||||
if (browser->session_id().id() == session_id) {
|
||||
base::Value::Dict result = BuildTargetDescriptor(browser);
|
||||
@@ -739,6 +753,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
@@ -739,6 +743,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -125,25 +98,24 @@ index 45b085ebb5096b2cdd083337371539a249c7a0a8..404a0a8d99eb68a35c0c06fe1b1dbfa3
|
||||
#endif // !BUILDFLAG(IS_ANDROID)
|
||||
// No browser with the specified |session_id| was found.
|
||||
base::Value::Dict result;
|
||||
@@ -807,11 +822,15 @@ void AccessibilityUIMessageHandler::StopRecording(
|
||||
@@ -807,11 +812,13 @@ void AccessibilityUIMessageHandler::StopRecording(
|
||||
}
|
||||
|
||||
ui::AXApiType::Type AccessibilityUIMessageHandler::GetRecordingApiType() {
|
||||
+#if 0
|
||||
PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
- PrefService* pref = Profile::FromWebUI(web_ui())->GetPrefs();
|
||||
- const std::vector<ui::AXApiType::Type> supported_types =
|
||||
- content::AXInspectFactory::SupportedApis();
|
||||
+ PrefService* pref = static_cast<electron::ElectronBrowserContext*>(
|
||||
+ web_ui()->GetWebContents()->GetBrowserContext())->prefs();
|
||||
ui::AXApiType::Type api_type =
|
||||
ui::AXApiType::From(pref->GetString(prefs::kShownAccessibilityApiType));
|
||||
+#else
|
||||
+ ui::AXApiType::Type api_type = ui::AXApiType::kBlink;
|
||||
+#endif
|
||||
+
|
||||
+ const std::vector<ui::AXApiType::Type> supported_types =
|
||||
+ content::AXInspectFactory::SupportedApis();
|
||||
// Check to see if it is in the supported types list.
|
||||
if (std::find(supported_types.begin(), supported_types.end(), api_type) ==
|
||||
supported_types.end()) {
|
||||
@@ -881,8 +900,11 @@ void AccessibilityUIMessageHandler::RequestAccessibilityEvents(
|
||||
@@ -881,8 +888,11 @@ void AccessibilityUIMessageHandler::RequestAccessibilityEvents(
|
||||
// static
|
||||
void AccessibilityUIMessageHandler::RegisterProfilePrefs(
|
||||
user_prefs::PrefRegistrySyncable* registry) {
|
||||
99
patches/chromium/cherry-pick-b2cc7b7ac538.patch
Normal file
99
patches/chromium/cherry-pick-b2cc7b7ac538.patch
Normal file
@@ -0,0 +1,99 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ken Rockot <rockot@google.com>
|
||||
Date: Wed, 8 May 2024 15:32:48 +0000
|
||||
Subject: Viz: Tolerate SinkGroup destruction during submit
|
||||
|
||||
Fixed: 339266700
|
||||
Change-Id: I8c0ea8c540948016346b00db64fe33260d2446f0
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5523748
|
||||
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Reviewed-by: Jonathan Ross <jonross@chromium.org>
|
||||
Commit-Queue: Ken Rockot <rockot@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1298119}
|
||||
|
||||
diff --git a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
index a43e274a920a7cc189652c29eb2fe4a09ab66ded..9fefc2446d9c95964db512e4c98654c3fcc4e8b4 100644
|
||||
--- a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
+++ b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
#include "components/viz/service/frame_sinks/frame_sink_bundle_impl.h"
|
||||
|
||||
+#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/functional/bind.h"
|
||||
+#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ref.h"
|
||||
+#include "base/memory/weak_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/viz/service/frame_sinks/compositor_frame_sink_impl.h"
|
||||
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
|
||||
@@ -45,6 +48,10 @@ class FrameSinkBundleImpl::SinkGroup : public BeginFrameObserver {
|
||||
|
||||
bool IsEmpty() const { return frame_sinks_.empty(); }
|
||||
|
||||
+ base::WeakPtr<SinkGroup> GetWeakPtr() {
|
||||
+ return weak_ptr_factory_.GetWeakPtr();
|
||||
+ }
|
||||
+
|
||||
void AddFrameSink(uint32_t sink_id) {
|
||||
frame_sinks_.insert(sink_id);
|
||||
|
||||
@@ -206,6 +213,8 @@ class FrameSinkBundleImpl::SinkGroup : public BeginFrameObserver {
|
||||
std::set<uint32_t> unacked_submissions_;
|
||||
|
||||
BeginFrameArgs last_used_begin_frame_args_;
|
||||
+
|
||||
+ base::WeakPtrFactory<SinkGroup> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
FrameSinkBundleImpl::FrameSinkBundleImpl(
|
||||
@@ -282,8 +291,9 @@ void FrameSinkBundleImpl::SetWantsBeginFrameAcks(uint32_t sink_id) {
|
||||
|
||||
void FrameSinkBundleImpl::Submit(
|
||||
std::vector<mojom::BundledFrameSubmissionPtr> submissions) {
|
||||
- std::set<SinkGroup*> groups;
|
||||
- std::set<SinkGroup*> affected_groups;
|
||||
+ std::map<raw_ptr<SinkGroup>, base::WeakPtr<SinkGroup>> groups;
|
||||
+ std::map<raw_ptr<SinkGroup>, base::WeakPtr<SinkGroup>> affected_groups;
|
||||
+
|
||||
// Count the frame submissions before processing anything. This ensures that
|
||||
// any frames submitted here will be acked together in a batch, and not acked
|
||||
// individually in case they happen to ack synchronously within
|
||||
@@ -294,10 +304,10 @@ void FrameSinkBundleImpl::Submit(
|
||||
// through to the client without batching.
|
||||
for (auto& submission : submissions) {
|
||||
if (auto* group = GetSinkGroup(submission->sink_id)) {
|
||||
- groups.insert(group);
|
||||
+ groups.emplace(group, group->GetWeakPtr());
|
||||
if (submission->data->is_frame()) {
|
||||
group->WillSubmitFrame(submission->sink_id);
|
||||
- affected_groups.insert(group);
|
||||
+ affected_groups.emplace(group, group->GetWeakPtr());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,12 +337,16 @@ void FrameSinkBundleImpl::Submit(
|
||||
}
|
||||
}
|
||||
|
||||
- for (auto* group : groups) {
|
||||
- group->DidFinishFrame();
|
||||
+ for (const auto& [unsafe_group, weak_group] : groups) {
|
||||
+ if (weak_group) {
|
||||
+ weak_group->DidFinishFrame();
|
||||
+ }
|
||||
}
|
||||
|
||||
- for (auto* group : affected_groups) {
|
||||
- group->FlushMessages();
|
||||
+ for (const auto& [unsafe_group, weak_group] : affected_groups) {
|
||||
+ if (weak_group) {
|
||||
+ weak_group->FlushMessages();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Sun, 5 May 2024 09:17:17 +0000
|
||||
Subject: feat: add support for missing dialog features to //shell_dialogs
|
||||
|
||||
This CL adds support for the following features to //shell_dialogs:
|
||||
* buttonLabel - Custom label for the confirmation button.
|
||||
* showHiddenFiles - Show hidden files in dialog.
|
||||
* showOverwriteConfirmation - Whether the user will be presented a confirmation dialog if the user types a file name that already exists.
|
||||
|
||||
This may be partially upstreamed to Chromium in the future.
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.cc b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
index 698f97b23f851c02af037880585c82d4494da63f..0a3b701a701289a2124214e7421a6383ff7f0320 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
@@ -243,6 +243,10 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
|
||||
std::string title_string = base::UTF16ToUTF8(title);
|
||||
|
||||
+ ExtraSettings extra_settings;
|
||||
+ if (params)
|
||||
+ extra_settings = *(static_cast<ExtraSettings*>(params));
|
||||
+
|
||||
set_file_type_index(file_type_index);
|
||||
if (file_types)
|
||||
set_file_types(*file_types);
|
||||
@@ -261,23 +265,23 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
case SELECT_UPLOAD_FOLDER:
|
||||
case SELECT_EXISTING_FOLDER:
|
||||
dialog = CreateSelectFolderDialog(type, title_string, default_path,
|
||||
- owning_window);
|
||||
+ owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_FILE:
|
||||
- dialog = CreateFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_MULTI_FILE:
|
||||
dialog =
|
||||
- CreateMultiFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ CreateMultiFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectMultiFileDialogResponse);
|
||||
break;
|
||||
case SELECT_SAVEAS_FILE:
|
||||
- dialog = CreateSaveAsDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateSaveAsDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
@@ -412,10 +416,14 @@ void SelectFileDialogLinuxGtk::FileNotSelected(GtkWidget* dialog) {
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetOpenLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, GetCancelLabel(),
|
||||
- GTK_RESPONSE_CANCEL, GetOpenLabel(), GTK_RESPONSE_ACCEPT);
|
||||
+ GTK_RESPONSE_CANCEL, button_label, GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
AddFilters(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
@@ -431,6 +439,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
GtkFileChooserSetCurrentFolder(GTK_FILE_CHOOSER(dialog),
|
||||
*last_opened_path());
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),
|
||||
+ settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@@ -438,7 +448,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string = title;
|
||||
if (title_string.empty()) {
|
||||
title_string =
|
||||
@@ -446,11 +457,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE)
|
||||
: l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
|
||||
}
|
||||
- std::string accept_button_label =
|
||||
- (type == SELECT_UPLOAD_FOLDER)
|
||||
- ? l10n_util::GetStringUTF8(
|
||||
- IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
- : GetOpenLabel();
|
||||
+
|
||||
+ std::string accept_button_label = settings.button_label;
|
||||
+ if (accept_button_label.empty()) {
|
||||
+ accept_button_label = (type == SELECT_UPLOAD_FOLDER)
|
||||
+ ? l10n_util::GetStringUTF8(
|
||||
+ IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
+ : GetOpenLabel();
|
||||
+ }
|
||||
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
@@ -472,19 +486,21 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
gtk_file_filter_add_mime_type(only_folders, "inode/directory");
|
||||
gtk_file_filter_add_mime_type(only_folders, "text/directory");
|
||||
gtk_file_chooser_add_filter(chooser, only_folders);
|
||||
- gtk_file_chooser_set_select_multiple(chooser, FALSE);
|
||||
+ gtk_file_chooser_set_select_multiple(chooser, settings.allow_multiple_selection);
|
||||
+ gtk_file_chooser_set_show_hidden(chooser, settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -492,12 +508,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog =
|
||||
+ CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -505,14 +523,17 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
|
||||
-
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetSaveLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
- GetCancelLabel(), GTK_RESPONSE_CANCEL, GetSaveLabel(),
|
||||
+ GetCancelLabel(), GTK_RESPONSE_CANCEL, button_label,
|
||||
GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
|
||||
@@ -538,9 +559,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
// Overwrite confirmation is always enabled in GTK4.
|
||||
if (!GtkCheckVersion(4)) {
|
||||
- gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
|
||||
- TRUE);
|
||||
+ gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
+ GTK_FILE_CHOOSER(dialog), settings.show_overwrite_confirmation);
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.h b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
index 53ae15f14c45ee72abdae172fc4555c9e4b3ff9a..af181afd9db1351cd886ba24dd651c7bf2f8a716 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
@@ -15,6 +15,13 @@
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+struct ExtraSettings {
|
||||
+ std::string button_label;
|
||||
+ bool show_overwrite_confirmation = true;
|
||||
+ bool show_hidden = false;
|
||||
+ bool allow_multiple_selection = false;
|
||||
+};
|
||||
+
|
||||
// Implementation of SelectFileDialog that shows a Gtk common dialog for
|
||||
// choosing a file or folder. This acts as a modal dialog.
|
||||
class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
@@ -90,19 +97,23 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
GtkWidget* CreateSelectFolderDialog(Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateMultiFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateSaveAsDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Removes and returns the |params| associated with |dialog| from
|
||||
// |params_map_|.
|
||||
@@ -121,7 +132,8 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
// Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog.
|
||||
GtkWidget* CreateFileOpenHelper(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Callback for when the user responds to a Save As or Open File dialog.
|
||||
void OnSelectSingleFileDialogResponse(GtkWidget* dialog, int response_id);
|
||||
@@ -1,13 +1,13 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
|
||||
Subject: Make gtk::GetLibGdkPixbuf public
|
||||
|
||||
Allows embedders to get a handle to the gtk and
|
||||
gdk_pixbuf libraries already loaded in the process.
|
||||
Allows embedders to get a handle to the gdk_pixbuf
|
||||
library already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411dbcbd96457 100644
|
||||
index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c70379358 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -66,11 +66,6 @@ void* GetLibGio() {
|
||||
@@ -22,20 +22,7 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411db
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
return libgtk4;
|
||||
}
|
||||
|
||||
-void* GetLibGtk() {
|
||||
- if (GtkCheckVersion(4))
|
||||
- return GetLibGtk4();
|
||||
- return GetLibGtk3();
|
||||
-}
|
||||
-
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -134,6 +123,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -134,6 +129,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -43,29 +30,20 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411db
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGtk() {
|
||||
+ if (GtkCheckVersion(4))
|
||||
+ return GetLibGtk4();
|
||||
+ return GetLibGtk3();
|
||||
+}
|
||||
+
|
||||
bool LoadGtk() {
|
||||
static bool loaded = LoadGtkImpl();
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 19f73cc179d82a3729c5fe37883460ac05f4d0c3..cdb67c98291f145f89f76a50b31bf00318648518 100644
|
||||
index 19f73cc179d82a3729c5fe37883460ac05f4d0c3..17aa0b95bd6158ed02c03095c1687185a057fe62 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -41,6 +41,12 @@ using SkColor = uint32_t;
|
||||
@@ -41,6 +41,9 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gtk library in the process.
|
||||
+void* GetLibGtk();
|
||||
+
|
||||
// Loads libgtk and related libraries and returns true on success.
|
||||
bool LoadGtk();
|
||||
|
||||
@@ -45,3 +45,4 @@ fix_revert_src_lb_reducing_c_calls_of_esm_legacy_main_resolve.patch
|
||||
src_preload_function_for_environment.patch
|
||||
deprecate_vector_v8_local_in_v8.patch
|
||||
fix_remove_deprecated_errno_constants.patch
|
||||
build_enable_perfetto.patch
|
||||
|
||||
@@ -11,7 +11,7 @@ really in 20/21. We have to wait until 22 is released to be able to
|
||||
build with upstream GN files.
|
||||
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index 1ed186b597eece7c34cb69c8e1e20870555a040d..7d201bb6b822f0401c3be2bc52d65bc71463748b 100644
|
||||
index 1ed186b597eece7c34cb69c8e1e20870555a040d..2ce1e8a7dcca2ba153d387d11970c72b5f43c167 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -1,14 +1,406 @@
|
||||
@@ -102,7 +102,7 @@ index 1ed186b597eece7c34cb69c8e1e20870555a040d..7d201bb6b822f0401c3be2bc52d65bc7
|
||||
+fs_files = node_files.fs_files
|
||||
+original_fs_files = []
|
||||
+foreach(file, fs_files) {
|
||||
+ original_fs_files += [string_replace(string_replace(file, "internal/fs/", "internal/original-fs/"), "lib/fs.js", "lib/original-fs.js")]
|
||||
+ original_fs_files += [string_replace(string_replace(string_replace(file, "internal/fs/", "internal/original-fs/"), "lib/fs.js", "lib/original-fs.js"), "lib/fs/", "lib/original-fs/")]
|
||||
+}
|
||||
+
|
||||
+copy("node_js2c_inputs") {
|
||||
@@ -1258,7 +1258,7 @@ index 0000000000000000000000000000000000000000..af9cbada10203b387fb9732b346583b1
|
||||
+}
|
||||
diff --git a/filenames.json b/filenames.json
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..12f844c771c19c8d27dcfb9210dcf7bc77b2e994
|
||||
index 0000000000000000000000000000000000000000..424b6f4f9d89c43a2c0404d1a5cb03828f29d906
|
||||
--- /dev/null
|
||||
+++ b/filenames.json
|
||||
@@ -0,0 +1,739 @@
|
||||
@@ -1277,7 +1277,8 @@ index 0000000000000000000000000000000000000000..12f844c771c19c8d27dcfb9210dcf7bc
|
||||
+ "lib/internal/fs/sync_write_stream.js",
|
||||
+ "lib/internal/fs/utils.js",
|
||||
+ "lib/internal/fs/watchers.js",
|
||||
+ "lib/fs.js"
|
||||
+ "lib/fs.js",
|
||||
+ "lib/fs/promises.js"
|
||||
+ ],
|
||||
+ "headers": [
|
||||
+ {
|
||||
@@ -1465,7 +1466,6 @@ index 0000000000000000000000000000000000000000..12f844c771c19c8d27dcfb9210dcf7bc
|
||||
+ "lib/dns/promises.js",
|
||||
+ "lib/domain.js",
|
||||
+ "lib/events.js",
|
||||
+ "lib/fs/promises.js",
|
||||
+ "lib/http.js",
|
||||
+ "lib/http2.js",
|
||||
+ "lib/https.js",
|
||||
@@ -2341,7 +2341,7 @@ index 0000000000000000000000000000000000000000..7848ddb1841b6d4f36e9376c73564eb4
|
||||
+ f.write('\n')
|
||||
diff --git a/tools/generate_original_fs.py b/tools/generate_original_fs.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..a6cdf33f2c0bcca4d7b4eacaa407f1ac5bdcb5cb
|
||||
index 0000000000000000000000000000000000000000..9be3ac447f9a4dde23fefc26e0b922b4ba88f083
|
||||
--- /dev/null
|
||||
+++ b/tools/generate_original_fs.py
|
||||
@@ -0,0 +1,19 @@
|
||||
@@ -2355,7 +2355,7 @@ index 0000000000000000000000000000000000000000..a6cdf33f2c0bcca4d7b4eacaa407f1ac
|
||||
+for fs_file in fs_files:
|
||||
+ with open(os.path.join(node_root_dir, fs_file), 'r') as f:
|
||||
+ contents = f.read()
|
||||
+ original_fs_file = fs_file.replace('internal/fs/', 'internal/original-fs/').replace('lib/fs.js', 'lib/original-fs.js')
|
||||
+ original_fs_file = fs_file.replace('internal/fs/', 'internal/original-fs/').replace('lib/fs.js', 'lib/original-fs.js').replace('lib/fs/', 'lib/original-fs/')
|
||||
+
|
||||
+ with open(os.path.join(out_dir, fs_file), 'w') as original_f:
|
||||
+ original_f.write(contents)
|
||||
|
||||
370
patches/node/build_enable_perfetto.patch
Normal file
370
patches/node/build_enable_perfetto.patch
Normal file
@@ -0,0 +1,370 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Wed, 17 Apr 2024 08:17:49 -0400
|
||||
Subject: build: enable perfetto
|
||||
|
||||
Enable perfetto by default in Node.js. Node.js disables perfetto by
|
||||
default but is broken on build - they don't currently add guards for
|
||||
`V8_USE_PERFETTO` and upstream only defines certain functions
|
||||
on `v8::TracingController` if perfetto is disabled. Electron already
|
||||
had minimal to no support for Node.js trace events, so the impact of
|
||||
adding associated guards there should be relatively small.
|
||||
|
||||
We should upstream this as it will eventually impact Node.js as well.
|
||||
|
||||
diff --git a/lib/internal/constants.js b/lib/internal/constants.js
|
||||
index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..a154559a56bf383d3c26af523c9bb07b564ef600 100644
|
||||
--- a/lib/internal/constants.js
|
||||
+++ b/lib/internal/constants.js
|
||||
@@ -5,12 +5,15 @@ const isWindows = process.platform === 'win32';
|
||||
module.exports = {
|
||||
// Alphabet chars.
|
||||
CHAR_UPPERCASE_A: 65, /* A */
|
||||
+ CHAR_UPPERCASE_B: 66, /* B */
|
||||
CHAR_LOWERCASE_A: 97, /* a */
|
||||
CHAR_UPPERCASE_Z: 90, /* Z */
|
||||
CHAR_LOWERCASE_Z: 122, /* z */
|
||||
CHAR_UPPERCASE_C: 67, /* C */
|
||||
CHAR_LOWERCASE_B: 98, /* b */
|
||||
+ CHAR_UPPERCASE_E: 69, /* E */
|
||||
CHAR_LOWERCASE_E: 101, /* e */
|
||||
+
|
||||
CHAR_LOWERCASE_N: 110, /* n */
|
||||
|
||||
// Non-alphabetic chars.
|
||||
diff --git a/lib/internal/http.js b/lib/internal/http.js
|
||||
index b20b3cd229efcd9701791309361b7d106f315900..6b2820c9dcce5e658b694f53e75d93707c4320d7 100644
|
||||
--- a/lib/internal/http.js
|
||||
+++ b/lib/internal/http.js
|
||||
@@ -10,8 +10,8 @@ const {
|
||||
const { setUnrefTimeout } = require('internal/timers');
|
||||
const { trace, isTraceCategoryEnabled } = internalBinding('trace_events');
|
||||
const {
|
||||
- CHAR_LOWERCASE_B,
|
||||
- CHAR_LOWERCASE_E,
|
||||
+ CHAR_UPPERCASE_B,
|
||||
+ CHAR_UPPERCASE_E,
|
||||
} = require('internal/constants');
|
||||
|
||||
let utcCache;
|
||||
@@ -44,11 +44,13 @@ function isTraceHTTPEnabled() {
|
||||
const traceEventCategory = 'node,node.http';
|
||||
|
||||
function traceBegin(...args) {
|
||||
- trace(CHAR_LOWERCASE_B, traceEventCategory, ...args);
|
||||
+ // See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
|
||||
+ trace(CHAR_UPPERCASE_B, traceEventCategory, ...args);
|
||||
}
|
||||
|
||||
function traceEnd(...args) {
|
||||
- trace(CHAR_LOWERCASE_E, traceEventCategory, ...args);
|
||||
+ // See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
|
||||
+ trace(CHAR_UPPERCASE_E, traceEventCategory, ...args);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
diff --git a/src/tracing/agent.cc b/src/tracing/agent.cc
|
||||
index 7ce59674356f9743438350949be42fa7ead2afbe..c5fedc3be86a77730c57321b9c73cc8e94a001d7 100644
|
||||
--- a/src/tracing/agent.cc
|
||||
+++ b/src/tracing/agent.cc
|
||||
@@ -50,7 +50,9 @@ using v8::platform::tracing::TraceWriter;
|
||||
using std::string;
|
||||
|
||||
Agent::Agent() : tracing_controller_(new TracingController()) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
tracing_controller_->Initialize(nullptr);
|
||||
+#endif
|
||||
|
||||
CHECK_EQ(uv_loop_init(&tracing_loop_), 0);
|
||||
CHECK_EQ(uv_async_init(&tracing_loop_,
|
||||
@@ -86,10 +88,14 @@ Agent::~Agent() {
|
||||
void Agent::Start() {
|
||||
if (started_)
|
||||
return;
|
||||
-
|
||||
+#ifdef V8_USE_PERFETTO
|
||||
+ std::ostringstream perfetto_output;
|
||||
+ tracing_controller_->InitializeForPerfetto(&perfetto_output);
|
||||
+#else
|
||||
NodeTraceBuffer* trace_buffer_ = new NodeTraceBuffer(
|
||||
NodeTraceBuffer::kBufferChunks, this, &tracing_loop_);
|
||||
tracing_controller_->Initialize(trace_buffer_);
|
||||
+#endif
|
||||
|
||||
// This thread should be created *after* async handles are created
|
||||
// (within NodeTraceWriter and NodeTraceBuffer constructors).
|
||||
@@ -143,8 +149,10 @@ void Agent::StopTracing() {
|
||||
return;
|
||||
// Perform final Flush on TraceBuffer. We don't want the tracing controller
|
||||
// to flush the buffer again on destruction of the V8::Platform.
|
||||
- tracing_controller_->StopTracing();
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
tracing_controller_->Initialize(nullptr);
|
||||
+#endif
|
||||
+ tracing_controller_->StopTracing();
|
||||
started_ = false;
|
||||
|
||||
// Thread should finish when the tracing loop is stopped.
|
||||
@@ -202,6 +210,7 @@ std::string Agent::GetEnabledCategories() const {
|
||||
return categories;
|
||||
}
|
||||
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
void Agent::AppendTraceEvent(TraceObject* trace_event) {
|
||||
for (const auto& id_writer : writers_)
|
||||
id_writer.second->AppendTraceEvent(trace_event);
|
||||
@@ -211,18 +220,21 @@ void Agent::AddMetadataEvent(std::unique_ptr<TraceObject> event) {
|
||||
Mutex::ScopedLock lock(metadata_events_mutex_);
|
||||
metadata_events_.push_back(std::move(event));
|
||||
}
|
||||
+#endif
|
||||
|
||||
void Agent::Flush(bool blocking) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
{
|
||||
Mutex::ScopedLock lock(metadata_events_mutex_);
|
||||
for (const auto& event : metadata_events_)
|
||||
AppendTraceEvent(event.get());
|
||||
}
|
||||
-
|
||||
+#endif
|
||||
for (const auto& id_writer : writers_)
|
||||
id_writer.second->Flush(blocking);
|
||||
}
|
||||
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
void TracingController::AddMetadataEvent(
|
||||
const unsigned char* category_group_enabled,
|
||||
const char* name,
|
||||
@@ -246,6 +258,6 @@ void TracingController::AddMetadataEvent(
|
||||
if (node_agent != nullptr)
|
||||
node_agent->AddMetadataEvent(std::move(trace_event));
|
||||
}
|
||||
-
|
||||
+#endif
|
||||
} // namespace tracing
|
||||
} // namespace node
|
||||
diff --git a/src/tracing/agent.h b/src/tracing/agent.h
|
||||
index b542a849fe8da7e8bbbcca7067b73dc32b18d6d3..059ce6f6ea17199ead09c6c13bcc680f18f8c4d0 100644
|
||||
--- a/src/tracing/agent.h
|
||||
+++ b/src/tracing/agent.h
|
||||
@@ -27,7 +27,9 @@ class Agent;
|
||||
class AsyncTraceWriter {
|
||||
public:
|
||||
virtual ~AsyncTraceWriter() = default;
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
virtual void AppendTraceEvent(TraceObject* trace_event) = 0;
|
||||
+#endif
|
||||
virtual void Flush(bool blocking) = 0;
|
||||
virtual void InitializeOnThread(uv_loop_t* loop) {}
|
||||
};
|
||||
@@ -36,6 +38,7 @@ class TracingController : public v8::platform::tracing::TracingController {
|
||||
public:
|
||||
TracingController() : v8::platform::tracing::TracingController() {}
|
||||
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
int64_t CurrentTimestampMicroseconds() override {
|
||||
return uv_hrtime() / 1000;
|
||||
}
|
||||
@@ -48,6 +51,7 @@ class TracingController : public v8::platform::tracing::TracingController {
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* convertable_values,
|
||||
unsigned int flags);
|
||||
+#endif
|
||||
};
|
||||
|
||||
class AgentWriterHandle {
|
||||
@@ -108,11 +112,12 @@ class Agent {
|
||||
|
||||
// Returns a comma-separated list of enabled categories.
|
||||
std::string GetEnabledCategories() const;
|
||||
-
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
// Writes to all writers registered through AddClient().
|
||||
void AppendTraceEvent(TraceObject* trace_event);
|
||||
|
||||
void AddMetadataEvent(std::unique_ptr<TraceObject> event);
|
||||
+#endif
|
||||
// Flushes all writers registered through AddClient().
|
||||
void Flush(bool blocking);
|
||||
|
||||
@@ -152,7 +157,9 @@ class Agent {
|
||||
std::set<AsyncTraceWriter*> to_be_initialized_;
|
||||
|
||||
Mutex metadata_events_mutex_;
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::list<std::unique_ptr<TraceObject>> metadata_events_;
|
||||
+#endif
|
||||
};
|
||||
|
||||
void AgentWriterHandle::reset() {
|
||||
diff --git a/src/tracing/node_trace_buffer.cc b/src/tracing/node_trace_buffer.cc
|
||||
index e187a1d78c81972b69cd4e03f7079cdb727956ad..3256c6326a08c6cafd83f1e49e3350193e813b51 100644
|
||||
--- a/src/tracing/node_trace_buffer.cc
|
||||
+++ b/src/tracing/node_trace_buffer.cc
|
||||
@@ -55,6 +55,7 @@ TraceObject* InternalTraceBuffer::GetEventByHandle(uint64_t handle) {
|
||||
}
|
||||
|
||||
void InternalTraceBuffer::Flush(bool blocking) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
{
|
||||
Mutex::ScopedLock scoped_lock(mutex_);
|
||||
if (total_chunks_ > 0) {
|
||||
@@ -75,6 +76,7 @@ void InternalTraceBuffer::Flush(bool blocking) {
|
||||
flushing_ = false;
|
||||
}
|
||||
}
|
||||
+#endif
|
||||
agent_->Flush(blocking);
|
||||
}
|
||||
|
||||
diff --git a/src/tracing/node_trace_writer.cc b/src/tracing/node_trace_writer.cc
|
||||
index 8f053efe93324b9acbb4e85f7b974b4f7712e200..e331ed5567caa39ade90ce28cea69f1d10533812 100644
|
||||
--- a/src/tracing/node_trace_writer.cc
|
||||
+++ b/src/tracing/node_trace_writer.cc
|
||||
@@ -95,7 +95,7 @@ void NodeTraceWriter::OpenNewFileForStreaming() {
|
||||
fd_ = -1;
|
||||
}
|
||||
}
|
||||
-
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
void NodeTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
|
||||
Mutex::ScopedLock scoped_lock(stream_mutex_);
|
||||
// If this is the first trace event, open a new file for streaming.
|
||||
@@ -112,7 +112,7 @@ void NodeTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
|
||||
++total_traces_;
|
||||
json_trace_writer_->AppendTraceEvent(trace_event);
|
||||
}
|
||||
-
|
||||
+#endif
|
||||
void NodeTraceWriter::FlushPrivate() {
|
||||
std::string str;
|
||||
int highest_request_id;
|
||||
diff --git a/src/tracing/node_trace_writer.h b/src/tracing/node_trace_writer.h
|
||||
index cd965d77b7859ff2edcf781a934594b5a9b6d251..fe1714ba77fddef693d37eeb8c7a196ddfd15c26 100644
|
||||
--- a/src/tracing/node_trace_writer.h
|
||||
+++ b/src/tracing/node_trace_writer.h
|
||||
@@ -20,7 +20,9 @@ class NodeTraceWriter : public AsyncTraceWriter {
|
||||
~NodeTraceWriter() override;
|
||||
|
||||
void InitializeOnThread(uv_loop_t* loop) override;
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
void AppendTraceEvent(TraceObject* trace_event) override;
|
||||
+#endif
|
||||
void Flush(bool blocking) override;
|
||||
|
||||
static const int kTracesPerFile = 1 << 19;
|
||||
diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h
|
||||
index be0f55a409a71bf9c1763c36fdc252857228742e..827b5330b2f8c545338a46c548f8abf4aab7f50c 100644
|
||||
--- a/src/tracing/trace_event.h
|
||||
+++ b/src/tracing/trace_event.h
|
||||
@@ -69,8 +69,16 @@ enum CategoryGroupEnabledFlags {
|
||||
// for best performance when tracing is disabled.
|
||||
// const uint8_t*
|
||||
// TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group)
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \
|
||||
node::tracing::TraceEventHelper::GetCategoryGroupEnabled
|
||||
+#else
|
||||
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group) \
|
||||
+ ([](const char*) -> const uint8_t* { \
|
||||
+ static uint8_t no = 0; \
|
||||
+ return &no; \
|
||||
+ })(category_group)
|
||||
+#endif
|
||||
|
||||
// Get the number of times traces have been recorded. This is used to implement
|
||||
// the TRACE_EVENT_IS_NEW_TRACE facility.
|
||||
@@ -114,10 +122,15 @@ enum CategoryGroupEnabledFlags {
|
||||
// const uint8_t* category_group_enabled,
|
||||
// const char* name,
|
||||
// uint64_t id)
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \
|
||||
if (auto controller = \
|
||||
node::tracing::TraceEventHelper::GetTracingController()) \
|
||||
controller->UpdateTraceEventDuration
|
||||
+#else
|
||||
+#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled, name, event_handle) \
|
||||
+ (void)(category_group_enabled), (void)(name), (void)(event_handle)
|
||||
+#endif
|
||||
|
||||
// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method
|
||||
// on the convertable value will be called at flush time.
|
||||
@@ -319,10 +332,13 @@ class TraceEventHelper {
|
||||
static void SetAgent(Agent* agent);
|
||||
|
||||
static inline const uint8_t* GetCategoryGroupEnabled(const char* group) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
v8::TracingController* controller = GetTracingController();
|
||||
static const uint8_t disabled = 0;
|
||||
if (UNLIKELY(controller == nullptr)) return &disabled;
|
||||
return controller->GetCategoryGroupEnabled(group);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -460,6 +476,7 @@ static inline uint64_t AddTraceEventImpl(
|
||||
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -469,13 +486,14 @@ static inline uint64_t AddTraceEventImpl(
|
||||
arg_convertibles[1].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
static_cast<intptr_t>(arg_values[1])));
|
||||
}
|
||||
- // DCHECK(num_args, 2);
|
||||
v8::TracingController* controller =
|
||||
node::tracing::TraceEventHelper::GetTracingController();
|
||||
if (controller == nullptr) return 0;
|
||||
return controller->AddTraceEvent(phase, category_group_enabled, name, scope, id,
|
||||
bind_id, num_args, arg_names, arg_types,
|
||||
arg_values, arg_convertibles, flags);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
@@ -483,6 +501,7 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags, int64_t timestamp) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertables[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertables[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -492,19 +511,21 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
arg_convertables[1].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
static_cast<intptr_t>(arg_values[1])));
|
||||
}
|
||||
- // DCHECK_LE(num_args, 2);
|
||||
v8::TracingController* controller =
|
||||
node::tracing::TraceEventHelper::GetTracingController();
|
||||
if (controller == nullptr) return 0;
|
||||
return controller->AddTraceEventWithTimestamp(
|
||||
phase, category_group_enabled, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, arg_convertables, flags, timestamp);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static V8_INLINE void AddMetadataEventImpl(
|
||||
const uint8_t* category_group_enabled, const char* name, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags) {
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -520,6 +541,7 @@ static V8_INLINE void AddMetadataEventImpl(
|
||||
return agent->GetTracingController()->AddMetadataEvent(
|
||||
category_group_enabled, name, num_args, arg_names, arg_types, arg_values,
|
||||
arg_convertibles, flags);
|
||||
+#endif
|
||||
}
|
||||
|
||||
// Define SetTraceValue for each allowed type. It stores the type and
|
||||
@@ -18,7 +18,7 @@ See https://gist.github.com/codebytere/275ec8923253fd6559b3d36115f7b31b for more
|
||||
This should be fixed and re-enabled on all platforms.
|
||||
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index 7d201bb6b822f0401c3be2bc52d65bc71463748b..38f4b5afb9e667f0958008847224fa7a6cfe64a0 100644
|
||||
index 2ce1e8a7dcca2ba153d387d11970c72b5f43c167..590303d1b78e2c28d7c3338196b12fcf14f65b28 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -181,6 +181,10 @@ config("node_internal_config") {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
deps_add_v8_object_setinternalfieldfornodecore.patch
|
||||
cherry-pick-f320600cd1f4.patch
|
||||
|
||||
27
patches/v8/cherry-pick-f320600cd1f4.patch
Normal file
27
patches/v8/cherry-pick-f320600cd1f4.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Thu, 9 May 2024 12:03:28 -0700
|
||||
Subject: Only normalize JSObject targets in SetOrCopyDataProperties
|
||||
|
||||
Bug: 339458194
|
||||
Change-Id: I4d6eebdd921971fa28d7c474535d978900ba633f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5527397
|
||||
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
|
||||
Commit-Queue: Shu-yu Guo <syg@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#93811}
|
||||
|
||||
diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.cc
|
||||
index 6c99451fab8bf7d5b08735dd9a4863b5978596d3..fa13ac023ebd7ea7cbb52b9e6924af1b21146676 100644
|
||||
--- a/src/objects/js-objects.cc
|
||||
+++ b/src/objects/js-objects.cc
|
||||
@@ -434,9 +434,7 @@ Maybe<bool> JSReceiver::SetOrCopyDataProperties(
|
||||
Nothing<bool>());
|
||||
|
||||
if (!from->HasFastProperties() && target->HasFastProperties() &&
|
||||
- !IsJSGlobalProxy(*target)) {
|
||||
- // JSProxy is always in slow-mode.
|
||||
- DCHECK(!IsJSProxy(*target));
|
||||
+ IsJSObject(*target) && !IsJSGlobalProxy(*target)) {
|
||||
// Convert to slow properties if we're guaranteed to overflow the number of
|
||||
// descriptors.
|
||||
int source_length;
|
||||
@@ -75,7 +75,8 @@ function spawnAndCheckExitCode (cmd, args, opts) {
|
||||
|
||||
function cpplint (args) {
|
||||
args.unshift(`--root=${SOURCE_ROOT}`);
|
||||
const result = childProcess.spawnSync(IS_WINDOWS ? 'cpplint.bat' : 'cpplint.py', args, { encoding: 'utf8', shell: true });
|
||||
const cmd = IS_WINDOWS ? 'cpplint.bat' : 'cpplint.py';
|
||||
const result = childProcess.spawnSync(cmd, args, { encoding: 'utf8', shell: true });
|
||||
// cpplint.py writes EVERYTHING to stderr, including status messages
|
||||
if (result.stderr) {
|
||||
for (const line of result.stderr.split(/[\r\n]+/)) {
|
||||
|
||||
@@ -93,7 +93,8 @@ async function main () {
|
||||
const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--verbose', '--directory', 'test', '-j', 'max'], {
|
||||
env,
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
if (buildStatus !== 0) {
|
||||
@@ -104,7 +105,8 @@ async function main () {
|
||||
const { status: installStatus } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], {
|
||||
env,
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
if (installStatus !== 0) {
|
||||
console.error('Failed to install nan node_modules');
|
||||
|
||||
@@ -10,7 +10,8 @@ if (fs.existsSync(checkPath)) {
|
||||
command.slice(1),
|
||||
{
|
||||
stdio: 'inherit',
|
||||
cwd: checkPath
|
||||
cwd: checkPath,
|
||||
shell: process.platform === 'win32'
|
||||
}
|
||||
);
|
||||
child.on('exit', code => process.exit(code));
|
||||
|
||||
@@ -221,7 +221,8 @@ async function installSpecModules (dir) {
|
||||
const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], {
|
||||
env,
|
||||
cwd: dir,
|
||||
stdio: 'inherit'
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
if (status !== 0 && !process.env.IGNORE_YARN_INSTALL_ERROR) {
|
||||
console.log(`${fail} Failed to yarn install in '${dir}'`);
|
||||
|
||||
@@ -11,7 +11,8 @@ if (require.main === module) {
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_yes: 'true'
|
||||
}
|
||||
},
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
child.on('exit', code => process.exit(code));
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "components/pdf/common/constants.h"
|
||||
#include "pdf/pdf.h" // nogncheck
|
||||
#include "components/pdf/common/constants.h" // nogncheck
|
||||
#include "pdf/pdf.h" // nogncheck
|
||||
#include "shell/common/electron_constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
|
||||
@@ -181,6 +181,26 @@ View::~View() {
|
||||
delete view_;
|
||||
}
|
||||
|
||||
void View::ReorderChildView(gin::Handle<View> child, size_t index) {
|
||||
view_->ReorderChildView(child->view(), index);
|
||||
|
||||
const auto i = base::ranges::find(child_views_, child.ToV8());
|
||||
DCHECK(i != child_views_.end());
|
||||
|
||||
// If |view| is already at the desired position, there's nothing to do.
|
||||
const auto pos = std::next(
|
||||
child_views_.begin(),
|
||||
static_cast<ptrdiff_t>(std::min(index, child_views_.size() - 1)));
|
||||
if (i == pos)
|
||||
return;
|
||||
|
||||
if (pos < i) {
|
||||
std::rotate(pos, i, std::next(i));
|
||||
} else {
|
||||
std::rotate(i, std::next(i), std::next(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void View::AddChildViewAt(gin::Handle<View> child,
|
||||
std::optional<size_t> maybe_index) {
|
||||
// TODO(nornagon): !view_ is only for supporting the weird case of
|
||||
@@ -189,8 +209,25 @@ void View::AddChildViewAt(gin::Handle<View> child,
|
||||
// has a View, possibly a wrapper view around the underlying platform View.
|
||||
if (!view_)
|
||||
return;
|
||||
|
||||
// This will CHECK and crash in View::AddChildViewAtImpl if not handled here.
|
||||
if (view_ == child->view()) {
|
||||
gin_helper::ErrorThrower(isolate()).ThrowError(
|
||||
"A view cannot be added as its own child");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index =
|
||||
std::min(child_views_.size(), maybe_index.value_or(child_views_.size()));
|
||||
|
||||
// If the child is already a child of this view, just reorder it.
|
||||
// This matches the behavior of View::AddChildViewAtImpl and
|
||||
// otherwise will CHECK if the same view is added multiple times.
|
||||
if (child->view()->parent() == view_) {
|
||||
ReorderChildView(child, index);
|
||||
return;
|
||||
}
|
||||
|
||||
child_views_.emplace(child_views_.begin() + index, // index
|
||||
isolate(), child->GetWrapper()); // v8::Global(args...)
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
@@ -213,7 +250,7 @@ void View::RemoveChildView(gin::Handle<View> child) {
|
||||
return;
|
||||
if (!child->view())
|
||||
return;
|
||||
auto it = std::find(child_views_.begin(), child_views_.end(), child.ToV8());
|
||||
const auto it = base::ranges::find(child_views_, child.ToV8());
|
||||
if (it != child_views_.end()) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
ScopedCAActionDisabler disable_animations;
|
||||
|
||||
@@ -57,6 +57,8 @@ class View : public gin_helper::EventEmitter<View>, public views::ViewObserver {
|
||||
void set_delete_view(bool should) { delete_view_ = should; }
|
||||
|
||||
private:
|
||||
void ReorderChildView(gin::Handle<View> child, size_t index);
|
||||
|
||||
std::vector<v8::Global<v8::Object>> child_views_;
|
||||
|
||||
bool delete_view_ = true;
|
||||
|
||||
@@ -89,6 +89,9 @@ void WebContentsView::OnViewAddedToWidget(views::View* observed_view) {
|
||||
widget->GetNativeWindowProperty(electron::kElectronNativeWindowKey));
|
||||
if (!native_window)
|
||||
return;
|
||||
// We don't need to call SetOwnerWindow(nullptr) in OnViewRemovedFromWidget
|
||||
// because that's handled in the WebContents dtor called prior.
|
||||
api_web_contents_->SetOwnerWindow(native_window);
|
||||
native_window->AddDraggableRegionProvider(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
#include "components/pdf/browser/pdf_document_helper.h" // nogncheck
|
||||
#include "components/pdf/browser/pdf_navigation_throttle.h"
|
||||
#include "components/pdf/browser/pdf_url_loader_request_interceptor.h"
|
||||
#include "components/pdf/common/constants.h"
|
||||
#include "components/pdf/common/constants.h" // nogncheck
|
||||
#include "shell/browser/electron_pdf_document_helper_client.h"
|
||||
#endif
|
||||
|
||||
@@ -456,7 +456,15 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
base::CommandLine* command_line,
|
||||
int process_id) {
|
||||
// Make sure we're about to launch a known executable
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// On Linux, do not perform this check for /proc/self/exe. It will always
|
||||
// point to the currently running executable so this check is not
|
||||
// necessary, and if the executable has been deleted it will return a fake
|
||||
// name that causes this check to fail.
|
||||
if (command_line->GetProgram() != base::FilePath(base::kProcSelfExe)) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
ScopedAllowBlockingForElectron allow_blocking;
|
||||
base::FilePath child_path;
|
||||
base::FilePath program =
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "shell/browser/protocol_registry.h"
|
||||
#include "shell/browser/special_storage_policy.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
||||
#include "shell/browser/ui/webui/accessibility_ui.h"
|
||||
#include "shell/browser/web_contents_permission_helper.h"
|
||||
#include "shell/browser/web_view_manager.h"
|
||||
#include "shell/browser/zoom_level_delegate.h"
|
||||
@@ -352,6 +353,7 @@ void ElectronBrowserContext::InitPrefs() {
|
||||
MediaDeviceIDSalt::RegisterPrefs(registry.get());
|
||||
ZoomLevelDelegate::RegisterPrefs(registry.get());
|
||||
PrefProxyConfigTrackerImpl::RegisterPrefs(registry.get());
|
||||
ElectronAccessibilityUIMessageHandler::RegisterPrefs(registry.get());
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
if (!in_memory_)
|
||||
extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get());
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
#include "content/public/browser/media_stream_request.h"
|
||||
#include "content/public/browser/resource_context.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/shell/browser/media/media_device_id_salt.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom.h"
|
||||
#include "shell/browser/media/media_device_id_salt.h"
|
||||
#include "third_party/blink/public/common/permissions/permission_utils.h"
|
||||
|
||||
class PrefService;
|
||||
|
||||
@@ -398,12 +398,6 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
||||
CHECK(linux_ui);
|
||||
linux_ui_getter_ = std::make_unique<LinuxUiGetterImpl>();
|
||||
|
||||
// Try loading gtk symbols used by Electron.
|
||||
electron::InitializeElectron_gtk(gtk::GetLibGtk());
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
electron::UninitializeElectron_gtk();
|
||||
}
|
||||
|
||||
electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
||||
@@ -131,153 +131,6 @@ JavascriptEnvironment::~JavascriptEnvironment() {
|
||||
platform_->UnregisterIsolate(isolate_);
|
||||
}
|
||||
|
||||
class EnabledStateObserverImpl final
|
||||
: public base::trace_event::TraceLog::EnabledStateObserver {
|
||||
public:
|
||||
EnabledStateObserverImpl() {
|
||||
base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
|
||||
}
|
||||
|
||||
~EnabledStateObserverImpl() override {
|
||||
base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(
|
||||
this);
|
||||
}
|
||||
|
||||
// disable copy
|
||||
EnabledStateObserverImpl(const EnabledStateObserverImpl&) = delete;
|
||||
EnabledStateObserverImpl& operator=(const EnabledStateObserverImpl&) = delete;
|
||||
|
||||
void OnTraceLogEnabled() final {
|
||||
base::AutoLock lock(mutex_);
|
||||
for (auto* o : observers_) {
|
||||
o->OnTraceEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
void OnTraceLogDisabled() final {
|
||||
base::AutoLock lock(mutex_);
|
||||
for (auto* o : observers_) {
|
||||
o->OnTraceDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
void AddObserver(v8::TracingController::TraceStateObserver* observer) {
|
||||
{
|
||||
base::AutoLock lock(mutex_);
|
||||
DCHECK(!observers_.count(observer));
|
||||
observers_.insert(observer);
|
||||
}
|
||||
|
||||
// Fire the observer if recording is already in progress.
|
||||
if (base::trace_event::TraceLog::GetInstance()->IsEnabled())
|
||||
observer->OnTraceEnabled();
|
||||
}
|
||||
|
||||
void RemoveObserver(v8::TracingController::TraceStateObserver* observer) {
|
||||
base::AutoLock lock(mutex_);
|
||||
DCHECK_EQ(observers_.count(observer), 1lu);
|
||||
observers_.erase(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
base::Lock mutex_;
|
||||
std::unordered_set<v8::TracingController::TraceStateObserver*> observers_;
|
||||
};
|
||||
|
||||
class TracingControllerImpl : public node::tracing::TracingController {
|
||||
public:
|
||||
TracingControllerImpl() = default;
|
||||
~TracingControllerImpl() override = default;
|
||||
|
||||
// disable copy
|
||||
TracingControllerImpl(const TracingControllerImpl&) = delete;
|
||||
TracingControllerImpl& operator=(const TracingControllerImpl&) = delete;
|
||||
|
||||
// TracingController implementation.
|
||||
const uint8_t* GetCategoryGroupEnabled(const char* name) override {
|
||||
return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(name);
|
||||
}
|
||||
uint64_t AddTraceEvent(
|
||||
char phase,
|
||||
const uint8_t* category_enabled_flag,
|
||||
const char* name,
|
||||
const char* scope,
|
||||
uint64_t id,
|
||||
uint64_t bind_id,
|
||||
int32_t num_args,
|
||||
const char** arg_names,
|
||||
const uint8_t* arg_types,
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
|
||||
unsigned int flags) override {
|
||||
base::trace_event::TraceArguments args(
|
||||
num_args, arg_names, arg_types,
|
||||
reinterpret_cast<const unsigned long long*>( // NOLINT(runtime/int)
|
||||
arg_values),
|
||||
arg_convertables);
|
||||
DCHECK_LE(num_args, 2);
|
||||
base::trace_event::TraceEventHandle handle =
|
||||
TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_BIND_ID(
|
||||
phase, category_enabled_flag, name, scope, id, bind_id, &args,
|
||||
flags);
|
||||
uint64_t result;
|
||||
memcpy(&result, &handle, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
uint64_t AddTraceEventWithTimestamp(
|
||||
char phase,
|
||||
const uint8_t* category_enabled_flag,
|
||||
const char* name,
|
||||
const char* scope,
|
||||
uint64_t id,
|
||||
uint64_t bind_id,
|
||||
int32_t num_args,
|
||||
const char** arg_names,
|
||||
const uint8_t* arg_types,
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
|
||||
unsigned int flags,
|
||||
int64_t timestampMicroseconds) override {
|
||||
base::trace_event::TraceArguments args(
|
||||
num_args, arg_names, arg_types,
|
||||
reinterpret_cast<const unsigned long long*>( // NOLINT(runtime/int)
|
||||
arg_values),
|
||||
arg_convertables);
|
||||
DCHECK_LE(num_args, 2);
|
||||
base::TimeTicks timestamp =
|
||||
base::TimeTicks() + base::Microseconds(timestampMicroseconds);
|
||||
base::trace_event::TraceEventHandle handle =
|
||||
TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
|
||||
phase, category_enabled_flag, name, scope, id, bind_id,
|
||||
TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, &args, flags);
|
||||
uint64_t result;
|
||||
memcpy(&result, &handle, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
void UpdateTraceEventDuration(const uint8_t* category_enabled_flag,
|
||||
const char* name,
|
||||
uint64_t handle) override {
|
||||
base::trace_event::TraceEventHandle traceEventHandle;
|
||||
memcpy(&traceEventHandle, &handle, sizeof(handle));
|
||||
TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_enabled_flag, name,
|
||||
traceEventHandle);
|
||||
}
|
||||
|
||||
void AddTraceStateObserver(TraceStateObserver* observer) override {
|
||||
GetObserverDelegate().AddObserver(observer);
|
||||
}
|
||||
|
||||
void RemoveTraceStateObserver(TraceStateObserver* observer) override {
|
||||
GetObserverDelegate().RemoveObserver(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
static EnabledStateObserverImpl& GetObserverDelegate() {
|
||||
static base::NoDestructor<EnabledStateObserverImpl> instance;
|
||||
return *instance;
|
||||
}
|
||||
};
|
||||
|
||||
v8::Isolate* JavascriptEnvironment::Initialize(uv_loop_t* event_loop,
|
||||
bool setup_wasm_streaming) {
|
||||
auto* cmd = base::CommandLine::ForCurrentProcess();
|
||||
@@ -292,7 +145,7 @@ v8::Isolate* JavascriptEnvironment::Initialize(uv_loop_t* event_loop,
|
||||
// The V8Platform of gin relies on Chromium's task schedule, which has not
|
||||
// been started at this point, so we have to rely on Node's V8Platform.
|
||||
auto* tracing_agent = node::CreateAgent();
|
||||
auto* tracing_controller = new TracingControllerImpl();
|
||||
auto* tracing_controller = tracing_agent->GetTracingController();
|
||||
node::tracing::TraceEventHelper::SetAgent(tracing_agent);
|
||||
platform_ = node::MultiIsolatePlatform::Create(
|
||||
base::RecommendedMaxNumberOfThreadsInThreadGroup(3, 8, 0.1, 0),
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
@@ -80,9 +81,9 @@
|
||||
#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h"
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
#include "ui/gfx/win/msg_util.h"
|
||||
#endif
|
||||
|
||||
@@ -1069,8 +1070,23 @@ ui::ZOrderLevel NativeWindowViews::GetZOrderLevel() const {
|
||||
return widget()->GetZOrderLevel();
|
||||
}
|
||||
|
||||
// We previous called widget()->CenterWindow() here, but in
|
||||
// Chromium CL 4916277 behavior was changed to center relative to the
|
||||
// parent window if there is one. We want to keep the old behavior
|
||||
// for now to avoid breaking API contract, but should consider the long
|
||||
// term plan for this aligning with upstream.
|
||||
void NativeWindowViews::Center() {
|
||||
widget()->CenterWindow(GetSize());
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
auto display =
|
||||
display::Screen::GetScreen()->GetDisplayNearestWindow(GetNativeWindow());
|
||||
gfx::Rect window_bounds_in_screen = display.work_area();
|
||||
window_bounds_in_screen.ClampToCenteredSize(GetSize());
|
||||
widget()->SetBounds(window_bounds_in_screen);
|
||||
#else
|
||||
HWND hwnd = GetAcceleratedWidget();
|
||||
gfx::Size size = display::win::ScreenWin::DIPToScreenSize(hwnd, GetSize());
|
||||
gfx::CenterAndSizeWindow(hwnd, hwnd, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::Invalidate() {
|
||||
|
||||
@@ -230,6 +230,7 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
|
||||
|
||||
ResizeRootLayer(false);
|
||||
render_widget_host_->SetView(this);
|
||||
render_widget_host_->render_frame_metadata_provider()->AddObserver(this);
|
||||
|
||||
if (content::GpuDataManager::GetInstance()->HardwareAccelerationEnabled()) {
|
||||
video_consumer_ = std::make_unique<OffScreenVideoConsumer>(
|
||||
@@ -240,7 +241,21 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
|
||||
}
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnLocalSurfaceIdChanged(
|
||||
const cc::RenderFrameMetadata& metadata) {
|
||||
if (metadata.local_surface_id) {
|
||||
bool changed = delegated_frame_host_allocator_.UpdateFromChild(
|
||||
*metadata.local_surface_id);
|
||||
|
||||
if (changed) {
|
||||
ResizeRootLayer(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() {
|
||||
render_widget_host_->render_frame_metadata_provider()->RemoveObserver(this);
|
||||
|
||||
// Marking the DelegatedFrameHost as removed from the window hierarchy is
|
||||
// necessary to remove all connections to its old ui::Compositor.
|
||||
if (is_showing_)
|
||||
|
||||
@@ -59,9 +59,11 @@ typedef base::RepeatingCallback<void(const gfx::Rect&, const SkBitmap&)>
|
||||
OnPaintCallback;
|
||||
typedef base::RepeatingCallback<void(const gfx::Rect&)> OnPopupPaintCallback;
|
||||
|
||||
class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
|
||||
public ui::CompositorDelegate,
|
||||
public OffscreenViewProxyObserver {
|
||||
class OffScreenRenderWidgetHostView
|
||||
: public content::RenderWidgetHostViewBase,
|
||||
public content::RenderFrameMetadataProvider::Observer,
|
||||
public ui::CompositorDelegate,
|
||||
public OffscreenViewProxyObserver {
|
||||
public:
|
||||
OffScreenRenderWidgetHostView(bool transparent,
|
||||
bool painting,
|
||||
@@ -175,6 +177,15 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
|
||||
RenderWidgetHostViewBase* target_view,
|
||||
gfx::PointF* transformed_point) override;
|
||||
|
||||
// RenderFrameMetadataProvider::Observer:
|
||||
void OnRenderFrameMetadataChangedBeforeActivation(
|
||||
const cc::RenderFrameMetadata& metadata) override {}
|
||||
void OnRenderFrameMetadataChangedAfterActivation(
|
||||
base::TimeTicks activation_time) override {}
|
||||
void OnRenderFrameSubmission() override {}
|
||||
void OnLocalSurfaceIdChanged(
|
||||
const cc::RenderFrameMetadata& metadata) override;
|
||||
|
||||
// ui::CompositorDelegate:
|
||||
bool IsOffscreen() const override;
|
||||
std::unique_ptr<viz::HostDisplayClient> CreateHostDisplayClient(
|
||||
|
||||
@@ -26,8 +26,6 @@ class ElectronMenuModel;
|
||||
@protected
|
||||
base::WeakPtr<electron::ElectronMenuModel> model_;
|
||||
NSMenu* __strong menu_;
|
||||
NSMenuItem* __strong recentDocumentsMenuItem_;
|
||||
NSMenu* __strong recentDocumentsMenuSwap_;
|
||||
BOOL isMenuOpen_;
|
||||
BOOL useDefaultAccelerator_;
|
||||
base::OnceClosure closeCallback;
|
||||
|
||||
@@ -31,10 +31,14 @@ using SharingItem = electron::ElectronMenuModel::SharingItem;
|
||||
|
||||
namespace {
|
||||
|
||||
static NSMenuItem* __strong recentDocumentsMenuItem_;
|
||||
static NSMenu* __strong recentDocumentsMenuSwap_;
|
||||
|
||||
struct Role {
|
||||
SEL selector;
|
||||
const char* role;
|
||||
};
|
||||
|
||||
Role kRolesMap[] = {
|
||||
{@selector(orderFrontStandardAboutPanel:), "about"},
|
||||
{@selector(hide:), "hide"},
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "ui/base/glib/scoped_gsignal.h"
|
||||
#include "ui/gtk/gtk_ui.h" // nogncheck
|
||||
#include "ui/gtk/gtk_util.h" // nogncheck
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
static const int kPreviewWidth = 256;
|
||||
static const int kPreviewHeight = 512;
|
||||
|
||||
std::string MakeCaseInsensitivePattern(const std::string& extension) {
|
||||
// If the extension is the "all files" extension, no change needed.
|
||||
if (extension == "*")
|
||||
return extension;
|
||||
|
||||
std::string pattern("*.");
|
||||
for (char ch : extension) {
|
||||
if (!base::IsAsciiAlpha(ch)) {
|
||||
pattern.push_back(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.push_back('[');
|
||||
pattern.push_back(base::ToLowerASCII(ch));
|
||||
pattern.push_back(base::ToUpperASCII(ch));
|
||||
pattern.push_back(']');
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
class FileChooserDialog {
|
||||
public:
|
||||
FileChooserDialog(GtkFileChooserAction action, const DialogSettings& settings)
|
||||
: parent_(
|
||||
static_cast<electron::NativeWindowViews*>(settings.parent_window)),
|
||||
filters_(settings.filters) {
|
||||
auto label = settings.button_label;
|
||||
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_native_new(
|
||||
settings.title.c_str(), nullptr, action,
|
||||
label.empty() ? nullptr : label.c_str(), nullptr));
|
||||
} else {
|
||||
const char* confirm_text = gtk_util::GetOkLabel();
|
||||
if (!label.empty())
|
||||
confirm_text = label.c_str();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
confirm_text = gtk_util::GetSaveLabel();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
confirm_text = gtk_util::GetOpenLabel();
|
||||
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new(
|
||||
settings.title.c_str(), nullptr, action, gtk_util::GetCancelLabel(),
|
||||
GTK_RESPONSE_CANCEL, confirm_text, GTK_RESPONSE_ACCEPT, nullptr));
|
||||
}
|
||||
|
||||
if (parent_) {
|
||||
parent_->SetEnabled(false);
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_set_modal(GTK_NATIVE_DIALOG(dialog_), TRUE);
|
||||
} else {
|
||||
gtk::SetGtkTransientForAura(GTK_WIDGET(dialog_),
|
||||
parent_->GetNativeWindow());
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(dialog_, TRUE);
|
||||
if (action != GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
gtk_file_chooser_set_create_folders(dialog_, TRUE);
|
||||
|
||||
if (!settings.default_path.empty()) {
|
||||
electron::ScopedAllowBlockingForElectron allow_blocking;
|
||||
if (base::DirectoryExists(settings.default_path)) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.value().c_str());
|
||||
} else {
|
||||
if (settings.default_path.IsAbsolute()) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.DirName().value().c_str());
|
||||
}
|
||||
|
||||
gtk_file_chooser_set_current_name(
|
||||
GTK_FILE_CHOOSER(dialog_),
|
||||
settings.default_path.BaseName().value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!settings.filters.empty())
|
||||
AddFilters(settings.filters);
|
||||
|
||||
// GtkFileChooserNative does not support preview widgets through the
|
||||
// org.freedesktop.portal.FileChooser portal. In the case of running through
|
||||
// the org.freedesktop.portal.FileChooser portal, anything having to do with
|
||||
// the update-preview signal or the preview widget will just be ignored.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
preview_ = gtk_image_new();
|
||||
signals_.emplace_back(
|
||||
dialog_, "update-preview",
|
||||
base::BindRepeating(&FileChooserDialog::OnUpdatePreview,
|
||||
base::Unretained(this)));
|
||||
gtk_file_chooser_set_preview_widget(dialog_, preview_);
|
||||
}
|
||||
}
|
||||
|
||||
~FileChooserDialog() {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_destroy(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_destroy(GTK_WIDGET(dialog_));
|
||||
}
|
||||
|
||||
if (parent_)
|
||||
parent_->SetEnabled(true);
|
||||
}
|
||||
|
||||
// disable copy
|
||||
FileChooserDialog(const FileChooserDialog&) = delete;
|
||||
FileChooserDialog& operator=(const FileChooserDialog&) = delete;
|
||||
|
||||
void SetupOpenProperties(int properties) {
|
||||
const auto hasProp = [properties](OpenFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_select_multiple(file_chooser,
|
||||
hasProp(OPEN_DIALOG_MULTI_SELECTIONS));
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(OPEN_DIALOG_SHOW_HIDDEN_FILES));
|
||||
}
|
||||
|
||||
void SetupSaveProperties(int properties) {
|
||||
const auto hasProp = [properties](SaveFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(SAVE_DIALOG_SHOW_HIDDEN_FILES));
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
file_chooser, hasProp(SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION));
|
||||
}
|
||||
|
||||
void RunAsynchronous() {
|
||||
signals_.emplace_back(
|
||||
GTK_WIDGET(dialog_), "response",
|
||||
base::BindRepeating(&FileChooserDialog::OnFileDialogResponse,
|
||||
base::Unretained(this)));
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_show(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog_));
|
||||
gtk::GtkUi::GetPlatform()->ShowGtkWindow(GTK_WINDOW(dialog_));
|
||||
}
|
||||
}
|
||||
|
||||
void RunSaveAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
save_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
void RunOpenAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
open_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
base::FilePath GetFileName() const {
|
||||
gchar* filename = gtk_file_chooser_get_filename(dialog_);
|
||||
const base::FilePath path(filename);
|
||||
g_free(filename);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<base::FilePath> GetFileNames() const {
|
||||
std::vector<base::FilePath> paths;
|
||||
auto* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog_));
|
||||
for (auto* iter = filenames; iter != nullptr; iter = iter->next) {
|
||||
auto* filename = static_cast<char*>(iter->data);
|
||||
paths.emplace_back(filename);
|
||||
g_free(filename);
|
||||
}
|
||||
g_slist_free(filenames);
|
||||
return paths;
|
||||
}
|
||||
|
||||
void OnFileDialogResponse(GtkWidget* widget, int response);
|
||||
|
||||
GtkFileChooser* dialog() const { return dialog_; }
|
||||
|
||||
private:
|
||||
void AddFilters(const Filters& filters);
|
||||
|
||||
raw_ptr<electron::NativeWindowViews> parent_;
|
||||
|
||||
RAW_PTR_EXCLUSION GtkFileChooser* dialog_;
|
||||
RAW_PTR_EXCLUSION GtkWidget* preview_;
|
||||
|
||||
Filters filters_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> save_promise_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> open_promise_;
|
||||
|
||||
// Callback for when we update the preview for the selection.
|
||||
void OnUpdatePreview(GtkFileChooser* chooser);
|
||||
|
||||
std::vector<ScopedGSignal> signals_;
|
||||
};
|
||||
|
||||
void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_hide(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_hide(GTK_WIDGET(dialog_));
|
||||
}
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
if (save_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(save_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePath", GetFileName());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePath", base::FilePath());
|
||||
}
|
||||
save_promise_->Resolve(dict);
|
||||
} else if (open_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(open_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", GetFileNames());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
open_promise_->Resolve(dict);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileChooserDialog::AddFilters(const Filters& filters) {
|
||||
for (const auto& filter : filters) {
|
||||
GtkFileFilter* gtk_filter = gtk_file_filter_new();
|
||||
|
||||
for (const auto& extension : filter.second) {
|
||||
std::string pattern = MakeCaseInsensitivePattern(extension);
|
||||
gtk_file_filter_add_pattern(gtk_filter, pattern.c_str());
|
||||
}
|
||||
|
||||
gtk_file_filter_set_name(gtk_filter, filter.first.c_str());
|
||||
gtk_file_chooser_add_filter(dialog_, gtk_filter);
|
||||
}
|
||||
}
|
||||
|
||||
bool CanPreview(const struct stat& st) {
|
||||
// Only preview regular files; pipes may hang.
|
||||
// See https://crbug.com/534754.
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't preview huge files; they may crash.
|
||||
// https://github.com/electron/electron/issues/31630
|
||||
// Setting an arbitrary filesize max t at 100 MB here.
|
||||
constexpr off_t ArbitraryMax = 100000000ULL;
|
||||
return st.st_size < ArbitraryMax;
|
||||
}
|
||||
|
||||
void FileChooserDialog::OnUpdatePreview(GtkFileChooser* chooser) {
|
||||
CHECK(!electron::IsElectron_gtkInitialized());
|
||||
gchar* filename = gtk_file_chooser_get_preview_filename(chooser);
|
||||
if (!filename) {
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(filename, &sb) != 0 || !CanPreview(sb)) {
|
||||
g_free(filename);
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth,
|
||||
kPreviewHeight, nullptr);
|
||||
g_free(filename);
|
||||
if (pixbuf) {
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, pixbuf ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowFileDialog(const FileChooserDialog& dialog) {
|
||||
// gtk_native_dialog_run() will call gtk_native_dialog_show() for us.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog.dialog()));
|
||||
}
|
||||
}
|
||||
|
||||
int RunFileDialog(const FileChooserDialog& dialog) {
|
||||
int response = 0;
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.dialog()));
|
||||
} else {
|
||||
response = gtk_dialog_run(GTK_DIALOG(dialog.dialog()));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog open_dialog(action, settings);
|
||||
open_dialog.SetupOpenProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(open_dialog);
|
||||
|
||||
const int response = RunFileDialog(open_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*paths = open_dialog.GetFileNames();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog* open_dialog = new FileChooserDialog(action, settings);
|
||||
open_dialog->SetupOpenProperties(settings.properties);
|
||||
open_dialog->RunOpenAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog.SetupSaveProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(save_dialog);
|
||||
|
||||
const int response = RunFileDialog(save_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*path = save_dialog.GetFileName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* save_dialog =
|
||||
new FileChooserDialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog->RunSaveAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
||||
258
shell/browser/ui/file_dialog_linux.cc
Normal file
258
shell/browser/ui/file_dialog_linux.cc
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "ui/gtk/select_file_dialog_linux_gtk.h" // nogncheck
|
||||
#include "ui/shell_dialogs/select_file_dialog.h"
|
||||
#include "ui/shell_dialogs/selected_file_info.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
ui::SelectFileDialog::Type GetDialogType(int properties) {
|
||||
if (properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
return ui::SelectFileDialog::SELECT_FOLDER;
|
||||
|
||||
if (properties & OPEN_DIALOG_MULTI_SELECTIONS)
|
||||
return ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
|
||||
|
||||
return ui::SelectFileDialog::SELECT_OPEN_FILE;
|
||||
}
|
||||
|
||||
ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) {
|
||||
ui::SelectFileDialog::FileTypeInfo file_type_info;
|
||||
|
||||
for (const auto& [name, extension_group] : filters) {
|
||||
file_type_info.extension_description_overrides.push_back(
|
||||
base::UTF8ToUTF16(name));
|
||||
|
||||
const bool has_all_files_wildcard = base::ranges::any_of(
|
||||
extension_group, [](const auto& ext) { return ext == "*"; });
|
||||
if (has_all_files_wildcard) {
|
||||
file_type_info.include_all_files = true;
|
||||
} else {
|
||||
file_type_info.extensions.emplace_back(extension_group);
|
||||
}
|
||||
}
|
||||
|
||||
return file_type_info;
|
||||
}
|
||||
|
||||
class FileChooserDialog : public ui::SelectFileDialog::Listener {
|
||||
public:
|
||||
enum class DialogType { OPEN, SAVE };
|
||||
|
||||
FileChooserDialog() { dialog_ = ui::SelectFileDialog::Create(this, nullptr); }
|
||||
|
||||
~FileChooserDialog() override = default;
|
||||
|
||||
gtk::ExtraSettings GetExtraSettings(const DialogSettings& settings) {
|
||||
gtk::ExtraSettings extra;
|
||||
extra.button_label = settings.button_label;
|
||||
extra.show_overwrite_confirmation =
|
||||
settings.properties & SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION;
|
||||
extra.allow_multiple_selection =
|
||||
settings.properties & OPEN_DIALOG_MULTI_SELECTIONS;
|
||||
if (type_ == DialogType::SAVE) {
|
||||
extra.show_hidden = settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES;
|
||||
} else {
|
||||
extra.show_hidden = settings.properties & OPEN_DIALOG_SHOW_HIDDEN_FILES;
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
void RunSaveDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::SAVE;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
ui::SelectFileDialog::SELECT_SAVEAS_FILE,
|
||||
base::UTF8ToUTF16(settings.title), settings.default_path,
|
||||
&file_info /* file_types */, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunSaveDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunSaveDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::OPEN;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
GetDialogType(settings.properties), base::UTF8ToUTF16(settings.title),
|
||||
settings.default_path, &file_info, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunOpenDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void FileSelected(const ui::SelectedFileInfo& file,
|
||||
int index,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", file.file_path);
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>{file.file_path});
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MultiFilesSelected(const std::vector<ui::SelectedFileInfo>& files,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", ui::SelectedFileInfoListToFilePathList(files));
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileSelectionCanceled(void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", true);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", base::FilePath());
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
DialogType type_;
|
||||
scoped_refptr<ui::SelectFileDialog> dialog_;
|
||||
base::OnceCallback<void(gin_helper::Dictionary)> callback_;
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePaths", file_paths);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), paths);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(cb), settings);
|
||||
|
||||
run_loop.Run();
|
||||
return !paths->empty();
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, base::FilePath* file_path,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePath", file_path);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), path);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
run_loop.Run();
|
||||
return !path->empty();
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "chrome/common/webui_url_constants.h"
|
||||
#include "chrome/grit/accessibility_resources.h" // nogncheck
|
||||
@@ -40,11 +39,17 @@
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_delegate.h"
|
||||
#include "content/public/browser/web_ui_data_source.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
#include "ui/accessibility/accessibility_features.h"
|
||||
#include "ui/accessibility/platform/ax_platform_node.h"
|
||||
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
|
||||
#include "ui/base/webui/web_ui_util.h"
|
||||
#include "ui/views/accessibility/view_accessibility.h"
|
||||
#include "ui/views/accessibility/widget_ax_tree_id_map.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -57,26 +62,34 @@ static const char kFaviconUrlField[] = "faviconUrl";
|
||||
static const char kNameField[] = "name";
|
||||
static const char kPagesField[] = "pages";
|
||||
static const char kPidField[] = "pid";
|
||||
static const char kSessionIdField[] = "sessionId";
|
||||
static const char kProcessIdField[] = "processId";
|
||||
static const char kRequestTypeField[] = "requestType";
|
||||
static const char kRoutingIdField[] = "routingId";
|
||||
static const char kSessionIdField[] = "sessionId";
|
||||
static const char kSupportedApiTypesField[] = "supportedApiTypes";
|
||||
static const char kTreeField[] = "tree";
|
||||
static const char kTypeField[] = "type";
|
||||
static const char kUrlField[] = "url";
|
||||
static const char kTreeField[] = "tree";
|
||||
static const char kWidgetsField[] = "widgets";
|
||||
static const char kApiTypeField[] = "apiType";
|
||||
|
||||
#if defined(USE_AURA)
|
||||
static const char kWidgetIdField[] = "widgetId";
|
||||
static const char kWidget[] = "widget";
|
||||
#endif
|
||||
|
||||
// Global flags
|
||||
static const char kBrowser[] = "browser";
|
||||
static const char kCopyTree[] = "copyTree";
|
||||
static const char kHTML[] = "html";
|
||||
static const char kInternal[] = "internal";
|
||||
static const char kLabelImages[] = "labelImages";
|
||||
static const char kLocked[] = "locked";
|
||||
static const char kNative[] = "native";
|
||||
static const char kPage[] = "page";
|
||||
static const char kPDF[] = "pdf";
|
||||
static const char kPDFPrinting[] = "pdfPrinting";
|
||||
static const char kScreenReader[] = "screenreader";
|
||||
static const char kShowOrRefreshTree[] = "showOrRefreshTree";
|
||||
static const char kText[] = "text";
|
||||
static const char kViewsAccessibility[] = "viewsAccessibility";
|
||||
static const char kWeb[] = "web";
|
||||
|
||||
// Possible global flag values
|
||||
@@ -141,75 +154,23 @@ base::Value::Dict BuildTargetDescriptor(electron::NativeWindow* window) {
|
||||
return target_data;
|
||||
}
|
||||
|
||||
#if defined(USE_AURA)
|
||||
base::Value::Dict BuildTargetDescriptor(views::Widget* widget) {
|
||||
base::Value::Dict widget_data;
|
||||
widget_data.Set(kNameField, widget->widget_delegate()->GetWindowTitle());
|
||||
widget_data.Set(kTypeField, kWidget);
|
||||
|
||||
// Use the Widget's root view ViewAccessibility's unique ID for lookup.
|
||||
int id = widget->GetRootView()->GetViewAccessibility().GetUniqueId().Get();
|
||||
widget_data.Set(kWidgetIdField, id);
|
||||
return widget_data;
|
||||
}
|
||||
#endif // defined(USE_AURA)
|
||||
|
||||
bool ShouldHandleAccessibilityRequestCallback(const std::string& path) {
|
||||
return path == kTargetsDataFile;
|
||||
}
|
||||
|
||||
// Add property filters to the property_filters vector for the given property
|
||||
// filter type. The attributes are passed in as a string with each attribute
|
||||
// separated by a space.
|
||||
void AddPropertyFilters(std::vector<ui::AXPropertyFilter>* property_filters,
|
||||
const std::string& attributes,
|
||||
ui::AXPropertyFilter::Type type) {
|
||||
for (const std::string& attribute : base::SplitString(
|
||||
attributes, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
|
||||
property_filters->emplace_back(attribute, type);
|
||||
}
|
||||
}
|
||||
|
||||
bool MatchesPropertyFilters(
|
||||
const std::vector<ui::AXPropertyFilter>& property_filters,
|
||||
const std::string& text) {
|
||||
bool allow = false;
|
||||
for (const auto& filter : property_filters) {
|
||||
if (base::MatchPattern(text, filter.match_str)) {
|
||||
switch (filter.type) {
|
||||
case ui::AXPropertyFilter::ALLOW_EMPTY:
|
||||
case ui::AXPropertyFilter::SCRIPT:
|
||||
allow = true;
|
||||
break;
|
||||
case ui::AXPropertyFilter::ALLOW:
|
||||
allow = (!base::MatchPattern(text, "*=''"));
|
||||
break;
|
||||
case ui::AXPropertyFilter::DENY:
|
||||
allow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
std::string RecursiveDumpAXPlatformNodeAsString(
|
||||
const ui::AXPlatformNode* node,
|
||||
int indent,
|
||||
const std::vector<ui::AXPropertyFilter>& property_filters) {
|
||||
if (!node)
|
||||
return "";
|
||||
std::string str(2 * indent, '+');
|
||||
const std::string line = node->GetDelegate()->GetData().ToString();
|
||||
const std::vector<std::string> attributes = base::SplitString(
|
||||
line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
||||
for (const std::string& attribute : attributes) {
|
||||
if (MatchesPropertyFilters(property_filters, attribute)) {
|
||||
str += attribute + " ";
|
||||
}
|
||||
}
|
||||
str += "\n";
|
||||
for (size_t i = 0; i < node->GetDelegate()->GetChildCount(); i++) {
|
||||
gfx::NativeViewAccessible child = node->GetDelegate()->ChildAtIndex(i);
|
||||
const ui::AXPlatformNode* child_node =
|
||||
ui::AXPlatformNode::FromNativeViewAccessible(child);
|
||||
str += RecursiveDumpAXPlatformNodeAsString(child_node, indent + 1,
|
||||
property_filters);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
bool IsValidJSValue(const std::string* str) {
|
||||
return str && str->length() < 5000U;
|
||||
}
|
||||
|
||||
void HandleAccessibilityRequestCallback(
|
||||
content::BrowserContext* current_context,
|
||||
const std::string& path,
|
||||
@@ -217,6 +178,8 @@ void HandleAccessibilityRequestCallback(
|
||||
DCHECK(ShouldHandleAccessibilityRequestCallback(path));
|
||||
|
||||
base::Value::Dict data;
|
||||
PrefService* pref =
|
||||
static_cast<electron::ElectronBrowserContext*>(current_context)->prefs();
|
||||
ui::AXMode mode =
|
||||
content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode();
|
||||
bool is_native_enabled = content::BrowserAccessibilityState::GetInstance()
|
||||
@@ -226,7 +189,7 @@ void HandleAccessibilityRequestCallback(
|
||||
bool text = mode.has_mode(ui::AXMode::kInlineTextBoxes);
|
||||
bool screenreader = mode.has_mode(ui::AXMode::kScreenReader);
|
||||
bool html = mode.has_mode(ui::AXMode::kHTML);
|
||||
bool pdf = mode.has_mode(ui::AXMode::kPDFPrinting);
|
||||
bool pdf_printing = mode.has_mode(ui::AXMode::kPDFPrinting);
|
||||
|
||||
// The "native" and "web" flags are disabled if
|
||||
// --disable-renderer-accessibility is set.
|
||||
@@ -241,73 +204,161 @@ void HandleAccessibilityRequestCallback(
|
||||
is_web_enabled ? (screenreader ? kOn : kOff) : kDisabled);
|
||||
data.Set(kHTML, is_web_enabled ? (html ? kOn : kOff) : kDisabled);
|
||||
|
||||
// TODO(codebytere): enable use of this flag.
|
||||
//
|
||||
// The "labelImages" flag works only if "web" is enabled, the current profile
|
||||
// has the kAccessibilityImageLabelsEnabled preference set and the appropriate
|
||||
// command line switch has been used. Since this is so closely tied into user
|
||||
// prefs and causes bugs, we're disabling it for now.
|
||||
bool are_accessibility_image_labels_enabled = is_web_enabled;
|
||||
data.Set(kLabelImages, kDisabled);
|
||||
// The "pdfPrinting" flag is independent of the others.
|
||||
data.Set(kPDFPrinting, pdf_printing ? kOn : kOff);
|
||||
|
||||
// The "pdf" flag is independent of the others.
|
||||
data.Set(kPDF, pdf ? kOn : kOff);
|
||||
// The "Top Level Widgets" section is only relevant if views accessibility is
|
||||
// enabled.
|
||||
data.Set(kViewsAccessibility, features::IsAccessibilityTreeForViewsEnabled());
|
||||
|
||||
// Always dump the Accessibility tree.
|
||||
data.Set(kInternal, kOn);
|
||||
std::string pref_api_type =
|
||||
std::string(pref->GetString(prefs::kShownAccessibilityApiType));
|
||||
bool pref_api_type_supported = false;
|
||||
|
||||
base::Value::List rvh_list;
|
||||
std::unique_ptr<content::RenderWidgetHostIterator> widgets(
|
||||
std::vector<ui::AXApiType::Type> supported_api_types =
|
||||
content::AXInspectFactory::SupportedApis();
|
||||
base::Value::List supported_api_list;
|
||||
supported_api_list.reserve(supported_api_types.size());
|
||||
for (ui::AXApiType::Type type : supported_api_types) {
|
||||
supported_api_list.Append(std::string_view(type));
|
||||
if (type == ui::AXApiType::From(pref_api_type)) {
|
||||
pref_api_type_supported = true;
|
||||
}
|
||||
}
|
||||
data.Set(kSupportedApiTypesField, std::move(supported_api_list));
|
||||
|
||||
// If the saved API type is not supported, use the default platform formatter
|
||||
// API type.
|
||||
if (!pref_api_type_supported) {
|
||||
pref_api_type = std::string_view(
|
||||
content::AXInspectFactory::DefaultPlatformFormatterType());
|
||||
}
|
||||
data.Set(kApiTypeField, pref_api_type);
|
||||
|
||||
bool is_mode_locked = !content::BrowserAccessibilityState::GetInstance()
|
||||
->IsAXModeChangeAllowed();
|
||||
data.Set(kLocked, is_mode_locked ? kOn : kOff);
|
||||
|
||||
base::Value::List page_list;
|
||||
std::unique_ptr<content::RenderWidgetHostIterator> widget_iter(
|
||||
content::RenderWidgetHost::GetRenderWidgetHosts());
|
||||
|
||||
while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
|
||||
while (content::RenderWidgetHost* widget = widget_iter->GetNextHost()) {
|
||||
// Ignore processes that don't have a connection, such as crashed tabs.
|
||||
if (!widget->GetProcess()->IsInitializedAndNotDead())
|
||||
if (!widget->GetProcess()->IsInitializedAndNotDead()) {
|
||||
continue;
|
||||
}
|
||||
content::RenderViewHost* rvh = content::RenderViewHost::From(widget);
|
||||
if (!rvh)
|
||||
if (!rvh) {
|
||||
continue;
|
||||
}
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromRenderViewHost(rvh);
|
||||
content::WebContentsDelegate* delegate = web_contents->GetDelegate();
|
||||
if (!delegate)
|
||||
if (!delegate) {
|
||||
continue;
|
||||
}
|
||||
if (web_contents->GetPrimaryMainFrame()->GetRenderViewHost() != rvh) {
|
||||
continue;
|
||||
}
|
||||
// Ignore views that are never user-visible, like background pages.
|
||||
if (delegate->IsNeverComposited(web_contents))
|
||||
if (delegate->IsNeverComposited(web_contents)) {
|
||||
continue;
|
||||
}
|
||||
content::BrowserContext* context = rvh->GetProcess()->GetBrowserContext();
|
||||
if (context != current_context)
|
||||
if (context != current_context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
base::Value::Dict descriptor = BuildTargetDescriptor(rvh);
|
||||
descriptor.Set(kNative, is_native_enabled);
|
||||
descriptor.Set(kScreenReader, is_web_enabled && screenreader);
|
||||
descriptor.Set(kWeb, is_web_enabled);
|
||||
descriptor.Set(kLabelImages, are_accessibility_image_labels_enabled);
|
||||
rvh_list.Append(std::move(descriptor));
|
||||
page_list.Append(std::move(descriptor));
|
||||
}
|
||||
|
||||
data.Set(kPagesField, std::move(rvh_list));
|
||||
data.Set(kPagesField, std::move(page_list));
|
||||
|
||||
base::Value::List window_list;
|
||||
for (auto* window : electron::WindowList::GetWindows()) {
|
||||
window_list.Append(BuildTargetDescriptor(window));
|
||||
}
|
||||
|
||||
data.Set(kBrowsersField, std::move(window_list));
|
||||
|
||||
base::Value::List widgets_list;
|
||||
#if defined(USE_AURA)
|
||||
if (features::IsAccessibilityTreeForViewsEnabled()) {
|
||||
views::WidgetAXTreeIDMap& manager_map =
|
||||
views::WidgetAXTreeIDMap::GetInstance();
|
||||
const std::vector<views::Widget*> widgets = manager_map.GetWidgets();
|
||||
for (views::Widget* widget : widgets) {
|
||||
widgets_list.Append(BuildTargetDescriptor(widget));
|
||||
}
|
||||
}
|
||||
#endif // defined(USE_AURA)
|
||||
data.Set(kWidgetsField, std::move(widgets_list));
|
||||
|
||||
std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
|
||||
base::WriteJson(data).value_or("")));
|
||||
}
|
||||
|
||||
std::string RecursiveDumpAXPlatformNodeAsString(
|
||||
ui::AXPlatformNode* node,
|
||||
int indent,
|
||||
const std::vector<ui::AXPropertyFilter>& property_filters) {
|
||||
if (!node) {
|
||||
return "";
|
||||
}
|
||||
std::string str(2 * indent, '+');
|
||||
std::string line = node->GetDelegate()->GetData().ToString();
|
||||
std::vector<std::string> attributes = base::SplitString(
|
||||
line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
||||
for (std::string attribute : attributes) {
|
||||
if (ui::AXTreeFormatter::MatchesPropertyFilters(property_filters, attribute,
|
||||
false)) {
|
||||
str += attribute + " ";
|
||||
}
|
||||
}
|
||||
str += "\n";
|
||||
for (size_t i = 0; i < node->GetDelegate()->GetChildCount(); i++) {
|
||||
gfx::NativeViewAccessible child = node->GetDelegate()->ChildAtIndex(i);
|
||||
ui::AXPlatformNode* child_node =
|
||||
ui::AXPlatformNode::FromNativeViewAccessible(child);
|
||||
str += RecursiveDumpAXPlatformNodeAsString(child_node, indent + 1,
|
||||
property_filters);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Add property filters to the property_filters vector for the given property
|
||||
// filter type. The attributes are passed in as a string with each attribute
|
||||
// separated by a space.
|
||||
void AddPropertyFilters(std::vector<ui::AXPropertyFilter>& property_filters,
|
||||
const std::string& attributes,
|
||||
ui::AXPropertyFilter::Type type) {
|
||||
for (const std::string& attribute : base::SplitString(
|
||||
attributes, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
|
||||
property_filters.emplace_back(attribute, type);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidJSValue(const std::string* str) {
|
||||
return str && str->length() < 5000U;
|
||||
}
|
||||
|
||||
const std::string& CheckJSValue(const std::string* str) {
|
||||
CHECK(IsValidJSValue(str));
|
||||
return *str;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronAccessibilityUI::ElectronAccessibilityUI(content::WebUI* web_ui)
|
||||
: content::WebUIController(web_ui) {
|
||||
auto* const browser_context = web_ui->GetWebContents()->GetBrowserContext();
|
||||
// Set up the chrome://accessibility source.
|
||||
content::WebUIDataSource* html_source =
|
||||
content::WebUIDataSource::CreateAndAdd(
|
||||
web_ui->GetWebContents()->GetBrowserContext(),
|
||||
chrome::kChromeUIAccessibilityHost);
|
||||
browser_context, chrome::kChromeUIAccessibilityHost);
|
||||
|
||||
// Add required resources.
|
||||
html_source->UseStringsJs();
|
||||
@@ -317,8 +368,7 @@ ElectronAccessibilityUI::ElectronAccessibilityUI(content::WebUI* web_ui)
|
||||
html_source->SetRequestFilter(
|
||||
base::BindRepeating(&ShouldHandleAccessibilityRequestCallback),
|
||||
base::BindRepeating(&HandleAccessibilityRequestCallback,
|
||||
web_ui->GetWebContents()->GetBrowserContext()));
|
||||
|
||||
browser_context));
|
||||
html_source->OverrideContentSecurityPolicy(
|
||||
network::mojom::CSPDirectiveName::TrustedTypes,
|
||||
"trusted-types parse-html-subset sanitize-inner-html;");
|
||||
@@ -332,33 +382,35 @@ ElectronAccessibilityUI::~ElectronAccessibilityUI() = default;
|
||||
ElectronAccessibilityUIMessageHandler::ElectronAccessibilityUIMessageHandler() =
|
||||
default;
|
||||
|
||||
void ElectronAccessibilityUIMessageHandler::GetRequestTypeAndFilters(
|
||||
const base::Value::Dict& data,
|
||||
std::string& request_type,
|
||||
std::string& allow,
|
||||
std::string& allow_empty,
|
||||
std::string& deny) {
|
||||
request_type = CheckJSValue(data.FindString(kRequestTypeField));
|
||||
CHECK(request_type == kShowOrRefreshTree || request_type == kCopyTree);
|
||||
allow = CheckJSValue(data.FindStringByDottedPath("filters.allow"));
|
||||
allow_empty = CheckJSValue(data.FindStringByDottedPath("filters.allowEmpty"));
|
||||
deny = CheckJSValue(data.FindStringByDottedPath("filters.deny"));
|
||||
}
|
||||
|
||||
void ElectronAccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
const base::Value::List& args) {
|
||||
const base::Value::Dict& data = args.front().GetDict();
|
||||
|
||||
const int window_id = *data.FindInt(kSessionIdField);
|
||||
const std::string* const request_type_p = data.FindString(kRequestTypeField);
|
||||
CHECK(IsValidJSValue(request_type_p));
|
||||
std::string request_type = *request_type_p;
|
||||
CHECK(request_type == kShowOrRefreshTree || request_type == kCopyTree);
|
||||
request_type = "accessibility." + request_type;
|
||||
std::string request_type, allow, allow_empty, deny;
|
||||
GetRequestTypeAndFilters(data, request_type, allow, allow_empty, deny);
|
||||
|
||||
const std::string* const allow_p =
|
||||
data.FindStringByDottedPath("filters.allow");
|
||||
CHECK(IsValidJSValue(allow_p));
|
||||
const std::string* const allow_empty_p =
|
||||
data.FindStringByDottedPath("filters.allowEmpty");
|
||||
CHECK(IsValidJSValue(allow_empty_p));
|
||||
const std::string* const deny_p = data.FindStringByDottedPath("filters.deny");
|
||||
CHECK(IsValidJSValue(deny_p));
|
||||
int window_id = *data.FindInt(kSessionIdField);
|
||||
|
||||
AllowJavascript();
|
||||
|
||||
std::vector<ui::AXPropertyFilter> property_filters;
|
||||
AddPropertyFilters(&property_filters, *allow_p, ui::AXPropertyFilter::ALLOW);
|
||||
AddPropertyFilters(&property_filters, *allow_empty_p,
|
||||
AddPropertyFilters(property_filters, allow, ui::AXPropertyFilter::ALLOW);
|
||||
AddPropertyFilters(property_filters, allow_empty,
|
||||
ui::AXPropertyFilter::ALLOW_EMPTY);
|
||||
AddPropertyFilters(&property_filters, *deny_p, ui::AXPropertyFilter::DENY);
|
||||
AddPropertyFilters(property_filters, deny, ui::AXPropertyFilter::DENY);
|
||||
|
||||
for (auto* window : electron::WindowList::GetWindows()) {
|
||||
if (window->window_id() == window_id) {
|
||||
@@ -366,9 +418,9 @@ void ElectronAccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
gfx::NativeWindow native_window = window->GetNativeWindow();
|
||||
ui::AXPlatformNode* node =
|
||||
ui::AXPlatformNode::FromNativeWindow(native_window);
|
||||
result.Set(kTreeField, base::Value(RecursiveDumpAXPlatformNodeAsString(
|
||||
node, 0, property_filters)));
|
||||
CallJavascriptFunction(request_type, base::Value(std::move(result)));
|
||||
result.Set(kTreeField, RecursiveDumpAXPlatformNodeAsString(
|
||||
node, 0, property_filters));
|
||||
FireWebUIListener(request_type, result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -378,7 +430,7 @@ void ElectronAccessibilityUIMessageHandler::RequestNativeUITree(
|
||||
result.Set(kSessionIdField, window_id);
|
||||
result.Set(kTypeField, kBrowser);
|
||||
result.Set(kErrorField, "Window no longer exists.");
|
||||
CallJavascriptFunction(request_type, base::Value(std::move(result)));
|
||||
FireWebUIListener(request_type, result);
|
||||
}
|
||||
|
||||
void ElectronAccessibilityUIMessageHandler::RegisterMessages() {
|
||||
@@ -393,6 +445,10 @@ void ElectronAccessibilityUIMessageHandler::RegisterMessages() {
|
||||
"setGlobalFlag",
|
||||
base::BindRepeating(&AccessibilityUIMessageHandler::SetGlobalFlag,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"setGlobalString",
|
||||
base::BindRepeating(&AccessibilityUIMessageHandler::SetGlobalString,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"requestWebContentsTree",
|
||||
base::BindRepeating(
|
||||
@@ -403,9 +459,24 @@ void ElectronAccessibilityUIMessageHandler::RegisterMessages() {
|
||||
base::BindRepeating(
|
||||
&ElectronAccessibilityUIMessageHandler::RequestNativeUITree,
|
||||
base::Unretained(this)));
|
||||
#if defined(USE_AURA)
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"requestWidgetsTree",
|
||||
base::BindRepeating(&AccessibilityUIMessageHandler::RequestWidgetsTree,
|
||||
base::Unretained(this)));
|
||||
#endif
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"requestAccessibilityEvents",
|
||||
base::BindRepeating(
|
||||
&AccessibilityUIMessageHandler::RequestAccessibilityEvents,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
// static
|
||||
void ElectronAccessibilityUIMessageHandler::RegisterPrefs(
|
||||
user_prefs::PrefRegistrySyncable* registry) {
|
||||
const std::string_view default_api_type =
|
||||
std::string_view(ui::AXApiType::Type(ui::AXApiType::kBlink));
|
||||
registry->RegisterStringPref(prefs::kShownAccessibilityApiType,
|
||||
std::string(default_api_type));
|
||||
}
|
||||
|
||||
@@ -31,7 +31,14 @@ class ElectronAccessibilityUIMessageHandler
|
||||
|
||||
void RegisterMessages() final;
|
||||
|
||||
static void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry);
|
||||
|
||||
private:
|
||||
void GetRequestTypeAndFilters(const base::Value::Dict& data,
|
||||
std::string& request_type,
|
||||
std::string& allow,
|
||||
std::string& allow_empty,
|
||||
std::string& deny);
|
||||
void RequestNativeUITree(const base::Value::List& args);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "base/apple/foundation_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/bind_post_task.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "shell/common/gin_converters/image_converter.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
@@ -38,6 +39,23 @@ double safeShift(double in, double def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
void ReceivedThumbnailResult(CGSize size,
|
||||
gin_helper::Promise<gfx::Image> p,
|
||||
QLThumbnailRepresentation* thumbnail,
|
||||
NSError* error) {
|
||||
if (error || !thumbnail) {
|
||||
std::string err_msg([error.localizedDescription UTF8String]);
|
||||
p.RejectWithErrorMessage("unable to retrieve thumbnail preview "
|
||||
"image for the given path: " +
|
||||
err_msg);
|
||||
} else {
|
||||
NSImage* result = [[NSImage alloc] initWithCGImage:[thumbnail CGImage]
|
||||
size:size];
|
||||
gfx::Image image(result);
|
||||
p.Resolve(image);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Promise> NativeImage::CreateThumbnailFromPath(
|
||||
v8::Isolate* isolate,
|
||||
@@ -70,31 +88,15 @@ v8::Local<v8::Promise> NativeImage::CreateThumbnailFromPath(
|
||||
size:cg_size
|
||||
scale:[screen backingScaleFactor]
|
||||
representationTypes:QLThumbnailGenerationRequestRepresentationTypeAll]);
|
||||
__block gin_helper::Promise<gfx::Image> p = std::move(promise);
|
||||
__block auto block_callback = base::BindPostTaskToCurrentDefault(
|
||||
base::BindOnce(&ReceivedThumbnailResult, cg_size, std::move(promise)));
|
||||
auto completionHandler =
|
||||
^(QLThumbnailRepresentation* thumbnail, NSError* error) {
|
||||
std::move(block_callback).Run(thumbnail, error);
|
||||
};
|
||||
[[QLThumbnailGenerator sharedGenerator]
|
||||
generateBestRepresentationForRequest:request
|
||||
completionHandler:^(
|
||||
QLThumbnailRepresentation* thumbnail,
|
||||
NSError* error) {
|
||||
if (error || !thumbnail) {
|
||||
std::string err_msg(
|
||||
[error.localizedDescription UTF8String]);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
p.RejectWithErrorMessage(
|
||||
"unable to retrieve thumbnail preview "
|
||||
"image for the given path: " +
|
||||
err_msg);
|
||||
});
|
||||
} else {
|
||||
NSImage* result = [[NSImage alloc]
|
||||
initWithCGImage:[thumbnail CGImage]
|
||||
size:cg_size];
|
||||
gfx::Image image(result);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
p.Resolve(image);
|
||||
});
|
||||
}
|
||||
}];
|
||||
completionHandler:completionHandler];
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "base/hash/hash.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "shell/common/api/electron_api_key_weak_map.h"
|
||||
@@ -17,18 +16,6 @@
|
||||
#include "url/origin.h"
|
||||
#include "v8/include/v8-profiler.h"
|
||||
|
||||
namespace std {
|
||||
|
||||
// The hash function used by DoubleIDWeakMap.
|
||||
template <typename Type1, typename Type2>
|
||||
struct hash<std::pair<Type1, Type2>> {
|
||||
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
||||
return base::HashInts(base::Hash(value.first), value.second);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <typename Type1, typename Type2>
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
#include "base/apple/osstatus_logging.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/scoped_aedesc.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/apple/url_conversions.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "url/gurl.h"
|
||||
@@ -183,15 +187,12 @@ void OpenExternal(const GURL& url,
|
||||
return;
|
||||
}
|
||||
|
||||
bool activate = options.activate;
|
||||
__block OpenCallback c = std::move(callback);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
__block std::string error = OpenURL(ns_url, activate);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
std::move(c).Run(error);
|
||||
});
|
||||
});
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
{base::MayBlock(), base::WithBaseSyncPrimitives(),
|
||||
base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
|
||||
base::BindOnce(&OpenURL, ns_url, options.activate), std::move(callback));
|
||||
}
|
||||
|
||||
bool MoveItemToTrashWithError(const base::FilePath& full_path,
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "components/pdf/common/constants.h"
|
||||
#include "components/pdf/common/pdf_util.h"
|
||||
#include "components/pdf/common/constants.h" // nogncheck
|
||||
#include "components/pdf/common/pdf_util.h" // nogncheck
|
||||
#include "components/pdf/renderer/pdf_internal_plugin_delegate.h"
|
||||
#include "shell/common/electron_constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
@@ -6596,6 +6596,7 @@ describe('BrowserWindow module', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
ifit(hasCapturableScreen())('should allow the window to be dragged when enabled', async () => {
|
||||
// FIXME: nut-js has been removed from npm; we need to find a replacement
|
||||
// WOA fails to load libnut so we're using require to defer loading only
|
||||
// on supported platforms.
|
||||
// "@nut-tree\libnut-win32\build\Release\libnut.node is not a valid Win32 application."
|
||||
@@ -6640,6 +6641,7 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
|
||||
ifit(hasCapturableScreen())('should allow the window to be dragged when no WCO and --webkit-app-region: drag enabled', async () => {
|
||||
// FIXME: nut-js has been removed from npm; we need to find a replacement
|
||||
// @ts-ignore: nut-js is an optional dependency so it may not be installed
|
||||
const { mouse, straightTo, centerOf, Region, Button } = require('@nut-tree/nut-js') as typeof import('@nut-tree/nut-js');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import { nativeImage } from 'electron/common';
|
||||
import { ifdescribe, ifit } from './lib/spec-helpers';
|
||||
import { ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers';
|
||||
import * as path from 'node:path';
|
||||
|
||||
describe('nativeImage module', () => {
|
||||
@@ -426,6 +426,8 @@ describe('nativeImage module', () => {
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'linux')('createThumbnailFromPath(path, size)', () => {
|
||||
useRemoteContext({ webPreferences: { contextIsolation: false, nodeIntegration: true } });
|
||||
|
||||
it('throws when invalid size is passed', async () => {
|
||||
const badSize = { width: -1, height: -1 };
|
||||
|
||||
@@ -473,6 +475,13 @@ describe('nativeImage module', () => {
|
||||
const result = await nativeImage.createThumbnailFromPath(imgPath, maxSize);
|
||||
expect(result.getSize()).to.deep.equal(maxSize);
|
||||
});
|
||||
|
||||
itremote('works in the renderer', async (path: string) => {
|
||||
const { nativeImage } = require('electron');
|
||||
const goodSize = { width: 100, height: 100 };
|
||||
const result = await nativeImage.createThumbnailFromPath(path, goodSize);
|
||||
expect(result.isEmpty()).to.equal(false);
|
||||
}, [path.join(fixturesPath, 'assets', 'logo.png')]);
|
||||
});
|
||||
|
||||
describe('addRepresentation()', () => {
|
||||
|
||||
@@ -1381,6 +1381,49 @@ describe('protocol module', () => {
|
||||
expect(body).to.equal(text);
|
||||
});
|
||||
|
||||
it('can receive stream request body asynchronously', async () => {
|
||||
let done: any;
|
||||
const requestReceived: Promise<Buffer[]> = new Promise(resolve => { done = resolve; });
|
||||
protocol.handle('http-like', async (req) => {
|
||||
const chunks = [];
|
||||
for await (const chunk of (req.body as any)) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
done(chunks);
|
||||
return new Response('ok');
|
||||
});
|
||||
defer(() => { protocol.unhandle('http-like'); });
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.loadURL('about:blank');
|
||||
const expectedHashChunks = await w.webContents.executeJavaScript(`
|
||||
const dataStream = () =>
|
||||
new ReadableStream({
|
||||
async start(controller) {
|
||||
for (let i = 0; i < 10; i++) { controller.enqueue(Array(1024 * 128).fill(+i).join("\\n")); }
|
||||
controller.close();
|
||||
},
|
||||
}).pipeThrough(new TextEncoderStream());
|
||||
fetch(
|
||||
new Request("http-like://host", {
|
||||
method: "POST",
|
||||
body: dataStream(),
|
||||
duplex: "half",
|
||||
})
|
||||
);
|
||||
(async () => {
|
||||
const chunks = []
|
||||
for await (const chunk of dataStream()) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return chunks;
|
||||
})()
|
||||
`);
|
||||
const expectedHash = Buffer.from(await crypto.subtle.digest('SHA-256', Buffer.concat(expectedHashChunks))).toString('hex');
|
||||
const body = Buffer.concat(await requestReceived);
|
||||
const actualHash = Buffer.from(await crypto.subtle.digest('SHA-256', Buffer.from(body))).toString('hex');
|
||||
expect(actualHash).to.equal(expectedHash);
|
||||
});
|
||||
|
||||
it('can receive multi-part postData from loadURL', async () => {
|
||||
protocol.handle('test-scheme', (req) => new Response(req.body));
|
||||
defer(() => { protocol.unhandle('test-scheme'); });
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('shell module', () => {
|
||||
});
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('opens an external link', async () => {
|
||||
async function urlOpened () {
|
||||
let url = 'http://127.0.0.1';
|
||||
let requestReceived: Promise<any>;
|
||||
if (process.platform === 'linux') {
|
||||
@@ -53,12 +53,26 @@ describe('shell module', () => {
|
||||
url = (await listen(server)).url;
|
||||
requestReceived = new Promise<void>(resolve => server.on('connection', () => resolve()));
|
||||
}
|
||||
return { url, requestReceived };
|
||||
}
|
||||
|
||||
it('opens an external link', async () => {
|
||||
const { url, requestReceived } = await urlOpened();
|
||||
await Promise.all<void>([
|
||||
shell.openExternal(url),
|
||||
requestReceived
|
||||
]);
|
||||
});
|
||||
|
||||
it('opens an external link in the renderer', async () => {
|
||||
const { url, requestReceived } = await urlOpened();
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: false, contextIsolation: false, nodeIntegration: true } });
|
||||
await w.loadURL('about:blank');
|
||||
await Promise.all<void>([
|
||||
w.webContents.executeJavaScript(`require("electron").shell.openExternal(${JSON.stringify(url)})`),
|
||||
requestReceived
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shell.trashItem()', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { expect } from 'chai';
|
||||
import { closeWindow } from './lib/window-helpers';
|
||||
import { BaseWindow, View } from 'electron/main';
|
||||
|
||||
@@ -10,6 +11,47 @@ describe('View', () => {
|
||||
|
||||
it('can be used as content view', () => {
|
||||
w = new BaseWindow({ show: false });
|
||||
w.setContentView(new View());
|
||||
const v = new View();
|
||||
w.setContentView(v);
|
||||
expect(w.contentView).to.equal(v);
|
||||
});
|
||||
|
||||
it('will throw when added as a child to itself', () => {
|
||||
w = new BaseWindow({ show: false });
|
||||
expect(() => {
|
||||
w.contentView.addChildView(w.contentView);
|
||||
}).to.throw('A view cannot be added as its own child');
|
||||
});
|
||||
|
||||
it('does not crash when attempting to add a child multiple times', () => {
|
||||
w = new BaseWindow({ show: false });
|
||||
const cv = new View();
|
||||
w.setContentView(cv);
|
||||
|
||||
const v = new View();
|
||||
w.contentView.addChildView(v);
|
||||
w.contentView.addChildView(v);
|
||||
w.contentView.addChildView(v);
|
||||
|
||||
expect(w.contentView.children).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('correctly reorders children', () => {
|
||||
w = new BaseWindow({ show: false });
|
||||
const cv = new View();
|
||||
w.setContentView(cv);
|
||||
|
||||
const v1 = new View();
|
||||
const v2 = new View();
|
||||
const v3 = new View();
|
||||
w.contentView.addChildView(v1);
|
||||
w.contentView.addChildView(v2);
|
||||
w.contentView.addChildView(v3);
|
||||
|
||||
expect(w.contentView.children).to.deep.equal([v1, v2, v3]);
|
||||
|
||||
w.contentView.addChildView(v1);
|
||||
w.contentView.addChildView(v2);
|
||||
expect(w.contentView.children).to.deep.equal([v3, v1, v2]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,25 @@ describe('WebContentsView', () => {
|
||||
v.removeChildView(wcv);
|
||||
});
|
||||
|
||||
it('correctly reorders children', () => {
|
||||
const w = new BaseWindow({ show: false });
|
||||
const cv = new View();
|
||||
w.setContentView(cv);
|
||||
|
||||
const wcv1 = new WebContentsView();
|
||||
const wcv2 = new WebContentsView();
|
||||
const wcv3 = new WebContentsView();
|
||||
w.contentView.addChildView(wcv1);
|
||||
w.contentView.addChildView(wcv2);
|
||||
w.contentView.addChildView(wcv3);
|
||||
|
||||
expect(w.contentView.children).to.deep.equal([wcv1, wcv2, wcv3]);
|
||||
|
||||
w.contentView.addChildView(wcv1);
|
||||
w.contentView.addChildView(wcv2);
|
||||
expect(w.contentView.children).to.deep.equal([wcv3, wcv1, wcv2]);
|
||||
});
|
||||
|
||||
function triggerGCByAllocation () {
|
||||
const arr = [];
|
||||
for (let i = 0; i < 1000000; i++) {
|
||||
@@ -60,6 +79,18 @@ describe('WebContentsView', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('can be fullscreened', async () => {
|
||||
const w = new BaseWindow();
|
||||
const v = new WebContentsView();
|
||||
w.setContentView(v);
|
||||
await v.webContents.loadURL('data:text/html,<div id="div">This is a simple div.</div>');
|
||||
|
||||
const enterFullScreen = once(w, 'enter-full-screen');
|
||||
await v.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
|
||||
await enterFullScreen;
|
||||
expect(w.isFullScreen()).to.be.true('isFullScreen');
|
||||
});
|
||||
|
||||
describe('visibilityState', () => {
|
||||
it('is initially hidden', async () => {
|
||||
const v = new WebContentsView();
|
||||
@@ -67,7 +98,7 @@ describe('WebContentsView', () => {
|
||||
expect(await v.webContents.executeJavaScript('initialVisibility')).to.equal('hidden');
|
||||
});
|
||||
|
||||
it('becomes visibile when attached', async () => {
|
||||
it('becomes visible when attached', async () => {
|
||||
const v = new WebContentsView();
|
||||
await v.webContents.loadURL('about:blank');
|
||||
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('hidden');
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
"session module ses.cookies should set cookie for standard scheme",
|
||||
"webFrameMain module WebFrame.visibilityState should match window state",
|
||||
"reporting api sends a report for a deprecation",
|
||||
"chromium features SpeechSynthesis should emit lifecycle events"
|
||||
]
|
||||
"chromium features SpeechSynthesis should emit lifecycle events",
|
||||
"BrowserWindow module draggable regions should allow the window to be dragged when enabled",
|
||||
"BrowserWindow module draggable regions should allow the window to be dragged when no WCO and --webkit-app-region: drag enabled"
|
||||
]
|
||||
|
||||
@@ -40,6 +40,7 @@ protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } },
|
||||
{ scheme: global.zoomScheme, privileges: { standard: true, secure: true } },
|
||||
{ scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } },
|
||||
{ scheme: 'http-like', privileges: { standard: true, secure: true, corsEnabled: true, supportFetchAPI: true } },
|
||||
{ scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
||||
{ scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
||||
{ scheme: 'no-cors', privileges: { supportFetchAPI: true } },
|
||||
|
||||
@@ -38,9 +38,6 @@
|
||||
"ws": "^7.4.6",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@nut-tree/nut-js": "^3.1.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"nan": "file:../../third_party/nan",
|
||||
"dbus-native/optimist/minimist": "1.2.7",
|
||||
|
||||
775
spec/yarn.lock
775
spec/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user