mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
15 Commits
robo/enabl
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b417696d6b | ||
|
|
4203d7688f | ||
|
|
62e637275a | ||
|
|
28c0eb29df | ||
|
|
8a730e2aec | ||
|
|
044be7ce40 | ||
|
|
7245c6a3f0 | ||
|
|
b484b0bde9 | ||
|
|
6c8a910232 | ||
|
|
cc3d4f5f58 | ||
|
|
b711ce7b04 | ||
|
|
adf9a6e303 | ||
|
|
6744293e96 | ||
|
|
0d3342debf | ||
|
|
157cdac4b9 |
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@@ -442,34 +442,7 @@ jobs:
|
||||
contents: read
|
||||
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
|
||||
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
|
||||
steps:
|
||||
steps:
|
||||
- name: GitHub Actions Jobs Done
|
||||
run: |
|
||||
echo "All GitHub Actions Jobs are done"
|
||||
|
||||
check-signed-commits:
|
||||
name: Check signed commits in green PR
|
||||
needs: gha-done
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check signed commits in PR
|
||||
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
|
||||
with:
|
||||
comment: |
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
(`git push --force-with-lease`)
|
||||
|
||||
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
|
||||
|
||||
- name: Remove needs-signed-commits label
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --remove-label needs-signed-commits
|
||||
|
||||
@@ -13,7 +13,6 @@ permissions: {}
|
||||
jobs:
|
||||
check-signed-commits:
|
||||
name: Check signed commits in PR
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -23,9 +22,9 @@ jobs:
|
||||
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
|
||||
with:
|
||||
comment: |
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
(`git push --force-with-lease`)
|
||||
|
||||
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
|
||||
@@ -37,3 +36,11 @@ jobs:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --add-label needs-signed-commits
|
||||
|
||||
- name: Remove needs-signed-commits label
|
||||
if: ${{ success() && contains(github.event.pull_request.labels.*.name, 'needs-signed-commits') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --remove-label needs-signed-commits
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { shell } from 'electron/common';
|
||||
import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
|
||||
import { app, dialog, BrowserWindow, ipcMain, Menu } from 'electron/main';
|
||||
|
||||
import * as path from 'node:path';
|
||||
import * as url from 'node:url';
|
||||
@@ -11,6 +11,53 @@ app.on('window-all-closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const helpMenu: Electron.MenuItemConstructorOptions = {
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://electronjs.org');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: async () => {
|
||||
const version = process.versions.electron;
|
||||
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://discord.gg/electronjs');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://github.com/electron/electron/issues');
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
|
||||
const template: Electron.MenuItemConstructorOptions[] = [
|
||||
...(isMac ? [macAppMenu] : []),
|
||||
{ role: 'fileMenu' },
|
||||
{ role: 'editMenu' },
|
||||
{ role: 'viewMenu' },
|
||||
{ role: 'windowMenu' },
|
||||
helpMenu
|
||||
];
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
||||
});
|
||||
|
||||
function decorateURL (url: string) {
|
||||
// safely add `?utm_source=default_app
|
||||
const parsedUrl = new URL(url);
|
||||
|
||||
@@ -46,7 +46,7 @@ this has the additional effect of removing the menu bar from the window.
|
||||
|
||||
> [!NOTE]
|
||||
> The default menu will be created automatically if the app does not set one.
|
||||
> It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`.
|
||||
> It contains standard items such as `File`, `Edit`, `View`, and `Window`.
|
||||
|
||||
#### `Menu.getApplicationMenu()`
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { shell } from 'electron/common';
|
||||
import { app, Menu } from 'electron/main';
|
||||
import { Menu } from 'electron/main';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
@@ -12,47 +11,13 @@ export const setApplicationMenuWasSet = () => {
|
||||
export const setDefaultApplicationMenu = () => {
|
||||
if (applicationMenuWasSet) return;
|
||||
|
||||
const helpMenu: Electron.MenuItemConstructorOptions = {
|
||||
role: 'help',
|
||||
submenu: app.isPackaged
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://electronjs.org');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: async () => {
|
||||
const version = process.versions.electron;
|
||||
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://discord.gg/electronjs');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://github.com/electron/electron/issues');
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
|
||||
const template: Electron.MenuItemConstructorOptions[] = [
|
||||
...(isMac ? [macAppMenu] : []),
|
||||
{ role: 'fileMenu' },
|
||||
{ role: 'editMenu' },
|
||||
{ role: 'viewMenu' },
|
||||
{ role: 'windowMenu' },
|
||||
helpMenu
|
||||
{ role: 'windowMenu' }
|
||||
];
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/stream-json": "^1.7.8",
|
||||
"@types/temp": "^0.9.4",
|
||||
"@xmldom/xmldom": "^0.8.11",
|
||||
"@xmldom/xmldom": "^0.8.12",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
|
||||
@@ -125,7 +125,6 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
|
||||
fix_win32_synchronous_spellcheck.patch
|
||||
chore_grandfather_in_electron_views_and_delegates.patch
|
||||
refactor_patch_electron_permissiontypes_into_blink.patch
|
||||
revert_views_remove_desktopwindowtreehostwin_window_enlargement.patch
|
||||
fix_add_macos_memory_query_fallback_to_avoid_crash.patch
|
||||
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
|
||||
feat_add_support_for_embedder_snapshot_validation.patch
|
||||
@@ -151,4 +150,3 @@ fix_pulseaudio_stream_and_icon_names.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
chore_register_node_as_a_dynamic_trace_category_prefix.patch
|
||||
|
||||
@@ -8,10 +8,10 @@ electron objects that extend gin::Wrappable and gets
|
||||
allocated on the cpp heap
|
||||
|
||||
diff --git a/gin/public/wrappable_pointer_tags.h b/gin/public/wrappable_pointer_tags.h
|
||||
index fee622ebde42211de6f702b754cfa38595df5a1c..6b524632ebb405e473cf4fe8e253bd13bf7b67e5 100644
|
||||
index fee622ebde42211de6f702b754cfa38595df5a1c..9f7e1b1b8d871721891255c1f21de825d0df1e30 100644
|
||||
--- a/gin/public/wrappable_pointer_tags.h
|
||||
+++ b/gin/public/wrappable_pointer_tags.h
|
||||
@@ -77,7 +77,20 @@ enum WrappablePointerTag : uint16_t {
|
||||
@@ -77,7 +77,21 @@ enum WrappablePointerTag : uint16_t {
|
||||
kWebAXObjectProxy, // content::WebAXObjectProxy
|
||||
kWrappedExceptionHandler, // extensions::WrappedExceptionHandler
|
||||
kIndigoContext, // indigo::IndigoContext
|
||||
@@ -24,6 +24,7 @@ index fee622ebde42211de6f702b754cfa38595df5a1c..6b524632ebb405e473cf4fe8e253bd13
|
||||
+ kElectronNetLog, // electron::api::NetLog
|
||||
+ kElectronPowerMonitor, // electron::api::PowerMonitor
|
||||
+ kElectronPowerSaveBlocker, // electron::api::PowerSaveBlocker
|
||||
+ kElectronProtocol, // electron::api::Protocol
|
||||
+ kElectronReplyChannel, // gin_helper::internal::ReplyChannel
|
||||
+ kElectronScreen, // electron::api::Screen
|
||||
+ kElectronSession, // electron::api::Session
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Tue, 31 Mar 2026 09:03:39 +0900
|
||||
Subject: chore: register node as a dynamic trace category prefix
|
||||
|
||||
This allows Node.js trace categories to be treated as dynamic Perfetto
|
||||
categories in the Chromium build. Without this, the categories
|
||||
must be registered in the static registry base/trace_event/builtin_categories.h
|
||||
which is backed by constexpr function ValidateCategories() that
|
||||
recursively validates to a depth of index + longest_category_name_length,
|
||||
adding the node categories exceeds the current constexpr recursion depth
|
||||
of 512 and requires additional patching to add `-fconstexpr-depth` to //base
|
||||
target. Given neither the static nor the dynamic registration can be
|
||||
upstreamed, the minimal of the two changes is chosen here.
|
||||
|
||||
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
|
||||
index 85c6f973788938b6a48a7a89e9fa803dc1030580..ae25a8188d57ff4c15e9a20e91629d585314db87 100644
|
||||
--- a/base/trace_event/builtin_categories.h
|
||||
+++ b/base/trace_event/builtin_categories.h
|
||||
@@ -14,6 +14,7 @@ PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("cat",
|
||||
"foo",
|
||||
"test",
|
||||
"kTest",
|
||||
+ "node",
|
||||
"noise",
|
||||
"Testing",
|
||||
"NotTesting",
|
||||
@@ -8,10 +8,10 @@ such as the background turning black when maximizing the window and
|
||||
dynamic background material settings not taking effect.
|
||||
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad8f5adaa5 100644
|
||||
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..3a5833fcc018f32e86c0a95a42937fb9ac6c5a40 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
@@ -184,6 +184,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
|
||||
@@ -167,6 +167,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ index d1e06b675b19226cf3b78e1aada8d8f2d684fada..ce810555b8501797643987916a728cad
|
||||
|
||||
void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) {
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
index a40bd9f25fa07a553c011cf19f155f8158f4ae5f..ae2baec731b5fcd8be97f2177d23b860d67ab8bc 100644
|
||||
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..b8d1fa863fd05ebc3ab8ac5ef8c4d81361ce45fe 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
@@ -93,6 +93,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
|
||||
|
||||
@@ -620,7 +620,7 @@ index 2a477e820d9f0126a05f86cd44f02c2189275bad..a2e9442ff9f5acf8e301f457b1806251
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
diff --git a/chrome/browser/printing/printer_query_oop.cc b/chrome/browser/printing/printer_query_oop.cc
|
||||
index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..e197026e8a7f132c1bf90a0f5f1eabb4f5f064ee 100644
|
||||
index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..5ca7920c8525c3c72fd96b14709fb35a9cc28daf 100644
|
||||
--- a/chrome/browser/printing/printer_query_oop.cc
|
||||
+++ b/chrome/browser/printing/printer_query_oop.cc
|
||||
@@ -126,7 +126,7 @@ void PrinterQueryOop::OnDidAskUserForSettings(
|
||||
@@ -632,7 +632,7 @@ index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..e197026e8a7f132c1bf90a0f5f1eabb4
|
||||
// Want the same PrintBackend service as the query so that we use the same
|
||||
// device context.
|
||||
print_document_client_id_ =
|
||||
@@ -189,6 +189,21 @@ void PrinterQueryOop::GetSettingsWithUI(uint32_t document_page_count,
|
||||
@@ -189,6 +189,28 @@ void PrinterQueryOop::GetSettingsWithUI(uint32_t document_page_count,
|
||||
// browser process.
|
||||
// - Other platforms don't have a system print UI or do not use OOP
|
||||
// printing, so this does not matter.
|
||||
@@ -643,12 +643,19 @@ index dc2a15ab4d784b0b6c85b84a30c3c08a17ed8e3d..e197026e8a7f132c1bf90a0f5f1eabb4
|
||||
+ // remote service context, not the local one used by the native dialog.
|
||||
+ if (settings().dpi()) {
|
||||
+ printing_context()->SetPrintSettings(settings());
|
||||
+ printing_context()->UpdatePrinterSettings(PrintingContext::PrinterSettings{
|
||||
+ if (printing_context()->UpdatePrinterSettings(
|
||||
+ PrintingContext::PrinterSettings{
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ .external_preview = false,
|
||||
+ .external_preview = false,
|
||||
+#endif
|
||||
+ .show_system_dialog = false,
|
||||
+ });
|
||||
+ .show_system_dialog = false,
|
||||
+ }) != mojom::ResultCode::kSuccess) {
|
||||
+ // Prefilling failed (e.g. the printer does not support the requested
|
||||
+ // resolution). Reinitialize with defaults so that AskUserForSettings
|
||||
+ // does not crash due to a null print_info_. The dialog will simply
|
||||
+ // open without prefilled values.
|
||||
+ printing_context()->UseDefaultSettings();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
PrinterQuery::GetSettingsWithUI(
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: John Kleinschmidt <jkleinsc@electronjs.org>
|
||||
Date: Sat, 14 Jun 2025 16:21:07 -0400
|
||||
Subject: Revert "[views] Remove DesktopWindowTreeHostWin::window_enlargement_"
|
||||
|
||||
This reverts commit 1771dbae6961e7bb7c22bbc6c77f84d90ef2be46.
|
||||
|
||||
Electron needs this patch to allow windows smaller than 64x64
|
||||
on Windows. We should refactor our code so that this patch isn't
|
||||
necessary.
|
||||
|
||||
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
|
||||
index 988866d79a5d1dbd366ebdbff0e8eb2c0c498168..5761ac48be0a64618be0a94606149dd944e46e27 100644
|
||||
--- a/testing/variations/fieldtrial_testing_config.json
|
||||
+++ b/testing/variations/fieldtrial_testing_config.json
|
||||
@@ -22626,6 +22626,21 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
+ "TransparentHwndEnlargement": [
|
||||
+ {
|
||||
+ "platforms": [
|
||||
+ "windows"
|
||||
+ ],
|
||||
+ "experiments": [
|
||||
+ {
|
||||
+ "name": "DisableTransparentHwndEnlargement",
|
||||
+ "disable_features": [
|
||||
+ "EnableTransparentHwndEnlargement"
|
||||
+ ]
|
||||
+ }
|
||||
+ ]
|
||||
+ }
|
||||
+ ],
|
||||
"TransportSecurityFileWriterScheduleAndroid": [
|
||||
{
|
||||
"platforms": [
|
||||
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc
|
||||
index 47077e16870889ef8f8c8b2adf58015bd5aff7fa..ba59e6e1609e61579bf49aca095490b083d72051 100644
|
||||
--- a/ui/views/views_features.cc
|
||||
+++ b/ui/views/views_features.cc
|
||||
@@ -30,6 +30,14 @@ BASE_FEATURE(kEnableInputProtection, base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
// crbug.com/370856871.
|
||||
BASE_FEATURE(kEnableTouchDragCursorSync, base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
|
||||
+// Enables enlargement of HWNDs to a minimum size of 64x64 to handle reported
|
||||
+// graphical glitches on certain hardware.
|
||||
+// TODO(crbug.com/401996981): Remove this once enlargement is confirmed to no
|
||||
+// longer be needed.
|
||||
+BASE_FEATURE(kEnableTransparentHwndEnlargement,
|
||||
+ "EnableTransparentHwndEnlargement",
|
||||
+ base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
+
|
||||
// Used to enable keyboard-accessible tooltips in Views UI, as opposed
|
||||
// to kKeyboardAccessibleTooltip in //ui/base/ui_base_features.cc.
|
||||
BASE_FEATURE(kKeyboardAccessibleTooltipInViews,
|
||||
diff --git a/ui/views/views_features.h b/ui/views/views_features.h
|
||||
index 888a16fb6213eceb131ae636dc643d7f2d5bcad9..6077412165081cd4abeaf0b061feb2f795ee8131 100644
|
||||
--- a/ui/views/views_features.h
|
||||
+++ b/ui/views/views_features.h
|
||||
@@ -15,6 +15,7 @@ namespace views::features {
|
||||
VIEWS_EXPORT BASE_DECLARE_FEATURE(kApplyInitialUrlToWebContents);
|
||||
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableInputProtection);
|
||||
VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTouchDragCursorSync);
|
||||
+VIEWS_EXPORT BASE_DECLARE_FEATURE(kEnableTransparentHwndEnlargement);
|
||||
VIEWS_EXPORT BASE_DECLARE_FEATURE(kKeyboardAccessibleTooltipInViews);
|
||||
|
||||
} // namespace views::features
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
index e4da40256ce94d6a0896792a8ef2faa18e1fa5d2..d1e06b675b19226cf3b78e1aada8d8f2d684fada 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
@@ -85,6 +85,23 @@ namespace {
|
||||
// This constant controls how many pixels wide that border is.
|
||||
const int kMouseCaptureRegionBorder = 5;
|
||||
|
||||
+gfx::Size GetExpandedWindowSize(bool is_translucent, gfx::Size size) {
|
||||
+ if (!base::FeatureList::IsEnabled(
|
||||
+ features::kEnableTransparentHwndEnlargement) ||
|
||||
+ !is_translucent) {
|
||||
+ return size;
|
||||
+ }
|
||||
+
|
||||
+ // Some AMD drivers can't display windows that are less than 64x64 pixels,
|
||||
+ // so expand them to be at least that size. http://crbug.com/286609
|
||||
+ gfx::Size expanded(std::max(size.width(), 64), std::max(size.height(), 64));
|
||||
+ return expanded;
|
||||
+}
|
||||
+
|
||||
+void InsetBottomRight(gfx::Rect* rect, const gfx::Vector2d& vector) {
|
||||
+ rect->Inset(gfx::Insets::TLBR(0, 0, vector.y(), vector.x()));
|
||||
+}
|
||||
+
|
||||
// Updates the cursor clip region. Used for mouse locking.
|
||||
void UpdateMouseLockRegion(aura::Window* window, bool locked) {
|
||||
if (!locked) {
|
||||
@@ -342,9 +359,14 @@ bool DesktopWindowTreeHostWin::IsVisible() const {
|
||||
}
|
||||
|
||||
void DesktopWindowTreeHostWin::SetSize(const gfx::Size& size) {
|
||||
- const gfx::Size size_in_pixels =
|
||||
+ gfx::Size size_in_pixels =
|
||||
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
|
||||
- message_handler_->SetSize(size_in_pixels);
|
||||
+ gfx::Size expanded =
|
||||
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
|
||||
+ window_enlargement_ =
|
||||
+ gfx::Vector2d(expanded.width() - size_in_pixels.width(),
|
||||
+ expanded.height() - size_in_pixels.height());
|
||||
+ message_handler_->SetSize(expanded);
|
||||
}
|
||||
|
||||
void DesktopWindowTreeHostWin::StackAbove(aura::Window* window) {
|
||||
@@ -359,30 +381,40 @@ void DesktopWindowTreeHostWin::StackAtTop() {
|
||||
}
|
||||
|
||||
void DesktopWindowTreeHostWin::CenterWindow(const gfx::Size& size) {
|
||||
- const gfx::Size size_in_pixels =
|
||||
+ gfx::Size size_in_pixels =
|
||||
display::win::GetScreenWin()->DIPToScreenSize(GetHWND(), size);
|
||||
- message_handler_->CenterWindow(size_in_pixels);
|
||||
+ gfx::Size expanded_size;
|
||||
+ expanded_size =
|
||||
+ GetExpandedWindowSize(message_handler_->is_translucent(), size_in_pixels);
|
||||
+ window_enlargement_ =
|
||||
+ gfx::Vector2d(expanded_size.width() - size_in_pixels.width(),
|
||||
+ expanded_size.height() - size_in_pixels.height());
|
||||
+ message_handler_->CenterWindow(expanded_size);
|
||||
}
|
||||
|
||||
void DesktopWindowTreeHostWin::GetWindowPlacement(
|
||||
gfx::Rect* bounds,
|
||||
ui::mojom::WindowShowState* show_state) const {
|
||||
message_handler_->GetWindowPlacement(bounds, show_state);
|
||||
+ InsetBottomRight(bounds, window_enlargement_);
|
||||
*bounds = display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), *bounds);
|
||||
}
|
||||
|
||||
gfx::Rect DesktopWindowTreeHostWin::GetWindowBoundsInScreen() const {
|
||||
gfx::Rect pixel_bounds = message_handler_->GetWindowBoundsInScreen();
|
||||
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
|
||||
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
|
||||
}
|
||||
|
||||
gfx::Rect DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() const {
|
||||
gfx::Rect pixel_bounds = message_handler_->GetClientAreaBoundsInScreen();
|
||||
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
|
||||
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
|
||||
}
|
||||
|
||||
gfx::Rect DesktopWindowTreeHostWin::GetRestoredBounds() const {
|
||||
gfx::Rect pixel_bounds = message_handler_->GetRestoredBounds();
|
||||
+ InsetBottomRight(&pixel_bounds, window_enlargement_);
|
||||
return display::win::GetScreenWin()->ScreenToDIPRect(GetHWND(), pixel_bounds);
|
||||
}
|
||||
|
||||
@@ -701,37 +733,44 @@ void DesktopWindowTreeHostWin::HideImpl() {
|
||||
// other get/set methods work in DIP.
|
||||
|
||||
gfx::Rect DesktopWindowTreeHostWin::GetBoundsInPixels() const {
|
||||
- const gfx::Rect bounds_px(message_handler_->GetClientAreaBounds());
|
||||
+ gfx::Rect bounds(message_handler_->GetClientAreaBounds());
|
||||
// If the window bounds were expanded we need to return the original bounds
|
||||
// To achieve this we do the reverse of the expansion, i.e. add the
|
||||
// window_expansion_top_left_delta_ to the origin and subtract the
|
||||
// window_expansion_bottom_right_delta_ from the width and height.
|
||||
- const gfx::Rect without_expansion_bounds_px(
|
||||
- bounds_px.x() + window_expansion_top_left_delta_.x(),
|
||||
- bounds_px.y() + window_expansion_top_left_delta_.y(),
|
||||
- bounds_px.width() - window_expansion_bottom_right_delta_.x(),
|
||||
- bounds_px.height() - window_expansion_bottom_right_delta_.y());
|
||||
- return without_expansion_bounds_px;
|
||||
+ gfx::Rect without_expansion(
|
||||
+ bounds.x() + window_expansion_top_left_delta_.x(),
|
||||
+ bounds.y() + window_expansion_top_left_delta_.y(),
|
||||
+ bounds.width() - window_expansion_bottom_right_delta_.x() -
|
||||
+ window_enlargement_.x(),
|
||||
+ bounds.height() - window_expansion_bottom_right_delta_.y() -
|
||||
+ window_enlargement_.y());
|
||||
+ return without_expansion;
|
||||
}
|
||||
|
||||
-void DesktopWindowTreeHostWin::SetBoundsInPixels(
|
||||
- const gfx::Rect& bounds_in_pixels) {
|
||||
+void DesktopWindowTreeHostWin::SetBoundsInPixels(const gfx::Rect& bounds) {
|
||||
// If the window bounds have to be expanded we need to subtract the
|
||||
// window_expansion_top_left_delta_ from the origin and add the
|
||||
// window_expansion_bottom_right_delta_ to the width and height
|
||||
- const gfx::Size old_content_size_px = GetBoundsInPixels().size();
|
||||
-
|
||||
- const gfx::Rect expanded_bounds_px(
|
||||
- bounds_in_pixels.x() - window_expansion_top_left_delta_.x(),
|
||||
- bounds_in_pixels.y() - window_expansion_top_left_delta_.y(),
|
||||
- bounds_in_pixels.width() + window_expansion_bottom_right_delta_.x(),
|
||||
- bounds_in_pixels.height() + window_expansion_bottom_right_delta_.y());
|
||||
-
|
||||
- // When `expanded_bounds_px` causes the window to be moved to a display with a
|
||||
+ gfx::Size old_content_size = GetBoundsInPixels().size();
|
||||
+
|
||||
+ gfx::Rect expanded(
|
||||
+ bounds.x() - window_expansion_top_left_delta_.x(),
|
||||
+ bounds.y() - window_expansion_top_left_delta_.y(),
|
||||
+ bounds.width() + window_expansion_bottom_right_delta_.x(),
|
||||
+ bounds.height() + window_expansion_bottom_right_delta_.y());
|
||||
+
|
||||
+ gfx::Rect new_expanded(
|
||||
+ expanded.origin(),
|
||||
+ GetExpandedWindowSize(message_handler_->is_translucent(),
|
||||
+ expanded.size()));
|
||||
+ window_enlargement_ =
|
||||
+ gfx::Vector2d(new_expanded.width() - expanded.width(),
|
||||
+ new_expanded.height() - expanded.height());
|
||||
+ // When |new_expanded| causes the window to be moved to a display with a
|
||||
// different DSF, HWNDMessageHandler::OnDpiChanged() will be called and the
|
||||
// window size will be scaled automatically.
|
||||
- message_handler_->SetBounds(expanded_bounds_px,
|
||||
- old_content_size_px != bounds_in_pixels.size());
|
||||
+ message_handler_->SetBounds(new_expanded, old_content_size != bounds.size());
|
||||
}
|
||||
|
||||
gfx::Rect
|
||||
@@ -943,18 +982,26 @@ int DesktopWindowTreeHostWin::GetNonClientComponent(
|
||||
|
||||
void DesktopWindowTreeHostWin::GetWindowMask(const gfx::Size& size_px,
|
||||
SkPath* path) {
|
||||
- Widget* widget = GetWidget();
|
||||
- if (!widget || !widget->non_client_view()) {
|
||||
- return;
|
||||
- }
|
||||
+ // Request the window mask for hwnd of `size_px`. The hwnd size must be
|
||||
+ // adjusted by `window_enlargement` to return to the client-expected window
|
||||
+ // size (see crbug.com/41047830).
|
||||
+ const gfx::Size adjusted_size_in_px =
|
||||
+ size_px - gfx::Size(window_enlargement_.x(), window_enlargement_.y());
|
||||
|
||||
- widget->non_client_view()->GetWindowMask(
|
||||
- display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(), size_px), path);
|
||||
- // Convert path in DIPs to pixels.
|
||||
- if (!path->isEmpty()) {
|
||||
- const float scale =
|
||||
- display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
|
||||
- *path = path->makeTransform(SkMatrix::Scale(scale, scale));
|
||||
+ if (Widget* widget = GetWidget(); widget && widget->non_client_view()) {
|
||||
+ widget->non_client_view()->GetWindowMask(
|
||||
+ display::win::GetScreenWin()->ScreenToDIPSize(GetHWND(),
|
||||
+ adjusted_size_in_px),
|
||||
+ path);
|
||||
+ // Convert path in DIPs to pixels.
|
||||
+ if (!path->isEmpty()) {
|
||||
+ const float scale =
|
||||
+ display::win::GetScreenWin()->GetScaleFactorForHWND(GetHWND());
|
||||
+ *path = path->makeTransform(SkMatrix::Scale(scale, scale));
|
||||
+ }
|
||||
+ } else if (!window_enlargement_.IsZero()) {
|
||||
+ *path = SkPath::Rect(SkRect::MakeXYWH(0, 0, adjusted_size_in_px.width(),
|
||||
+ adjusted_size_in_px.height()));
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
index 27322ef34edf3fa8bfbd20b1baddcaf3b7555618..a40bd9f25fa07a553c011cf19f155f8158f4ae5f 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
@@ -175,7 +175,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
|
||||
void ShowImpl() override;
|
||||
void HideImpl() override;
|
||||
gfx::Rect GetBoundsInPixels() const override;
|
||||
- void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) override;
|
||||
+ void SetBoundsInPixels(const gfx::Rect& bounds) override;
|
||||
gfx::Rect GetBoundsInAcceleratedWidgetPixelCoordinates() override;
|
||||
gfx::Point GetLocationOnScreenInPixels() const override;
|
||||
void SetCapture() override;
|
||||
@@ -330,6 +330,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
|
||||
gfx::Vector2d window_expansion_top_left_delta_;
|
||||
gfx::Vector2d window_expansion_bottom_right_delta_;
|
||||
|
||||
+ // Windows are enlarged to be at least 64x64 pixels, so keep track of the
|
||||
+ // extra added here.
|
||||
+ // TODO(crbug.com/401996981): This is likely no longer necessary and should be
|
||||
+ // removed.
|
||||
+ gfx::Vector2d window_enlargement_;
|
||||
+
|
||||
// Whether the window close should be converted to a hide, and then actually
|
||||
// closed on the completion of the hide animation. This is cached because
|
||||
// the property is set on the contained window which has a shorter lifetime.
|
||||
@@ -133,10 +133,10 @@ index 6fe4f0492dc1f3eaf576c8ff7866080a54cb81c1..41e8e052ff81df78ece87163b0499966
|
||||
// Recreate the buffer in the constructor.
|
||||
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index 57a46c8be2e052b298ed841eed6f291d62711750..e4ffaa465a4ffe21334496c52334fcb1404f67a9 100644
|
||||
index b5cf58cc953590493beb52abf249e33e486ffc46..347ec5c42e098186ff489dff199ac5989961f6e3 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -1764,10 +1764,10 @@ void AsyncHooks::Deserialize(Local<Context> context) {
|
||||
@@ -1765,10 +1765,10 @@ void AsyncHooks::Deserialize(Local<Context> context) {
|
||||
context->GetDataFromSnapshotOnce<Array>(
|
||||
info_->js_execution_async_resources).ToLocalChecked();
|
||||
} else {
|
||||
@@ -149,7 +149,7 @@ index 57a46c8be2e052b298ed841eed6f291d62711750..e4ffaa465a4ffe21334496c52334fcb1
|
||||
|
||||
// The native_execution_async_resources_ field requires v8::Local<> instances
|
||||
// for async calls whose resources were on the stack as JS objects when they
|
||||
@@ -1807,7 +1807,7 @@ AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
|
||||
@@ -1808,7 +1808,7 @@ AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
|
||||
info.async_id_fields = async_id_fields_.Serialize(context, creator);
|
||||
if (!js_execution_async_resources_.IsEmpty()) {
|
||||
info.js_execution_async_resources = creator->AddData(
|
||||
@@ -458,7 +458,7 @@ index fea0426496978c0003fe1481afcf93fc9c23edca..c9588880d05435ab9f4e23fcff74c933
|
||||
|
||||
CHECK(
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index 986a2d8da7fd04b5d4060d9c8d44c61a231dcce6..9f11d32c70366524cf3b7c1cfdfd24f31e438e7b 100644
|
||||
index 3c234205e89be7e976dae5c3fcc73ca67953e034..e66d4fcb0c064f96cdb819c783027d864fe88d12 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -113,7 +113,7 @@ namespace {
|
||||
@@ -479,7 +479,7 @@ index 986a2d8da7fd04b5d4060d9c8d44c61a231dcce6..9f11d32c70366524cf3b7c1cfdfd24f3
|
||||
|
||||
PropertyAttribute attributes = PropertyAttribute::None;
|
||||
bool is_declared =
|
||||
@@ -1665,7 +1665,7 @@ static MaybeLocal<Function> CompileFunctionForCJSLoader(
|
||||
@@ -1666,7 +1666,7 @@ static MaybeLocal<Function> CompileFunctionForCJSLoader(
|
||||
bool* cache_rejected,
|
||||
bool is_cjs_scope,
|
||||
ScriptCompiler::CachedData* cached_data) {
|
||||
@@ -533,10 +533,10 @@ index 55a0c986c5b6989ee9ce277bb6a9778abb2ad2ee..809d88f21e5572807e38132d40ee7587
|
||||
READONLY_PROPERTY(target, "exitCodes", exit_codes);
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index c7a9648b0f83e910190dc620f4b72577ffde6c44..46cd16b535d9bd651ef733ca52ea58db7d39b09f 100644
|
||||
index 96aac2d86695732bf6805f2ad2168a62241b5045..547455bb5011677719a8de1f98cb447561bce6aa 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3857,7 +3857,7 @@ void BindingData::Deserialize(Local<Context> context,
|
||||
@@ -3850,7 +3850,7 @@ void BindingData::Deserialize(Local<Context> context,
|
||||
int index,
|
||||
InternalFieldInfoBase* info) {
|
||||
DCHECK_IS_SNAPSHOT_SLOT(index);
|
||||
|
||||
@@ -14,10 +14,10 @@ We don't need to do this for zlib, as the existing gn workflow uses the same
|
||||
Upstreamed at https://github.com/nodejs/node/pull/55903
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index a773152813376bef1fa227c331241a1d944c9317..43f09d1e68c88d3ba3b862a1a74769f73c370894 100644
|
||||
index bff7b0650cfe8578a044e45d0f9e352859909695..4ab316e45bd84e43a53335df60f847b17fe6c2fa 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -203,7 +203,17 @@ template("node_gn_build") {
|
||||
@@ -199,7 +199,17 @@ template("node_gn_build") {
|
||||
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
|
||||
configs += [ "//build/config/gcc:symbol_visibility_default" ]
|
||||
}
|
||||
@@ -36,7 +36,7 @@ index a773152813376bef1fa227c331241a1d944c9317..43f09d1e68c88d3ba3b862a1a74769f7
|
||||
if (v8_enable_i18n_support) {
|
||||
deps += [ "//third_party/icu" ]
|
||||
}
|
||||
@@ -236,6 +246,19 @@ template("node_gn_build") {
|
||||
@@ -232,6 +242,19 @@ template("node_gn_build") {
|
||||
sources += node_inspector.node_inspector_sources +
|
||||
node_inspector.node_inspector_generated_sources
|
||||
}
|
||||
|
||||
@@ -3,14 +3,20 @@ 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 and wire track events
|
||||
through legacy shim.
|
||||
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..8061013fcfe3c3b02aabaca0447069423ac853b2 100644
|
||||
index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..a154559a56bf383d3c26af523c9bb07b564ef600 100644
|
||||
--- a/lib/internal/constants.js
|
||||
+++ b/lib/internal/constants.js
|
||||
@@ -5,12 +5,16 @@ const isWindows = process.platform === 'win32';
|
||||
@@ -5,12 +5,15 @@ const isWindows = process.platform === 'win32';
|
||||
module.exports = {
|
||||
// Alphabet chars.
|
||||
CHAR_UPPERCASE_A: 65, /* A */
|
||||
@@ -22,216 +28,61 @@ index 8d7204f6cb48f783adc4d1c1eb2de0c83b7fffe2..8061013fcfe3c3b02aabaca044706942
|
||||
CHAR_LOWERCASE_B: 98, /* b */
|
||||
+ CHAR_UPPERCASE_E: 69, /* E */
|
||||
CHAR_LOWERCASE_E: 101, /* e */
|
||||
+ CHAR_UPPERCASE_I: 73, /* I */
|
||||
+
|
||||
CHAR_LOWERCASE_N: 110, /* n */
|
||||
|
||||
// Non-alphabetic chars.
|
||||
diff --git a/lib/internal/http.js b/lib/internal/http.js
|
||||
index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..0f924a5cc6718415226ffef5f8bc40a51043be04 100644
|
||||
index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..209e3bcf8be5a23ac528dcd673bed82cbad709ca 100644
|
||||
--- a/lib/internal/http.js
|
||||
+++ b/lib/internal/http.js
|
||||
@@ -9,7 +9,7 @@ const {
|
||||
} = primordials;
|
||||
|
||||
@@ -11,8 +11,8 @@ const {
|
||||
const { setUnrefTimeout } = require('internal/timers');
|
||||
-const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
const {
|
||||
CHAR_LOWERCASE_B,
|
||||
CHAR_LOWERCASE_E,
|
||||
@@ -42,13 +42,11 @@ function getNextTraceEventId() {
|
||||
return ++traceEventId;
|
||||
}
|
||||
- CHAR_LOWERCASE_B,
|
||||
- CHAR_LOWERCASE_E,
|
||||
+ CHAR_UPPERCASE_B,
|
||||
+ CHAR_UPPERCASE_E,
|
||||
} = require('internal/constants');
|
||||
|
||||
-const httpEnabled = getCategoryEnabledBuffer('node.http');
|
||||
-
|
||||
function isTraceHTTPEnabled() {
|
||||
- return httpEnabled[0] > 0;
|
||||
+ return isTraceCategoryEnabled('node.http');
|
||||
}
|
||||
|
||||
-const traceEventCategory = 'node,node.http';
|
||||
+const traceEventCategory = 'node.http';
|
||||
const { URL } = require('internal/url');
|
||||
@@ -51,11 +51,13 @@ function isTraceHTTPEnabled() {
|
||||
const traceEventCategory = 'node,node.http';
|
||||
|
||||
function traceBegin(...args) {
|
||||
trace(CHAR_LOWERCASE_B, traceEventCategory, ...args);
|
||||
diff --git a/lib/internal/perf/usertiming.js b/lib/internal/perf/usertiming.js
|
||||
index 88bb63ead2d3d620dea6b54db043a404b484ead9..a37386bbf51b55b26a140cd81fbaf26d78015f21 100644
|
||||
--- a/lib/internal/perf/usertiming.js
|
||||
+++ b/lib/internal/perf/usertiming.js
|
||||
@@ -13,6 +13,12 @@ const { PerformanceEntry, kSkipThrow } = require('internal/perf/performance_entr
|
||||
const { now } = require('internal/perf/utils');
|
||||
const { enqueue, bufferUserTiming } = require('internal/perf/observe');
|
||||
const nodeTiming = require('internal/perf/nodetiming');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
+const {
|
||||
+ CHAR_LOWERCASE_B,
|
||||
+ CHAR_LOWERCASE_E,
|
||||
+ CHAR_UPPERCASE_I
|
||||
+} = require('internal/constants');
|
||||
|
||||
const {
|
||||
validateNumber,
|
||||
@@ -41,6 +47,9 @@ const kDetail = Symbol('kDetail');
|
||||
|
||||
const markTimings = new SafeMap();
|
||||
|
||||
+const traceEventCategory = 'node.perf.usertiming';
|
||||
+let traceEventId = 0;
|
||||
+
|
||||
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
|
||||
'nodeStart',
|
||||
'v8Start',
|
||||
@@ -168,6 +177,10 @@ function mark(name, options) {
|
||||
const mark = new PerformanceMark(name, options);
|
||||
enqueue(mark);
|
||||
bufferUserTiming(mark);
|
||||
+ if (isTraceCategoryEnabled(traceEventCategory)) {
|
||||
+ trace(CHAR_UPPERCASE_I, traceEventCategory, name, undefined,
|
||||
+ { startTime: mark.startTime });
|
||||
+ }
|
||||
return mark;
|
||||
- trace(CHAR_LOWERCASE_B, traceEventCategory, ...args);
|
||||
+ // See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
|
||||
+ trace(CHAR_UPPERCASE_B, traceEventCategory, ...args);
|
||||
}
|
||||
|
||||
@@ -233,6 +246,13 @@ function measure(name, startOrMeasureOptions, endMark) {
|
||||
const measure = createPerformanceMeasure(name, start, duration, detail);
|
||||
enqueue(measure);
|
||||
bufferUserTiming(measure);
|
||||
+ if (isTraceCategoryEnabled(traceEventCategory)) {
|
||||
+ const id = ++traceEventId;
|
||||
+ trace(CHAR_LOWERCASE_B, traceEventCategory, name, id,
|
||||
+ { startTime: start });
|
||||
+ trace(CHAR_LOWERCASE_E, traceEventCategory, name, id,
|
||||
+ { startTime: start, duration });
|
||||
+ }
|
||||
return measure;
|
||||
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);
|
||||
}
|
||||
|
||||
diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js
|
||||
index a9f517ffc9e4eea5bc68997ffadc85d43dde2a52..e85bcd2f500ff3f5bbd2b25922c13cb29de50993 100644
|
||||
--- a/lib/internal/trace_events_async_hooks.js
|
||||
+++ b/lib/internal/trace_events_async_hooks.js
|
||||
@@ -20,7 +20,7 @@ const {
|
||||
// the specific C++ macros.
|
||||
const kBeforeEvent = CHAR_LOWERCASE_B;
|
||||
const kEndEvent = CHAR_LOWERCASE_E;
|
||||
-const kTraceEventCategory = 'node,node.async_hooks';
|
||||
+const kTraceEventCategory = 'node.async_hooks';
|
||||
|
||||
const kEnabled = Symbol('enabled');
|
||||
|
||||
diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js
|
||||
index 06a4f8a239855571dcc67cd81e7da7a255a9ebfd..1fa9e314ad796cdf74f718f0eb2a15530f5833d3 100644
|
||||
--- a/lib/internal/util/debuglog.js
|
||||
+++ b/lib/internal/util/debuglog.js
|
||||
@@ -20,7 +20,7 @@ const {
|
||||
CHAR_LOWERCASE_N: kTraceInstant,
|
||||
} = require('internal/constants');
|
||||
const { inspect, format, formatWithOptions } = require('internal/util/inspect');
|
||||
-const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events');
|
||||
+const { isTraceCategoryEnabled, trace } = internalBinding('trace_events');
|
||||
|
||||
// `debugImpls` and `testEnabled` are deliberately not initialized so any call
|
||||
// to `debuglog()` before `initializeDebugEnv()` is called will throw.
|
||||
@@ -386,14 +386,13 @@ function debugWithTimer(set, cb) {
|
||||
}
|
||||
|
||||
const traceCategory = `node,node.${StringPrototypeToLowerCase(set)}`;
|
||||
- let traceCategoryBuffer;
|
||||
let debugLogCategoryEnabled = false;
|
||||
let timerFlags = kNone;
|
||||
|
||||
function ensureTimerFlagsAreUpdated() {
|
||||
timerFlags &= ~kSkipTrace;
|
||||
|
||||
- if (traceCategoryBuffer[0] === 0) {
|
||||
+ if (!isTraceCategoryEnabled(traceCategory)) {
|
||||
timerFlags |= kSkipTrace;
|
||||
}
|
||||
}
|
||||
@@ -467,7 +466,6 @@ function debugWithTimer(set, cb) {
|
||||
}
|
||||
emitWarningIfNeeded(set);
|
||||
debugLogCategoryEnabled = testEnabled(set);
|
||||
- traceCategoryBuffer = getCategoryEnabledBuffer(traceCategory);
|
||||
|
||||
timerFlags = kNone;
|
||||
|
||||
@@ -475,7 +473,7 @@ function debugWithTimer(set, cb) {
|
||||
timerFlags |= kSkipLog;
|
||||
}
|
||||
|
||||
- if (traceCategoryBuffer[0] === 0) {
|
||||
+ if (!isTraceCategoryEnabled(traceCategory)) {
|
||||
timerFlags |= kSkipTrace;
|
||||
}
|
||||
|
||||
function ipToInt(ip) {
|
||||
diff --git a/node.gyp b/node.gyp
|
||||
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..b7072ce74354495bec49357f962f4ef2999bf727 100644
|
||||
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..5eb70ce3820f2b82121bc102c5182ab768cbef36 100644
|
||||
--- a/node.gyp
|
||||
+++ b/node.gyp
|
||||
@@ -182,9 +182,9 @@
|
||||
@@ -182,7 +182,6 @@
|
||||
'src/timers.cc',
|
||||
'src/timer_wrap.cc',
|
||||
'src/tracing/agent.cc',
|
||||
- 'src/tracing/node_trace_buffer.cc',
|
||||
'src/tracing/node_trace_writer.cc',
|
||||
'src/tracing/trace_event.cc',
|
||||
+ 'src/tracing/trace_categories.cc',
|
||||
'src/tracing/traced_value.cc',
|
||||
'src/tty_wrap.cc',
|
||||
'src/udp_wrap.cc',
|
||||
@@ -314,9 +314,9 @@
|
||||
@@ -314,7 +313,6 @@
|
||||
'src/tcp_wrap.h',
|
||||
'src/timers.h',
|
||||
'src/tracing/agent.h',
|
||||
- 'src/tracing/node_trace_buffer.h',
|
||||
'src/tracing/node_trace_writer.h',
|
||||
'src/tracing/trace_event.h',
|
||||
+ 'src/tracing/trace_categories.h',
|
||||
'src/tracing/trace_event_common.h',
|
||||
'src/tracing/traced_value.h',
|
||||
'src/timer_wrap.h',
|
||||
diff --git a/src/async_wrap.cc b/src/async_wrap.cc
|
||||
index 301f77c419f178c4eea258e0896327f69389dda7..d5068a18392a6128ceee7f0146f8f9c77f9924bb 100644
|
||||
--- a/src/async_wrap.cc
|
||||
+++ b/src/async_wrap.cc
|
||||
@@ -110,8 +110,7 @@ void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) {
|
||||
}
|
||||
|
||||
void AsyncWrap::EmitTraceAsyncStart() const {
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(async_hooks))) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(async_hooks))) {
|
||||
tracing::AsyncWrapArgs data(env()->execution_async_id(),
|
||||
get_trigger_async_id());
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(async_hooks),
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index fdabe48dd7776c59298f7d972286d0d2ed062752..c185d822b29c0b691bbf5f724f71f59638c6184d 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -650,8 +650,8 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
|
||||
return;
|
||||
}
|
||||
|
||||
- bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(async_hooks)))) != 0;
|
||||
+ bool async_hooks_enabled =
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(async_hooks));
|
||||
|
||||
Isolate* isolate = env_->isolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
@@ -893,8 +893,7 @@ Environment::Environment(IsolateData* isolate_data,
|
||||
time_origin_timestamp_,
|
||||
MAYBE_FIELD_PTR(env_info, performance_state));
|
||||
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE1(environment)) != 0) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE1(environment))) {
|
||||
tracing::EnvironmentArgs traced_value(args, exec_args);
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment),
|
||||
"Environment",
|
||||
diff --git a/src/inspector/tracing_agent.cc b/src/inspector/tracing_agent.cc
|
||||
index 40c8aea35c931c46fc62b717c978eab0659645fd..348cdfb0b42aa18f352c220cea0b896c09f67753 100644
|
||||
--- a/src/inspector/tracing_agent.cc
|
||||
@@ -253,156 +104,6 @@ index 40c8aea35c931c46fc62b717c978eab0659645fd..348cdfb0b42aa18f352c220cea0b896c
|
||||
void Flush(bool) override {
|
||||
if (!json_writer_)
|
||||
return;
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index 0bc086ccd1ff449c0f3fb08a972a0c45d3178f1c..ca74e83ef6f7b0e8b8496457af3813f07f52eb37 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -78,6 +78,11 @@
|
||||
|
||||
#include "large_pages/node_large_page.h"
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "perfetto/tracing/tracing.h"
|
||||
+#include "tracing/trace_categories.h"
|
||||
+#endif
|
||||
+
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
|
||||
#define NODE_USE_V8_WASM_TRAP_HANDLER 1
|
||||
#else
|
||||
@@ -1261,6 +1266,14 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore);
|
||||
}
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+ // Register Node's Perfetto TrackEvent data source so that trace
|
||||
+ // categories are available.
|
||||
+ if (perfetto::Tracing::IsInitialized()) {
|
||||
+ node::perfetto_track_event::TrackEvent::Register();
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
#if NODE_USE_V8_WASM_TRAP_HANDLER
|
||||
bool use_wasm_trap_handler =
|
||||
!per_process::cli_options->disable_wasm_trap_handler;
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index 3c234205e89be7e976dae5c3fcc73ca67953e034..986a2d8da7fd04b5d4060d9c8d44c61a231dcce6 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -1026,8 +1026,7 @@ void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
ContextifyScript* contextify_script = New(env, args.This());
|
||||
|
||||
- if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
|
||||
- TRACING_CATEGORY_NODE2(vm, script)) != 0) {
|
||||
+ if (NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(vm, script))) {
|
||||
Utf8Value fn(isolate, filename);
|
||||
TRACE_EVENT_BEGIN1(TRACING_CATEGORY_NODE2(vm, script),
|
||||
"ContextifyScript::New",
|
||||
diff --git a/src/node_dir.cc b/src/node_dir.cc
|
||||
index c9173d404c79a69743fc75ddb6bba0ac9579c1ef..8ffac047a69b3900f37d712334c504a1c65c83fd 100644
|
||||
--- a/src/node_dir.cc
|
||||
+++ b/src/node_dir.cc
|
||||
@@ -61,18 +61,25 @@ static const char* get_dir_func_name_by_type(uv_fs_type req_type) {
|
||||
|
||||
#define TRACE_NAME(name) "fs_dir.sync." #name
|
||||
#define GET_TRACE_ENABLED \
|
||||
- (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
|
||||
- TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(fs_dir, sync))
|
||||
#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), \
|
||||
TRACE_NAME(syscall), \
|
||||
##__VA_ARGS__);
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto's TRACE_EVENT_END does not accept a name; it matches the prior
|
||||
+// TRACE_EVENT_BEGIN on the same thread.
|
||||
+#define FS_DIR_SYNC_TRACE_END(syscall, ...) \
|
||||
+ if (GET_TRACE_ENABLED) \
|
||||
+ TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), ##__VA_ARGS__);
|
||||
+#else
|
||||
#define FS_DIR_SYNC_TRACE_END(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), \
|
||||
TRACE_NAME(syscall), \
|
||||
##__VA_ARGS__);
|
||||
+#endif
|
||||
|
||||
#define FS_DIR_ASYNC_TRACE_BEGIN0(fs_type, id) \
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs_dir, async), \
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 96aac2d86695732bf6805f2ad2168a62241b5045..c7a9648b0f83e910190dc620f4b72577ffde6c44 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -147,16 +147,23 @@ static const char* get_fs_func_name_by_type(uv_fs_type req_type) {
|
||||
|
||||
#define TRACE_NAME(name) "fs.sync." #name
|
||||
#define GET_TRACE_ENABLED \
|
||||
- (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \
|
||||
- TRACING_CATEGORY_NODE2(fs, sync)) != 0)
|
||||
+ NODE_TRACE_CATEGORY_ENABLED(TRACING_CATEGORY_NODE2(fs, sync))
|
||||
#define FS_SYNC_TRACE_BEGIN(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_BEGIN( \
|
||||
TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto's TRACE_EVENT_END does not accept a name; it matches the prior
|
||||
+// TRACE_EVENT_BEGIN on the same thread.
|
||||
+#define FS_SYNC_TRACE_END(syscall, ...) \
|
||||
+ if (GET_TRACE_ENABLED) \
|
||||
+ TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), ##__VA_ARGS__);
|
||||
+#else
|
||||
#define FS_SYNC_TRACE_END(syscall, ...) \
|
||||
if (GET_TRACE_ENABLED) \
|
||||
TRACE_EVENT_END( \
|
||||
TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__);
|
||||
+#endif
|
||||
|
||||
#define FS_ASYNC_TRACE_BEGIN0(fs_type, id) \
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs, async), \
|
||||
diff --git a/src/node_internals.h b/src/node_internals.h
|
||||
index 8e930a6fecd6589b858293d91b2454ea14ae7c73..a95dd02d4149a02ff40c759010e130c89ad1d848 100644
|
||||
--- a/src/node_internals.h
|
||||
+++ b/src/node_internals.h
|
||||
@@ -315,6 +315,14 @@ class ThreadPoolWork {
|
||||
const char* type_;
|
||||
};
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+// Perfetto categories must be single strings (not comma-separated).
|
||||
+#define TRACING_CATEGORY_NODE "node"
|
||||
+#define TRACING_CATEGORY_NODE1(one) "node." #one
|
||||
+#define TRACING_CATEGORY_NODE2(one, two) "node." #one "." #two
|
||||
+#define NODE_TRACE_CATEGORY_ENABLED(category) \
|
||||
+ TRACE_EVENT_CATEGORY_ENABLED(category)
|
||||
+#else
|
||||
#define TRACING_CATEGORY_NODE "node"
|
||||
#define TRACING_CATEGORY_NODE1(one) \
|
||||
TRACING_CATEGORY_NODE "," \
|
||||
@@ -323,6 +331,9 @@ class ThreadPoolWork {
|
||||
TRACING_CATEGORY_NODE "," \
|
||||
TRACING_CATEGORY_NODE "." #one "," \
|
||||
TRACING_CATEGORY_NODE "." #one "." #two
|
||||
+#define NODE_TRACE_CATEGORY_ENABLED(category) \
|
||||
+ (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category) != 0)
|
||||
+#endif
|
||||
|
||||
// Functions defined in node.cc that are exposed via the bootstrapper object
|
||||
|
||||
diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc
|
||||
index 225b1465b7c97d972a38968faf6d685017a80bf0..4a53a07f4d5e79354e647ba3ff6e2e1095a5b684 100644
|
||||
--- a/src/node_trace_events.cc
|
||||
+++ b/src/node_trace_events.cc
|
||||
@@ -127,7 +127,8 @@ static void GetCategoryEnabledBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
node::Utf8Value category_name(isolate, args[0]);
|
||||
|
||||
const uint8_t* enabled_pointer =
|
||||
- TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_name.out());
|
||||
+ tracing::TraceEventHelper::GetCategoryGroupEnabled(
|
||||
+ category_name.out());
|
||||
uint8_t* enabled_pointer_cast = const_cast<uint8_t*>(enabled_pointer);
|
||||
uint8_t size = sizeof(*enabled_pointer_cast);
|
||||
|
||||
diff --git a/src/tracing/agent.cc b/src/tracing/agent.cc
|
||||
index eddcf6c3bf91b730d6ca72960e3048ceed7e7844..184e8647b2148bc597d9d3eb63f86ae99917c642 100644
|
||||
--- a/src/tracing/agent.cc
|
||||
@@ -596,138 +297,48 @@ index cd965d77b7859ff2edcf781a934594b5a9b6d251..fe1714ba77fddef693d37eeb8c7a196d
|
||||
void Flush(bool blocking) override;
|
||||
|
||||
static const int kTracesPerFile = 1 << 19;
|
||||
diff --git a/src/tracing/trace_categories.cc b/src/tracing/trace_categories.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4abc4dc5d9ef9c2a9b2e3d85d858f4bbf5ac6432
|
||||
--- /dev/null
|
||||
+++ b/src/tracing/trace_categories.cc
|
||||
@@ -0,0 +1,5 @@
|
||||
+#include "tracing/trace_categories.h"
|
||||
+
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(node);
|
||||
+#endif
|
||||
diff --git a/src/tracing/trace_categories.h b/src/tracing/trace_categories.h
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b28d4baa7bf766301c9281b80e0d29729ef9832e
|
||||
--- /dev/null
|
||||
+++ b/src/tracing/trace_categories.h
|
||||
@@ -0,0 +1,66 @@
|
||||
+#ifndef SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
+#define SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
+
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+
|
||||
+#ifdef BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
|
||||
+// Compiling mode where Chromium's Perfetto TrackEvent
|
||||
+// is already set up (via PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(base)).
|
||||
+// Node trace categories (node.perf, node.async_hooks, etc.) will be treated
|
||||
+// as dynamic categories.
|
||||
+#else
|
||||
+// Set up Node's own Perfetto TrackEvent data source with its trace categories,
|
||||
+// following the same pattern V8 uses (PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE).
|
||||
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1
|
||||
+
|
||||
+#include "perfetto/tracing/track_event.h"
|
||||
+#include "perfetto/tracing/track_event_legacy.h"
|
||||
+
|
||||
+// Register Node.js trace categories as static Perfetto categories in the
|
||||
+// 'node' namespace.
|
||||
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(
|
||||
+ node,
|
||||
+ perfetto::Category("__metadata"),
|
||||
+ perfetto::Category("node"),
|
||||
+ perfetto::Category("node.perf"),
|
||||
+ perfetto::Category("node.perf.timerify"),
|
||||
+ perfetto::Category("node.perf.usertiming"),
|
||||
+ perfetto::Category("node.perf.event_loop"),
|
||||
+ perfetto::Category("node.async_hooks"),
|
||||
+ perfetto::Category("node.bootstrap"),
|
||||
+ perfetto::Category("node.dns.native"),
|
||||
+ perfetto::Category("node.environment"),
|
||||
+ perfetto::Category("node.fs.async"),
|
||||
+ perfetto::Category("node.fs.sync"),
|
||||
+ perfetto::Category("node.fs_dir.async"),
|
||||
+ perfetto::Category("node.fs_dir.sync"),
|
||||
+ perfetto::Category("node.http"),
|
||||
+ perfetto::Category("node.net.native"),
|
||||
+ perfetto::Category("node.promises.rejections"),
|
||||
+ perfetto::Category("node.realm"),
|
||||
+ perfetto::Category("node.threadpoolwork.async"),
|
||||
+ perfetto::Category("node.threadpoolwork.sync"),
|
||||
+ perfetto::Category("node.vm.script"),
|
||||
+ perfetto::Category("v8"));
|
||||
+
|
||||
+// Make Node's categories available through the default TrackEvent namespace
|
||||
+// so that TRACE_EVENT macros work without qualification.
|
||||
+PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(node);
|
||||
+
|
||||
+// These deprecated phase constants are not defined by Perfetto's legacy shim
|
||||
+// but are still exported to JavaScript by node_constants.cc.
|
||||
+#ifndef TRACE_EVENT_PHASE_ENTER_CONTEXT
|
||||
+#define TRACE_EVENT_PHASE_ENTER_CONTEXT ('(')
|
||||
+#endif
|
||||
+#ifndef TRACE_EVENT_PHASE_LEAVE_CONTEXT
|
||||
+#define TRACE_EVENT_PHASE_LEAVE_CONTEXT (')')
|
||||
+#endif
|
||||
+#ifndef TRACE_EVENT_PHASE_LINK_IDS
|
||||
+#define TRACE_EVENT_PHASE_LINK_IDS ('=')
|
||||
+#endif
|
||||
+
|
||||
+#endif // BASE_TRACE_EVENT_BUILTIN_CATEGORIES_H_
|
||||
+
|
||||
+#endif // defined(V8_USE_PERFETTO)
|
||||
+
|
||||
+#endif // SRC_TRACING_TRACE_CATEGORIES_H_
|
||||
diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h
|
||||
index a662a081dc3bf356bf93e4063fcb043e4d8df07b..a7d0363e15a260feaaa5c7826a3b3137be531934 100644
|
||||
index a662a081dc3bf356bf93e4063fcb043e4d8df07b..c89cdfe2b2681fbf9946200a03d7d1f7bad21226 100644
|
||||
--- a/src/tracing/trace_event.h
|
||||
+++ b/src/tracing/trace_event.h
|
||||
@@ -7,13 +7,23 @@
|
||||
|
||||
#include "v8-platform.h"
|
||||
#include "tracing/agent.h"
|
||||
-#include "trace_event_common.h"
|
||||
#include <atomic>
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "tracing/trace_categories.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
|
||||
+#include "trace_event_common.h"
|
||||
+#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
|
||||
+
|
||||
// This header file defines implementation details of how the trace macros in
|
||||
// trace_event_common.h collect and store trace events. Anything not
|
||||
// implementation-specific should go in trace_macros_common.h instead of here.
|
||||
|
||||
+#if !defined(V8_USE_PERFETTO)
|
||||
+// When Perfetto is enabled, all trace event macros and their internal
|
||||
+// implementation are provided by Perfetto's track event legacy shim
|
||||
+// (included via trace_categories.h). The following definitions are only
|
||||
+// needed for the non-Perfetto tracing backend.
|
||||
// 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
|
||||
|
||||
// The pointer returned from GetCategoryGroupEnabled() points to a
|
||||
// value with zero or more of the following bits. Used in this class only.
|
||||
@@ -301,6 +311,8 @@ enum CategoryGroupEnabledFlags {
|
||||
INTERNAL_TRACE_EVENT_UID(ScopedContext) \
|
||||
INTERNAL_TRACE_EVENT_UID(scoped_context)(context);
|
||||
|
||||
+#endif // !defined(V8_USE_PERFETTO)
|
||||
+
|
||||
namespace node {
|
||||
namespace tracing {
|
||||
|
||||
@@ -319,15 +331,24 @@ class TraceEventHelper {
|
||||
// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method
|
||||
// on the convertable value will be called at flush time.
|
||||
@@ -319,12 +332,15 @@ class TraceEventHelper {
|
||||
static void SetAgent(Agent* agent);
|
||||
|
||||
static inline const uint8_t* GetCategoryGroupEnabled(const char* group) {
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+ // Under Perfetto, callers should use TRACE_EVENT_CATEGORY_ENABLED()
|
||||
+ // instead. This function exists only for backward compatibility with
|
||||
+ // non-Perfetto builds.
|
||||
+ static const uint8_t disabled = 0;
|
||||
+ return &disabled;
|
||||
+#else
|
||||
+#ifndef V8_USE_PERFETTO
|
||||
v8::TracingController* controller = GetTracingController();
|
||||
static const uint8_t disabled = 0;
|
||||
if (controller == nullptr) [[unlikely]] {
|
||||
@@ -735,90 +346,56 @@ index a662a081dc3bf356bf93e4063fcb043e4d8df07b..a7d0363e15a260feaaa5c7826a3b3137
|
||||
}
|
||||
return controller->GetCategoryGroupEnabled(group);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
};
|
||||
|
||||
+#if !defined(V8_USE_PERFETTO)
|
||||
// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
|
||||
// are by default mangled with the Process ID so that they are unlikely to
|
||||
// collide when the same pointer is used on different processes.
|
||||
@@ -478,6 +499,7 @@ static inline uint64_t AddTraceEventImpl(
|
||||
@@ -462,6 +478,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*>(
|
||||
@@ -478,6 +495,8 @@ static inline uint64_t AddTraceEventImpl(
|
||||
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(
|
||||
@@ -501,6 +523,7 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
@@ -485,6 +504,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_convertibles[2];
|
||||
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
|
||||
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
|
||||
@@ -501,12 +521,15 @@ static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
|
||||
return controller->AddTraceEventWithTimestamp(
|
||||
phase, category_group_enabled, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, arg_convertibles, flags, timestamp);
|
||||
+#endif
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static V8_INLINE void AddMetadataEventImpl(
|
||||
@@ -716,6 +739,8 @@ class ScopedTracer {
|
||||
Data data_;
|
||||
};
|
||||
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*>(
|
||||
@@ -522,6 +545,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
|
||||
}
|
||||
|
||||
+#endif // !defined(V8_USE_PERFETTO)
|
||||
+
|
||||
} // namespace tracing
|
||||
} // namespace node
|
||||
|
||||
diff --git a/src/tracing/traced_value.h b/src/tracing/traced_value.h
|
||||
index 0bc9df81d87562243817a6618641a49b602654e3..b6dd8b9a9c21051f3d385d5ecea9c50c8b8b1629 100644
|
||||
--- a/src/tracing/traced_value.h
|
||||
+++ b/src/tracing/traced_value.h
|
||||
@@ -11,6 +11,26 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
+#if defined(V8_USE_PERFETTO)
|
||||
+#include "perfetto/tracing/traced_value.h"
|
||||
+
|
||||
+namespace perfetto {
|
||||
+
|
||||
+template <>
|
||||
+struct TraceFormatTraits<
|
||||
+ std::unique_ptr<v8::ConvertableToTraceFormat>> {
|
||||
+ static void WriteIntoTrace(
|
||||
+ TracedValue context,
|
||||
+ const std::unique_ptr<v8::ConvertableToTraceFormat>& value) {
|
||||
+ std::string json;
|
||||
+ value->AppendAsTraceFormat(&json);
|
||||
+ std::move(context).WriteString(json);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+} // namespace perfetto
|
||||
+#endif // defined(V8_USE_PERFETTO)
|
||||
+
|
||||
namespace node {
|
||||
namespace tracing {
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index bff7b0650cfe8578a044e45d0f9e352859909695..a773152813376bef1fa227c331241a1d944c9317 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -143,7 +143,10 @@ template("node_gn_build") {
|
||||
[ "node.gyp" ])
|
||||
|
||||
source_set("libnode") {
|
||||
- configs += [ ":node_internal_config" ]
|
||||
+ configs += [
|
||||
+ ":node_internal_config",
|
||||
+ "$node_v8_path:v8_tracing_config",
|
||||
+ ]
|
||||
public_configs = [
|
||||
":node_external_config",
|
||||
"deps/googletest:googletest_config",
|
||||
@@ -173,6 +176,7 @@ template("node_gn_build") {
|
||||
"//third_party/zstd:headers",
|
||||
"$node_simdutf_path",
|
||||
"$node_v8_path:v8_libplatform",
|
||||
+ "$node_v8_path:v8_tracing",
|
||||
]
|
||||
|
||||
cflags_cc = [
|
||||
// Define SetTraceValue for each allowed type. It stores the type and
|
||||
|
||||
@@ -20,18 +20,22 @@ index ab7dc27de3e304f6d912d5834da47e3b4eb25495..b6c0fd4ceee989dac55c7d54e52fef18
|
||||
}
|
||||
}
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index 43f09d1e68c88d3ba3b862a1a74769f73c370894..cedd2b0a0941fe66bdae479c4fc768ce3d7bc6ac 100644
|
||||
index 4ab316e45bd84e43a53335df60f847b17fe6c2fa..def9a302830e493e51cc2b3588816fcbd3a1bb51 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -146,6 +146,7 @@ template("node_gn_build") {
|
||||
configs += [
|
||||
":node_internal_config",
|
||||
"$node_v8_path:v8_tracing_config",
|
||||
@@ -143,7 +143,10 @@ template("node_gn_build") {
|
||||
[ "node.gyp" ])
|
||||
|
||||
source_set("libnode") {
|
||||
- configs += [ ":node_internal_config" ]
|
||||
+ configs += [
|
||||
+ ":node_internal_config",
|
||||
+ "//build/config/compiler:no_exit_time_destructors"
|
||||
]
|
||||
+ ]
|
||||
public_configs = [
|
||||
":node_external_config",
|
||||
@@ -368,6 +369,7 @@ template("node_gn_build") {
|
||||
"deps/googletest:googletest_config",
|
||||
@@ -364,6 +367,7 @@ template("node_gn_build") {
|
||||
"src/embedded_data.h",
|
||||
]
|
||||
include_dirs = [ "src", "tools" ]
|
||||
|
||||
@@ -18,10 +18,10 @@ Stage 3.
|
||||
Upstreamed in https://github.com/nodejs/node/pull/60364
|
||||
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index 6e7df97bfdb3bb2ff9fcbb0eba6118239018d632..2b221e84bb5e84829af8193b38eec31b57668e75 100644
|
||||
index b9d35e60f39d1edd910cd0fc1e57157458db93f5..4421ddd05f69e32f38d074a4cc04e4e7eac89e76 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -783,7 +783,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
|
||||
@@ -778,7 +778,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
|
||||
|
||||
if (std::ranges::find(v8_args, "--no-js-source-phase-imports") ==
|
||||
v8_args.end()) {
|
||||
|
||||
@@ -12,10 +12,10 @@ This can be removed/refactored once Node.js upgrades to a version of V8
|
||||
containing the above CL.
|
||||
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index ca74e83ef6f7b0e8b8496457af3813f07f52eb37..6e7df97bfdb3bb2ff9fcbb0eba6118239018d632 100644
|
||||
index 0bc086ccd1ff449c0f3fb08a972a0c45d3178f1c..b9d35e60f39d1edd910cd0fc1e57157458db93f5 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -1249,7 +1249,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
@@ -1244,7 +1244,7 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
|
||||
result->platform_ = per_process::v8_platform.Platform();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ change, it seems to introduce an incompatibility when compiling
|
||||
using clang modules. Disabling them resolves the issue.
|
||||
|
||||
diff --git a/unofficial.gni b/unofficial.gni
|
||||
index cedd2b0a0941fe66bdae479c4fc768ce3d7bc6ac..86bd7d18ca299d0866c872b52fb0508174c148d9 100644
|
||||
index def9a302830e493e51cc2b3588816fcbd3a1bb51..900c5e4d8a48d0725420518c923c7024518158b8 100644
|
||||
--- a/unofficial.gni
|
||||
+++ b/unofficial.gni
|
||||
@@ -199,6 +199,10 @@ template("node_gn_build") {
|
||||
@@ -197,6 +197,10 @@ template("node_gn_build") {
|
||||
"CoreFoundation.framework",
|
||||
"Security.framework",
|
||||
]
|
||||
@@ -24,7 +24,7 @@ index cedd2b0a0941fe66bdae479c4fc768ce3d7bc6ac..86bd7d18ca299d0866c872b52fb05081
|
||||
}
|
||||
if (is_posix) {
|
||||
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
|
||||
@@ -371,6 +375,12 @@ template("node_gn_build") {
|
||||
@@ -369,6 +373,12 @@ template("node_gn_build") {
|
||||
include_dirs = [ "src", "tools" ]
|
||||
configs += [ "//build/config/compiler:no_exit_time_destructors" ]
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ libc++ added [[nodiscard]] to std::filesystem::copy_options operator|=
|
||||
which causes build failures with -Werror.
|
||||
|
||||
diff --git a/src/node_file.cc b/src/node_file.cc
|
||||
index 46cd16b535d9bd651ef733ca52ea58db7d39b09f..7a7c71a0fcbb71e1c3dfcac7a00da207c4c3bf56 100644
|
||||
index 547455bb5011677719a8de1f98cb447561bce6aa..385db5fd6fe5db6bb7ff17e98309b6cd605a82d3 100644
|
||||
--- a/src/node_file.cc
|
||||
+++ b/src/node_file.cc
|
||||
@@ -3467,11 +3467,11 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -3460,11 +3460,11 @@ static void CpSyncCopyDir(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
auto file_copy_opts = std::filesystem::copy_options::recursive;
|
||||
if (force) {
|
||||
|
||||
@@ -89,7 +89,7 @@ index fb2af584a4ae777022c9ef8c20ada1edcbbbefdc..fe6300a5d5d2d6602a84cbd33736c213
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index c185d822b29c0b691bbf5f724f71f59638c6184d..57a46c8be2e052b298ed841eed6f291d62711750 100644
|
||||
index fdabe48dd7776c59298f7d972286d0d2ed062752..b5cf58cc953590493beb52abf249e33e486ffc46 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -611,7 +611,7 @@ IsolateData::~IsolateData() {}
|
||||
|
||||
@@ -19,7 +19,7 @@ index 2c95ac99be70b0750372e9c858753bf519498e3d..5ab30502fd232196739ca2b450e35cc9
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
if (module->GetStatus() < Module::kInstantiated) {
|
||||
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
|
||||
index 9f11d32c70366524cf3b7c1cfdfd24f31e438e7b..3f1772b62aa0300540d25fb93012c49bce9d8134 100644
|
||||
index e66d4fcb0c064f96cdb819c783027d864fe88d12..619980b36db457ef7e476eacd446e3bf2a9a71d2 100644
|
||||
--- a/src/node_contextify.cc
|
||||
+++ b/src/node_contextify.cc
|
||||
@@ -460,7 +460,7 @@ ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo<T>& args) {
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
build_warn_instead_of_abort_on_builtin_pgo_profile_mismatch.patch
|
||||
src_use_legacy_trace_macros_in_perfetto_to_support_all_phases.patch
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Tue, 31 Mar 2026 07:05:17 +0900
|
||||
Subject: src: use legacy trace macros in perfetto to support all phases
|
||||
|
||||
Replace the phase-specific SDK macros with the INTERNAL_TRACE_EVENT_ADD
|
||||
and INTERNAL_TRACE_EVENT_ADD_WITH_ID legacy shim macros, which accept an
|
||||
arbitrary phase character and forward it through Perfetto's legacy
|
||||
compatibility layer. This supports nestable async ('b'/'e'/'n')
|
||||
and counter ('C') trace event phases needed for Node.js.
|
||||
|
||||
Additionally:
|
||||
|
||||
1. Clear TRACE_EVENT_FLAG_COPY before calling Perfetto macros. Perfetto
|
||||
manages string lifetimes internally via DynamicString and
|
||||
TRACE_STR_COPY; passing FLAG_COPY triggers a CHECK failure in
|
||||
Perfetto's legacy shim.
|
||||
|
||||
2. Default instant events (phase 'I') with GLOBAL scope to THREAD scope.
|
||||
Under Perfetto, global-scope instant events land on Track::Global(0),
|
||||
producing pid:0/tid:0 in the trace output, making them effectively
|
||||
invisible in trace viewers.
|
||||
|
||||
diff --git a/src/builtins/builtins-trace.cc b/src/builtins/builtins-trace.cc
|
||||
index c17d72d477d4c28d25e3f385d8af3c5b7024f7f7..5990e6cee1d08ba0e86059cb7f3affc878dcc632 100644
|
||||
--- a/src/builtins/builtins-trace.cc
|
||||
+++ b/src/builtins/builtins-trace.cc
|
||||
@@ -181,37 +181,44 @@ BUILTIN(Trace) {
|
||||
}
|
||||
|
||||
#if defined(V8_USE_PERFETTO)
|
||||
- // TODO(skyostil): Use interned names to reduce trace size.
|
||||
- auto trace_args = [&](perfetto::EventContext ctx) {
|
||||
+ // Perfetto handles string lifetimes internally (via DynamicString and
|
||||
+ // TRACE_STR_COPY), so TRACE_EVENT_FLAG_COPY must not be set — Perfetto's
|
||||
+ // legacy shim CHECKs against it.
|
||||
+ flags &= ~TRACE_EVENT_FLAG_COPY;
|
||||
+
|
||||
+ // Default instant events to thread scope under Perfetto. Without this,
|
||||
+ // scope bits are 0 (GLOBAL), which puts events on Track::Global(0)
|
||||
+ // resulting in pid:0/tid:0 in the trace output.
|
||||
+ if (phase == TRACE_EVENT_PHASE_INSTANT) {
|
||||
+ auto scope = flags & TRACE_EVENT_FLAG_SCOPE_MASK;
|
||||
+ if (scope == TRACE_EVENT_SCOPE_GLOBAL) {
|
||||
+ flags = (flags & ~TRACE_EVENT_FLAG_SCOPE_MASK) | TRACE_EVENT_SCOPE_THREAD;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Use the legacy trace event macros which support all phase types
|
||||
+ // (including nestable async 'b'/'e'/'n' and counter 'C' used by Node.js)
|
||||
+ if (flags & TRACE_EVENT_FLAG_HAS_ID) {
|
||||
if (num_args) {
|
||||
MaybeUtf8 arg_contents(isolate, Cast<String>(arg_json));
|
||||
- auto annotation = ctx.event()->add_debug_annotations();
|
||||
- annotation->set_name(arg_name);
|
||||
- annotation->set_legacy_json_value(*arg_contents);
|
||||
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), id, flags,
|
||||
+ arg_name, TRACE_STR_COPY(*arg_contents));
|
||||
+ } else {
|
||||
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), id, flags);
|
||||
}
|
||||
- if (flags & TRACE_EVENT_FLAG_HAS_ID) {
|
||||
- auto legacy_event = ctx.event()->set_legacy_event();
|
||||
- legacy_event->set_global_id(id);
|
||||
+ } else {
|
||||
+ if (num_args) {
|
||||
+ MaybeUtf8 arg_contents(isolate, Cast<String>(arg_json));
|
||||
+ INTERNAL_TRACE_EVENT_ADD(
|
||||
+ phase, dynamic_category, perfetto::DynamicString(*name), flags,
|
||||
+ arg_name, TRACE_STR_COPY(*arg_contents));
|
||||
+ } else {
|
||||
+ INTERNAL_TRACE_EVENT_ADD(phase, dynamic_category,
|
||||
+ perfetto::DynamicString(*name), flags);
|
||||
}
|
||||
- };
|
||||
-
|
||||
- switch (phase) {
|
||||
- case TRACE_EVENT_PHASE_BEGIN:
|
||||
- TRACE_EVENT_BEGIN(dynamic_category, perfetto::DynamicString(*name),
|
||||
- trace_args);
|
||||
- break;
|
||||
- case TRACE_EVENT_PHASE_END:
|
||||
- TRACE_EVENT_END(dynamic_category, trace_args);
|
||||
- break;
|
||||
- case TRACE_EVENT_PHASE_INSTANT:
|
||||
- TRACE_EVENT_INSTANT(dynamic_category, perfetto::DynamicString(*name),
|
||||
- trace_args);
|
||||
- break;
|
||||
- default:
|
||||
- THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
- isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError));
|
||||
}
|
||||
-
|
||||
#else // !defined(V8_USE_PERFETTO)
|
||||
uint8_t arg_type;
|
||||
uint64_t arg_value;
|
||||
@@ -8,7 +8,9 @@ from pathlib import Path
|
||||
|
||||
SRC_DIR = Path(__file__).resolve().parents[3]
|
||||
sys.path.append(os.path.join(SRC_DIR, 'third_party/electron_node/tools'))
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1])) # electron/script/
|
||||
|
||||
from lib.util import get_out_dir
|
||||
import install
|
||||
|
||||
class LoadPythonDictionaryError(Exception):
|
||||
@@ -31,13 +33,6 @@ def LoadPythonDictionary(path):
|
||||
)
|
||||
return file_data
|
||||
|
||||
def get_out_dir():
|
||||
out_dir = 'Testing'
|
||||
override = os.environ.get('ELECTRON_OUT_DIR')
|
||||
if override is not None:
|
||||
out_dir = override
|
||||
return os.path.join(SRC_DIR, 'out', out_dir)
|
||||
|
||||
if __name__ == '__main__':
|
||||
node_root_dir = os.path.join(SRC_DIR, 'third_party/electron_node')
|
||||
out = {}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "base/no_destructor.h"
|
||||
#include "content/common/url_schemes.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "gin/converter.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
@@ -19,13 +20,13 @@
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/net_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "url/url_util.h"
|
||||
#include "v8/include/cppgc/allocation.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -81,7 +82,8 @@ struct Converter<CustomScheme> {
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
gin::DeprecatedWrapperInfo Protocol::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
const gin::WrapperInfo Protocol::kWrapperInfo = {{gin::kEmbedderNativeGin},
|
||||
gin::kElectronProtocol};
|
||||
|
||||
std::vector<std::string>& GetStandardSchemes() {
|
||||
static base::NoDestructor<std::vector<std::string>> g_standard_schemes;
|
||||
@@ -296,23 +298,22 @@ void Protocol::HandleOptionalCallback(gin::Arguments* args, Error error) {
|
||||
}
|
||||
|
||||
// static
|
||||
gin_helper::Handle<Protocol> Protocol::Create(
|
||||
v8::Isolate* isolate,
|
||||
ProtocolRegistry* protocol_registry) {
|
||||
return gin_helper::CreateHandle(isolate, new Protocol{protocol_registry});
|
||||
Protocol* Protocol::Create(v8::Isolate* isolate,
|
||||
ProtocolRegistry* protocol_registry) {
|
||||
return cppgc::MakeGarbageCollected<Protocol>(
|
||||
isolate->GetCppHeap()->GetAllocationHandle(), protocol_registry);
|
||||
}
|
||||
|
||||
// static
|
||||
gin_helper::Handle<Protocol> Protocol::New(gin_helper::ErrorThrower thrower) {
|
||||
Protocol* Protocol::New(gin_helper::ErrorThrower thrower) {
|
||||
thrower.ThrowError("Protocol cannot be created from JS");
|
||||
return {};
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::ObjectTemplate> Protocol::FillObjectTemplate(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> tmpl) {
|
||||
return gin::ObjectTemplateBuilder(isolate, GetClassName(), tmpl)
|
||||
void Protocol::FillObjectTemplate(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> tmpl) {
|
||||
gin::ObjectTemplateBuilder(isolate, GetClassName(), tmpl)
|
||||
.SetMethod("registerStringProtocol",
|
||||
&Protocol::RegisterProtocolFor<ProtocolType::kString>)
|
||||
.SetMethod("registerBufferProtocol",
|
||||
@@ -345,8 +346,12 @@ v8::Local<v8::ObjectTemplate> Protocol::FillObjectTemplate(
|
||||
.Build();
|
||||
}
|
||||
|
||||
const char* Protocol::GetTypeName() {
|
||||
return GetClassName();
|
||||
const gin::WrapperInfo* Protocol::wrapper_info() const {
|
||||
return &kWrapperInfo;
|
||||
}
|
||||
|
||||
const char* Protocol::GetHumanReadableName() const {
|
||||
return "Electron / Protocol";
|
||||
}
|
||||
|
||||
} // namespace electron::api
|
||||
@@ -372,7 +377,8 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
v8::Isolate* const isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Dictionary dict{isolate, exports};
|
||||
dict.Set("Protocol",
|
||||
electron::api::Protocol::GetConstructor(isolate, context));
|
||||
electron::api::Protocol::GetConstructor(
|
||||
isolate, context, &electron::api::Protocol::kWrapperInfo));
|
||||
dict.SetMethod("registerSchemesAsPrivileged", &RegisterSchemesAsPrivileged);
|
||||
dict.SetMethod("getStandardSchemes", &electron::api::GetStandardSchemes);
|
||||
}
|
||||
|
||||
@@ -9,20 +9,14 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/browser/net/electron_url_loader_factory.h"
|
||||
#include "shell/common/gin_helper/constructible.h"
|
||||
#include "shell/common/gin_helper/wrappable.h"
|
||||
|
||||
namespace gin {
|
||||
class Arguments;
|
||||
} // namespace gin
|
||||
|
||||
namespace gin_helper {
|
||||
template <typename T>
|
||||
class Handle;
|
||||
} // namespace gin_helper
|
||||
|
||||
namespace electron {
|
||||
|
||||
class ProtocolRegistry;
|
||||
@@ -38,23 +32,25 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
||||
v8::Local<v8::Value> val);
|
||||
|
||||
// Protocol implementation based on network services.
|
||||
class Protocol final : public gin_helper::DeprecatedWrappable<Protocol>,
|
||||
class Protocol final : public gin::Wrappable<Protocol>,
|
||||
public gin_helper::Constructible<Protocol> {
|
||||
public:
|
||||
static gin_helper::Handle<Protocol> Create(
|
||||
v8::Isolate* isolate,
|
||||
ProtocolRegistry* protocol_registry);
|
||||
static Protocol* Create(v8::Isolate* isolate,
|
||||
ProtocolRegistry* protocol_registry);
|
||||
|
||||
// gin_helper::Constructible
|
||||
static gin_helper::Handle<Protocol> New(gin_helper::ErrorThrower thrower);
|
||||
static v8::Local<v8::ObjectTemplate> FillObjectTemplate(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> tmpl);
|
||||
static Protocol* New(gin_helper::ErrorThrower thrower);
|
||||
static void FillObjectTemplate(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> tmpl);
|
||||
static const char* GetClassName() { return "Protocol"; }
|
||||
|
||||
// gin_helper::Wrappable
|
||||
static gin::DeprecatedWrapperInfo kWrapperInfo;
|
||||
const char* GetTypeName() override;
|
||||
// gin::Wrappable
|
||||
static const gin::WrapperInfo kWrapperInfo;
|
||||
const gin::WrapperInfo* wrapper_info() const override;
|
||||
const char* GetHumanReadableName() const override;
|
||||
|
||||
explicit Protocol(ProtocolRegistry* protocol_registry);
|
||||
~Protocol() override;
|
||||
|
||||
private:
|
||||
// Possible errors.
|
||||
@@ -70,9 +66,6 @@ class Protocol final : public gin_helper::DeprecatedWrappable<Protocol>,
|
||||
using CompletionCallback =
|
||||
base::RepeatingCallback<void(v8::Local<v8::Value>)>;
|
||||
|
||||
explicit Protocol(ProtocolRegistry* protocol_registry);
|
||||
~Protocol() override;
|
||||
|
||||
[[nodiscard]] static std::string_view ErrorCodeToString(Error error);
|
||||
|
||||
// JS APIs.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/types/pass_key.h"
|
||||
@@ -76,6 +77,7 @@
|
||||
#include "shell/browser/media/media_device_id_salt.h"
|
||||
#include "shell/browser/net/cert_verifier_client.h"
|
||||
#include "shell/browser/net/resolve_host_function.h"
|
||||
#include "shell/browser/net/resolve_proxy_helper.h"
|
||||
#include "shell/browser/session_preferences.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/content_converter.h"
|
||||
@@ -558,9 +560,7 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
|
||||
|
||||
SessionPreferences::CreateForBrowserContext(browser_context);
|
||||
|
||||
protocol_.Reset(
|
||||
isolate,
|
||||
Protocol::Create(isolate, browser_context->protocol_registry()).ToV8());
|
||||
protocol_ = Protocol::Create(isolate, browser_context->protocol_registry());
|
||||
|
||||
browser_context->SetUserData(
|
||||
kElectronApiSessionKey,
|
||||
@@ -1354,8 +1354,8 @@ v8::Local<v8::Value> Session::Extensions(v8::Isolate* isolate) {
|
||||
return extensions_.Get(isolate);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
|
||||
return protocol_.Get(isolate);
|
||||
api::Protocol* Session::Protocol() {
|
||||
return protocol_.Get();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
@@ -20,7 +19,6 @@
|
||||
#include "services/network/public/mojom/ssl_config.mojom-forward.h"
|
||||
#include "shell/browser/api/ipc_dispatcher.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/browser/net/resolve_proxy_helper.h"
|
||||
#include "shell/common/gin_helper/constructible.h"
|
||||
#include "shell/common/gin_helper/self_keep_alive.h"
|
||||
|
||||
@@ -60,6 +58,7 @@ struct PreloadScript;
|
||||
namespace api {
|
||||
|
||||
class NetLog;
|
||||
class Protocol;
|
||||
class WebRequest;
|
||||
|
||||
class Session final : public gin::Wrappable<Session>,
|
||||
@@ -167,7 +166,7 @@ class Session final : public gin::Wrappable<Session>,
|
||||
const gin_helper::Dictionary& options);
|
||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Extensions(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||
api::Protocol* Protocol();
|
||||
v8::Local<v8::Value> ServiceWorkerContext(v8::Isolate* isolate);
|
||||
WebRequest* WebRequest(v8::Isolate* isolate);
|
||||
api::NetLog* NetLog(v8::Isolate* isolate);
|
||||
@@ -214,7 +213,7 @@ class Session final : public gin::Wrappable<Session>,
|
||||
// Cached gin_helper::Wrappable objects.
|
||||
v8::TracedReference<v8::Value> cookies_;
|
||||
v8::TracedReference<v8::Value> extensions_;
|
||||
v8::TracedReference<v8::Value> protocol_;
|
||||
cppgc::Member<api::Protocol> protocol_;
|
||||
cppgc::Member<api::NetLog> net_log_;
|
||||
v8::TracedReference<v8::Value> service_worker_context_;
|
||||
cppgc::Member<api::WebRequest> web_request_;
|
||||
|
||||
@@ -972,11 +972,12 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
|
||||
void WebContents::InitZoomController(content::WebContents* web_contents,
|
||||
const gin_helper::Dictionary& options) {
|
||||
WebContentsZoomController::CreateForWebContents(web_contents);
|
||||
zoom_controller_ = WebContentsZoomController::FromWebContents(web_contents);
|
||||
WebContentsZoomController* const zoom_controller =
|
||||
WebContentsZoomController::GetOrCreateForWebContents(web_contents);
|
||||
|
||||
double zoom_factor;
|
||||
if (options.Get(options::kZoomFactor, &zoom_factor))
|
||||
zoom_controller_->SetDefaultZoomFactor(zoom_factor);
|
||||
zoom_controller->SetDefaultZoomFactor(zoom_factor);
|
||||
|
||||
// Nothing to do with ZoomController, but this function gets called in all
|
||||
// init cases!
|
||||
@@ -2889,8 +2890,8 @@ v8::Local<v8::Promise> WebContents::SavePage(
|
||||
return handle;
|
||||
}
|
||||
|
||||
auto* handler = new SavePageHandler(web_contents(), std::move(promise));
|
||||
handler->Handle(full_file_path, save_type);
|
||||
auto* handler = new SavePageHandler{std::move(promise)};
|
||||
handler->Handle(full_file_path, save_type, web_contents());
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -3152,16 +3153,18 @@ void OnGetDeviceNameToUse(base::WeakPtr<content::WebContents> web_contents,
|
||||
.Set(printing::kSettingMediaSizeIsDefault, true);
|
||||
};
|
||||
|
||||
const bool use_default_size =
|
||||
print_settings.FindBool(kUseDefaultPrinterPageSize).value_or(false);
|
||||
std::optional<gfx::Size> paper_size;
|
||||
if (use_default_size)
|
||||
paper_size = GetPrinterDefaultPaperSize(base::UTF16ToUTF8(info.second));
|
||||
if (!print_settings.Find(printing::kSettingMediaSize)) {
|
||||
const bool use_default_size =
|
||||
print_settings.FindBool(kUseDefaultPrinterPageSize).value_or(false);
|
||||
std::optional<gfx::Size> paper_size;
|
||||
if (use_default_size)
|
||||
paper_size = GetPrinterDefaultPaperSize(base::UTF16ToUTF8(info.second));
|
||||
|
||||
print_settings.Set(
|
||||
printing::kSettingMediaSize,
|
||||
paper_size ? make_media_size(paper_size->height(), paper_size->width())
|
||||
: make_media_size(297000, 210000));
|
||||
print_settings.Set(
|
||||
printing::kSettingMediaSize,
|
||||
paper_size ? make_media_size(paper_size->height(), paper_size->width())
|
||||
: make_media_size(297000, 210000));
|
||||
}
|
||||
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get());
|
||||
if (!rfh)
|
||||
@@ -3867,12 +3870,16 @@ gfx::Size WebContents::GetSizeForNewRenderView(content::WebContents* wc) {
|
||||
return {};
|
||||
}
|
||||
|
||||
WebContentsZoomController* WebContents::GetZoomController() const {
|
||||
return WebContentsZoomController::FromWebContents(web_contents());
|
||||
}
|
||||
|
||||
void WebContents::SetZoomLevel(double level) {
|
||||
zoom_controller_->SetZoomLevel(level);
|
||||
GetZoomController()->SetZoomLevel(level);
|
||||
}
|
||||
|
||||
double WebContents::GetZoomLevel() const {
|
||||
return zoom_controller_->GetZoomLevel();
|
||||
return GetZoomController()->GetZoomLevel();
|
||||
}
|
||||
|
||||
void WebContents::SetZoomFactor(gin_helper::ErrorThrower thrower,
|
||||
@@ -3892,7 +3899,7 @@ double WebContents::GetZoomFactor() const {
|
||||
}
|
||||
|
||||
void WebContents::SetTemporaryZoomLevel(double level) {
|
||||
zoom_controller_->SetTemporaryZoomLevel(level);
|
||||
GetZoomController()->SetTemporaryZoomLevel(level);
|
||||
}
|
||||
|
||||
std::optional<PreloadScript> WebContents::GetPreloadScript() const {
|
||||
|
||||
@@ -380,7 +380,7 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
content::RenderFrameHost* Opener();
|
||||
content::RenderFrameHost* FocusedFrame();
|
||||
|
||||
WebContentsZoomController* GetZoomController() { return zoom_controller_; }
|
||||
[[nodiscard]] WebContentsZoomController* GetZoomController() const;
|
||||
|
||||
void AddObserver(ExtendedWebContentsObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
@@ -858,11 +858,6 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
// destroyed before dialog_manager_, otherwise a crash would happen.
|
||||
std::unique_ptr<InspectableWebContents> inspectable_web_contents_;
|
||||
|
||||
// The zoom controller for this webContents.
|
||||
// Note: owned by inspectable_web_contents_, so declare this *after*
|
||||
// that field to ensure the dtor destroys them in the right order.
|
||||
raw_ptr<WebContentsZoomController> zoom_controller_ = nullptr;
|
||||
|
||||
std::optional<GURL> pending_unload_url_ = std::nullopt;
|
||||
|
||||
// Maps url to file path, used by the file requests sent from devtools.
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
SavePageHandler::SavePageHandler(content::WebContents* web_contents,
|
||||
gin_helper::Promise<void> promise)
|
||||
: web_contents_(web_contents), promise_(std::move(promise)) {}
|
||||
SavePageHandler::SavePageHandler(gin_helper::Promise<void> promise)
|
||||
: promise_{std::move(promise)} {}
|
||||
|
||||
SavePageHandler::~SavePageHandler() = default;
|
||||
|
||||
@@ -26,9 +25,10 @@ void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager,
|
||||
}
|
||||
|
||||
bool SavePageHandler::Handle(const base::FilePath& full_path,
|
||||
const content::SavePageType& save_type) {
|
||||
const content::SavePageType& save_type,
|
||||
content::WebContents* web_contents) {
|
||||
auto* download_manager =
|
||||
web_contents_->GetBrowserContext()->GetDownloadManager();
|
||||
web_contents->GetBrowserContext()->GetDownloadManager();
|
||||
download_manager->AddObserver(this);
|
||||
// Chromium will create a 'foo_files' directory under the directory of saving
|
||||
// page 'foo.html' for holding other resource files of 'foo.html'.
|
||||
@@ -36,7 +36,7 @@ bool SavePageHandler::Handle(const base::FilePath& full_path,
|
||||
full_path.RemoveExtension().BaseName().value() +
|
||||
FILE_PATH_LITERAL("_files"));
|
||||
bool result =
|
||||
web_contents_->SavePage(full_path, saved_main_directory_path, save_type);
|
||||
web_contents->SavePage(full_path, saved_main_directory_path, save_type);
|
||||
download_manager->RemoveObserver(this);
|
||||
// If initialization fails which means fail to create |DownloadItem|, we need
|
||||
// to delete the |SavePageHandler| instance to avoid memory-leak.
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
|
||||
#define ELECTRON_SHELL_BROWSER_API_SAVE_PAGE_HANDLER_H_
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "components/download/public/common/download_item.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
#include "content/public/browser/save_page_type.h"
|
||||
@@ -26,12 +25,12 @@ namespace electron::api {
|
||||
class SavePageHandler : private content::DownloadManager::Observer,
|
||||
private download::DownloadItem::Observer {
|
||||
public:
|
||||
SavePageHandler(content::WebContents* web_contents,
|
||||
gin_helper::Promise<void> promise);
|
||||
explicit SavePageHandler(gin_helper::Promise<void> promise);
|
||||
~SavePageHandler() override;
|
||||
|
||||
bool Handle(const base::FilePath& full_path,
|
||||
const content::SavePageType& save_type);
|
||||
const content::SavePageType& save_type,
|
||||
content::WebContents* web_contents);
|
||||
|
||||
private:
|
||||
void Destroy(download::DownloadItem* item);
|
||||
@@ -43,7 +42,6 @@ class SavePageHandler : private content::DownloadManager::Observer,
|
||||
// download::DownloadItem::Observer:
|
||||
void OnDownloadUpdated(download::DownloadItem* item) override;
|
||||
|
||||
raw_ptr<content::WebContents> web_contents_; // weak
|
||||
gin_helper::Promise<void> promise_;
|
||||
};
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
#include "pdf/pdf_features.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h"
|
||||
#include "services/device/public/cpp/geolocation/location_provider.h"
|
||||
@@ -1591,6 +1592,46 @@ bool ElectronBrowserClient::ShouldEnableStrictSiteIsolation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::ShouldEnableSubframeZoom() {
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
return chrome_pdf::features::IsOopifPdfEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
std::optional<network::CrossOriginEmbedderPolicy>
|
||||
ElectronBrowserClient::MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
if (!chrome_pdf::features::IsOopifPdfEnabled() ||
|
||||
!navigation_handle->IsPdf()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
content::RenderFrameHost* pdf_extension = navigation_handle->GetParentFrame();
|
||||
if (!pdf_extension) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
content::RenderFrameHost* pdf_embedder = pdf_extension->GetParent();
|
||||
CHECK(pdf_embedder);
|
||||
return pdf_embedder->GetCrossOriginEmbedderPolicy();
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
bool ElectronBrowserClient::DoesSiteRequireDedicatedProcess(
|
||||
content::BrowserContext* browser_context,
|
||||
const GURL& effective_site_url) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return GetEnabledExtensionFromEffectiveURL(browser_context,
|
||||
effective_site_url) != nullptr;
|
||||
#else
|
||||
return content::ContentBrowserClient::DoesSiteRequireDedicatedProcess(
|
||||
browser_context, effective_site_url);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::BindHostReceiverForRenderer(
|
||||
content::RenderProcessHost* render_process_host,
|
||||
mojo::GenericPendingReceiver receiver) {
|
||||
|
||||
@@ -30,6 +30,7 @@ class FilePath;
|
||||
|
||||
namespace content {
|
||||
class ClientCertificateDelegate;
|
||||
class NavigationHandle;
|
||||
class PlatformNotificationService;
|
||||
class NavigationThrottleRegistry;
|
||||
class QuotaPermissionContext;
|
||||
@@ -82,6 +83,14 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
// content::ContentBrowserClient:
|
||||
std::string GetApplicationLocale() override;
|
||||
bool ShouldEnableStrictSiteIsolation() override;
|
||||
bool ShouldEnableSubframeZoom() override;
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
std::optional<network::CrossOriginEmbedderPolicy>
|
||||
MaybeOverrideLocalURLCrossOriginEmbedderPolicy(
|
||||
content::NavigationHandle* navigation_handle) override;
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context,
|
||||
const GURL& effective_site_url) override;
|
||||
void BindHostReceiverForRenderer(
|
||||
content::RenderProcessHost* render_process_host,
|
||||
mojo::GenericPendingReceiver receiver) override;
|
||||
|
||||
@@ -71,7 +71,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
|
||||
std::move(transferrable_loader), original_url);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif) &&
|
||||
if (chrome_pdf::features::IsOopifPdfEnabled() &&
|
||||
extension_id == extension_misc::kPdfExtensionId) {
|
||||
pdf::PdfViewerStreamManager::Create(web_contents);
|
||||
pdf::PdfViewerStreamManager::FromWebContents(web_contents)
|
||||
|
||||
@@ -398,9 +398,7 @@ ExtensionFunction::ResponseAction TabsGetZoomFunction::Run() {
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
double zoom_level = contents->GetZoomController()->GetZoomLevel();
|
||||
double zoom_factor = blink::ZoomLevelToZoomFactor(zoom_level);
|
||||
|
||||
const double zoom_factor = contents->GetZoomFactor();
|
||||
return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor)));
|
||||
}
|
||||
|
||||
@@ -414,9 +412,9 @@ ExtensionFunction::ResponseAction TabsGetZoomSettingsFunction::Run() {
|
||||
if (!contents)
|
||||
return RespondNow(Error("No such tab"));
|
||||
|
||||
auto* zoom_controller = contents->GetZoomController();
|
||||
WebContentsZoomController::ZoomMode zoom_mode =
|
||||
contents->GetZoomController()->zoom_mode();
|
||||
const auto* zoom_controller = contents->GetZoomController();
|
||||
const WebContentsZoomController::ZoomMode zoom_mode =
|
||||
zoom_controller->zoom_mode();
|
||||
tabs::ZoomSettings zoom_settings;
|
||||
ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
|
||||
zoom_settings.default_zoom_factor =
|
||||
|
||||
@@ -34,10 +34,6 @@
|
||||
#include "printing/printing_features.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "ui/views/views_features.h"
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
void InitializeFeatureList() {
|
||||
@@ -71,13 +67,6 @@ void InitializeFeatureList() {
|
||||
blink::features::kDropInputEventsWhilePaintHolding.name;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Refs https://issues.chromium.org/issues/401996981
|
||||
// TODO(deepak1556): Remove this once test added in
|
||||
// https://github.com/electron/electron/pull/12904
|
||||
// can work without this feature.
|
||||
enable_features += std::string(",") +
|
||||
views::features::kEnableTransparentHwndEnlargement.name;
|
||||
|
||||
// See https://chromium-review.googlesource.com/c/chromium/src/+/7204292
|
||||
// This feature causes the following sandbox failure on Windows:
|
||||
// sandbox\policy\win\sandbox_win.cc:777 Sandbox cannot access executable
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/views/views_features.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_OZONE)
|
||||
#include "ui/base/ui_base_features.h"
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
@@ -71,31 +66,6 @@ struct Converter<electron::NativeWindow::TitleBarStyle> {
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
gfx::Size GetExpandedWindowSize(const NativeWindow* window,
|
||||
bool transparent,
|
||||
gfx::Size size) {
|
||||
if (!base::FeatureList::IsEnabled(
|
||||
views::features::kEnableTransparentHwndEnlargement) ||
|
||||
!transparent) {
|
||||
return size;
|
||||
}
|
||||
|
||||
gfx::Size min_size = display::win::GetScreenWin()->ScreenToDIPSize(
|
||||
window->GetAcceleratedWidget(), gfx::Size{64, 64});
|
||||
|
||||
// Some AMD drivers can't display windows that are less than 64x64 pixels,
|
||||
// so expand them to be at least that size. http://crbug.com/286609
|
||||
gfx::Size expanded(std::max(size.width(), min_size.width()),
|
||||
std::max(size.height(), min_size.height()));
|
||||
return expanded;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindow::NativeWindow(const int32_t base_window_id,
|
||||
const gin_helper::Dictionary& options,
|
||||
NativeWindow* parent)
|
||||
@@ -398,15 +368,7 @@ gfx::Size NativeWindow::GetContentMinimumSize() const {
|
||||
}
|
||||
|
||||
gfx::Size NativeWindow::GetContentMaximumSize() const {
|
||||
const auto size_constraints = GetContentSizeConstraints();
|
||||
gfx::Size maximum_size = size_constraints.GetMaximumSize();
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (size_constraints.HasMaximumSize())
|
||||
maximum_size = GetExpandedWindowSize(this, transparent(), maximum_size);
|
||||
#endif
|
||||
|
||||
return maximum_size;
|
||||
return GetContentSizeConstraints().GetMaximumSize();
|
||||
}
|
||||
|
||||
void NativeWindow::SetSheetOffset(const double offsetX, const double offsetY) {
|
||||
|
||||
@@ -440,13 +440,11 @@ NativeWindowViews::NativeWindowViews(const int32_t base_window_id,
|
||||
if (window)
|
||||
window->AddPreTargetHandler(this);
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// We need to set bounds again after widget init for two reasons:
|
||||
// 1. For CSD windows, user-specified bounds need to be inflated by frame
|
||||
// insets, but the frame view isn't available at first.
|
||||
// 2. The widget clamps bounds to fit the screen, but we want to allow
|
||||
// windows larger than the display.
|
||||
SetBounds(gfx::Rect(GetPosition(), size), false);
|
||||
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
|
||||
// The initial params.bounds was applied before the frame view existed, so
|
||||
// non-client insets weren't accounted for and bounds need to be set again.
|
||||
if (!GetRestoredFrameBorderInsets().IsEmpty())
|
||||
SetBounds(gfx::Rect(GetPosition(), size), false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -906,7 +904,9 @@ gfx::Rect NativeWindowViews::GetNormalBounds() const {
|
||||
if (IsMaximized() && transparent())
|
||||
return restore_bounds_;
|
||||
#endif
|
||||
return WidgetToLogicalBounds(widget()->GetRestoredBounds());
|
||||
gfx::Rect bounds = widget()->GetRestoredBounds();
|
||||
bounds.Inset(GetRestoredFrameBorderInsets());
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetContentSizeConstraints(
|
||||
@@ -1676,17 +1676,24 @@ NativeWindowHandle NativeWindowViews::GetNativeWindowHandle() const {
|
||||
|
||||
gfx::Rect NativeWindowViews::LogicalToWidgetBounds(
|
||||
const gfx::Rect& bounds) const {
|
||||
// Use widget() directly since NativeWindowViews::IsMaximized() can
|
||||
// call GetBounds and end up in a loop.
|
||||
if (widget()->IsMaximized() || widget()->IsFullscreen())
|
||||
return bounds;
|
||||
|
||||
gfx::Rect widget_bounds(bounds);
|
||||
const gfx::Insets frame_insets = GetRestoredFrameBorderInsets();
|
||||
widget_bounds.Outset(
|
||||
gfx::Outsets::TLBR(frame_insets.top(), frame_insets.left(),
|
||||
frame_insets.bottom(), frame_insets.right()));
|
||||
|
||||
return widget_bounds;
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowViews::WidgetToLogicalBounds(
|
||||
const gfx::Rect& bounds) const {
|
||||
if (widget()->IsMaximized() || widget()->IsFullscreen())
|
||||
return bounds;
|
||||
|
||||
gfx::Rect logical_bounds(bounds);
|
||||
logical_bounds.Inset(GetRestoredFrameBorderInsets());
|
||||
return logical_bounds;
|
||||
|
||||
@@ -194,6 +194,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
TaskbarHost& taskbar_host() { return taskbar_host_; }
|
||||
void UpdateThickFrame();
|
||||
void SetLayered();
|
||||
bool has_thick_frame() const { return thick_frame_; }
|
||||
#endif
|
||||
|
||||
SkColor overlay_button_color() const { return overlay_button_color_; }
|
||||
|
||||
@@ -158,45 +158,43 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
windowSize.width() - contentSize.width() + extraSize.width();
|
||||
double extraHeightPlusFrame = titleBarHeight + extraSize.height();
|
||||
|
||||
newSize.width =
|
||||
roundf((frameSize.height - extraHeightPlusFrame) * aspectRatio +
|
||||
extraWidthPlusFrame);
|
||||
newSize.height =
|
||||
roundf((newSize.width - extraWidthPlusFrame) / aspectRatio +
|
||||
extraHeightPlusFrame);
|
||||
auto widthForHeight = [&](double h) {
|
||||
return (h - extraHeightPlusFrame) * aspectRatio + extraWidthPlusFrame;
|
||||
};
|
||||
auto heightForWidth = [&](double w) {
|
||||
return (w - extraWidthPlusFrame) / aspectRatio + extraHeightPlusFrame;
|
||||
};
|
||||
|
||||
newSize.width = roundf(widthForHeight(frameSize.height));
|
||||
newSize.height = roundf(heightForWidth(newSize.width));
|
||||
|
||||
// Clamp to minimum width/height while ensuring aspect ratio remains.
|
||||
NSSize minSize = [window minSize];
|
||||
NSSize zeroSize =
|
||||
shell_->has_frame() ? NSMakeSize(0, titleBarHeight) : NSZeroSize;
|
||||
if (!NSEqualSizes(minSize, zeroSize)) {
|
||||
double minWidthForAspectRatio =
|
||||
(minSize.height - titleBarHeight) * aspectRatio;
|
||||
bool atMinHeight =
|
||||
minSize.height > zeroSize.height && newSize.height <= minSize.height;
|
||||
newSize.width = atMinHeight ? minWidthForAspectRatio
|
||||
newSize.width = atMinHeight ? widthForHeight(minSize.height)
|
||||
: std::max(newSize.width, minSize.width);
|
||||
|
||||
double minHeightForAspectRatio = minSize.width / aspectRatio;
|
||||
bool atMinWidth =
|
||||
minSize.width > zeroSize.width && newSize.width <= minSize.width;
|
||||
newSize.height = atMinWidth ? minHeightForAspectRatio
|
||||
newSize.height = atMinWidth ? heightForWidth(minSize.width)
|
||||
: std::max(newSize.height, minSize.height);
|
||||
}
|
||||
|
||||
// Clamp to maximum width/height while ensuring aspect ratio remains.
|
||||
NSSize maxSize = [window maxSize];
|
||||
if (!NSEqualSizes(maxSize, NSMakeSize(FLT_MAX, FLT_MAX))) {
|
||||
double maxWidthForAspectRatio = maxSize.height * aspectRatio;
|
||||
bool atMaxHeight =
|
||||
maxSize.height < FLT_MAX && newSize.height >= maxSize.height;
|
||||
newSize.width = atMaxHeight ? maxWidthForAspectRatio
|
||||
newSize.width = atMaxHeight ? widthForHeight(maxSize.height)
|
||||
: std::min(newSize.width, maxSize.width);
|
||||
|
||||
double maxHeightForAspectRatio = maxSize.width / aspectRatio;
|
||||
bool atMaxWidth =
|
||||
maxSize.width < FLT_MAX && newSize.width >= maxSize.width;
|
||||
newSize.height = atMaxWidth ? maxHeightForAspectRatio
|
||||
newSize.height = atMaxWidth ? heightForWidth(maxSize.width)
|
||||
: std::min(newSize.height, maxSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,29 +106,19 @@ int WinFrameView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(
|
||||
views::HWNDForWidget(frame()), DWMWA_CAPTION_BUTTON_BOUNDS,
|
||||
&button_bounds, sizeof(button_bounds)))) {
|
||||
gfx::RectF button_bounds_in_dips = gfx::ConvertRectToDips(
|
||||
gfx::Rect(button_bounds), display::win::GetDPIScale());
|
||||
// TODO(crbug.com/1131681): GetMirroredRect() requires an integer rect,
|
||||
// but the size in DIPs may not be an integer with a fractional device
|
||||
// scale factor. If we want to keep using integers, the choice to use
|
||||
// ToFlooredRectDeprecated() seems to be doing the wrong thing given the
|
||||
// comment below about insetting 1 DIP instead of 1 physical pixel. We
|
||||
// should probably use ToEnclosedRect() and then we could have inset 1
|
||||
// physical pixel here.
|
||||
gfx::Rect buttons =
|
||||
GetMirroredRect(gfx::ToFlooredRectDeprecated(button_bounds_in_dips));
|
||||
|
||||
gfx::Rect button_bounds_px(button_bounds);
|
||||
// There is a small one-pixel strip right above the caption buttons in
|
||||
// which the resize border "peeks" through.
|
||||
constexpr int kCaptionButtonTopInset = 1;
|
||||
// The sizing region at the window edge above the caption buttons is
|
||||
// 1 px regardless of scale factor. If we inset by 1 before converting
|
||||
// to DIPs, the precision loss might eliminate this region entirely. The
|
||||
// best we can do is to inset after conversion. This guarantees we'll
|
||||
// show the resize cursor when resizing is possible. The cost of which
|
||||
// is also maybe showing it over the portion of the DIP that isn't the
|
||||
// outermost pixel.
|
||||
buttons.Inset(gfx::Insets::TLBR(0, kCaptionButtonTopInset, 0, 0));
|
||||
// which the resize border "peeks" through. Inset in physical pixels
|
||||
// before converting to DIPs so the resize strip remains exposed at
|
||||
// fractional scale factors.
|
||||
button_bounds_px.Inset(gfx::Insets::TLBR(1, 0, 0, 0));
|
||||
|
||||
const gfx::RectF button_bounds_in_dips =
|
||||
gfx::ConvertRectToDips(button_bounds_px, display::win::GetDPIScale());
|
||||
// GetMirroredRect() requires an integer rect. Use ToEnclosedRect() so
|
||||
// the top inset is preserved (rounded up) at fractional scale factors.
|
||||
gfx::Rect buttons =
|
||||
GetMirroredRect(gfx::ToEnclosedRect(button_bounds_in_dips));
|
||||
if (buttons.Contains(point))
|
||||
return HTNOWHERE;
|
||||
}
|
||||
@@ -238,14 +228,15 @@ void WinFrameView::LayoutCaptionButtons() {
|
||||
int custom_height = window()->titlebar_overlay_height();
|
||||
int height = TitlebarHeight(custom_height);
|
||||
|
||||
// TODO(mlaurencin): This -1 creates a 1 pixel margin between the right
|
||||
// edge of the button container and the edge of the window, allowing for this
|
||||
// edge portion to return the correct hit test and be manually resized
|
||||
// properly. Alternatives can be explored, but the differences in view
|
||||
// structures between Electron and Chromium may result in this as the best
|
||||
// option.
|
||||
int variable_width =
|
||||
IsMaximized() ? preferred_size.width() : preferred_size.width() - 1;
|
||||
// Insets place the resize hit targets outside of the frame, so the caption
|
||||
// buttons can go right at the edge. Without insets, the resize hit
|
||||
// targets are inside the frame, and a 1px margin is needed to click and drag
|
||||
// next to the button container. The margin can be removed if support is added
|
||||
// for insets on non-thick frames.
|
||||
int variable_width = !RestoredFrameBorderInsets().IsEmpty()
|
||||
? preferred_size.width()
|
||||
: (IsMaximized() ? preferred_size.width()
|
||||
: preferred_size.width() - 1);
|
||||
caption_button_container_->SetBounds(width() - preferred_size.width(),
|
||||
WindowTopY(), variable_width, height);
|
||||
|
||||
@@ -277,22 +268,33 @@ bool WinFrameView::GetShouldPaintAsActive() {
|
||||
gfx::Size WinFrameView::GetMinimumSize() const {
|
||||
if (!window_)
|
||||
return gfx::Size();
|
||||
// Chromium expects minimum size to be in content dimensions on Windows
|
||||
// because it adds the frame border automatically in OnGetMinMaxInfo.
|
||||
// Chromium expects minimum size to be in content dimensions on Windows.
|
||||
// If WidgetSizeIsClientSize() is true, it will account for frame borders and
|
||||
// insets automatically.
|
||||
return window_->GetContentMinimumSize();
|
||||
}
|
||||
|
||||
gfx::Size WinFrameView::GetMaximumSize() const {
|
||||
if (!window_)
|
||||
return gfx::Size();
|
||||
// Chromium expects minimum size to be in content dimensions on Windows
|
||||
// because it adds the frame border automatically in OnGetMinMaxInfo.
|
||||
// See comment in GetMinimumSize().
|
||||
gfx::Size size = window_->GetContentMaximumSize();
|
||||
// Electron public APIs returns (0, 0) when maximum size is not set, but it
|
||||
// would break internal window APIs like HWNDMessageHandler::SetAspectRatio.
|
||||
return size.IsEmpty() ? gfx::Size(INT_MAX, INT_MAX) : size;
|
||||
}
|
||||
|
||||
gfx::Insets WinFrameView::RestoredFrameBorderInsets() const {
|
||||
if (window_->has_frame() || !window_->has_thick_frame() ||
|
||||
!window_->IsResizable())
|
||||
return {};
|
||||
|
||||
const int thickness =
|
||||
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CXSIZEFRAME) +
|
||||
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CXPADDEDBORDER);
|
||||
return gfx::Insets::TLBR(0, thickness, thickness, thickness);
|
||||
}
|
||||
|
||||
BEGIN_METADATA(WinFrameView)
|
||||
END_METADATA
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ class WinFrameView : public FramelessView {
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
gfx::Size GetMaximumSize() const override;
|
||||
|
||||
// views::FramelessView:
|
||||
gfx::Insets RestoredFrameBorderInsets() const override;
|
||||
|
||||
WinCaptionButtonContainer* caption_button_container() {
|
||||
return caption_button_container_;
|
||||
}
|
||||
|
||||
@@ -89,24 +89,45 @@ bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElectronDesktopWindowTreeHostWin::WidgetSizeIsClientSize() const {
|
||||
// For both framed and frameless windows with resize insets (thick frames),
|
||||
// this should return true so that the aura layer is sized to the client area
|
||||
// rather than the full HWND, and so insets are accounted for when handling
|
||||
// size/aspect ratio constraints.
|
||||
if (native_window_view_->has_thick_frame())
|
||||
return true;
|
||||
return views::DesktopWindowTreeHostWin::WidgetSizeIsClientSize();
|
||||
}
|
||||
|
||||
bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets(
|
||||
gfx::Insets* insets,
|
||||
int frame_thickness) const {
|
||||
// Windows by default extends the maximized window slightly larger than
|
||||
// current workspace, for frameless window since the standard frame has been
|
||||
// removed, the client area would then be drew outside current workspace.
|
||||
//
|
||||
// Indenting the client area can fix this behavior.
|
||||
if (IsMaximized() && !native_window_view_->has_frame()) {
|
||||
// The insets would be eventually passed to WM_NCCALCSIZE, which takes
|
||||
// the metrics under the DPI of _main_ monitor instead of current monitor.
|
||||
//
|
||||
// Please make sure you tested maximized frameless window under multiple
|
||||
// monitors with different DPIs before changing this code.
|
||||
if (!native_window_view_->has_frame()) {
|
||||
const int thickness = ::GetSystemMetrics(SM_CXSIZEFRAME) +
|
||||
::GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||
*insets = gfx::Insets::TLBR(thickness, thickness, thickness, thickness);
|
||||
return true;
|
||||
|
||||
if (IsMaximized()) {
|
||||
// Windows by default extends the maximized window slightly larger than
|
||||
// current workspace, for frameless window since the standard frame has
|
||||
// been removed, the client area would then be drew outside current
|
||||
// workspace.
|
||||
//
|
||||
// Indenting the client area can fix this behavior.
|
||||
//
|
||||
// The insets would be eventually passed to WM_NCCALCSIZE, which takes
|
||||
// the metrics under the DPI of _main_ monitor instead of current monitor.
|
||||
//
|
||||
// Please make sure you tested maximized frameless window under multiple
|
||||
// monitors with different DPIs before changing this code.
|
||||
*insets = gfx::Insets::TLBR(thickness, thickness, thickness, thickness);
|
||||
return true;
|
||||
} else if (native_window_view_->has_thick_frame() &&
|
||||
native_window_view_->IsResizable()) {
|
||||
// Grow the insets to support resize targets past the frame edge like in
|
||||
// windows with standard frames.
|
||||
*insets = gfx::Insets::TLBR(0, thickness, thickness, thickness);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
|
||||
LRESULT* result) override;
|
||||
bool ShouldPaintAsActive() const override;
|
||||
bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override;
|
||||
bool WidgetSizeIsClientSize() const override;
|
||||
bool GetClientAreaInsets(gfx::Insets* insets,
|
||||
int frame_thickness) const override;
|
||||
bool HandleMouseEventForCaption(UINT message) const override;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include "shell/common/crash_keys.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/circular_deque.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/strcat.h"
|
||||
@@ -28,17 +28,22 @@ namespace electron::crash_keys {
|
||||
|
||||
namespace {
|
||||
|
||||
// Do NOT replace with base::circular_deque. CrashKeyString wraps a
|
||||
// crashpad::Annotation that holds self-referential pointers and registers
|
||||
// in a process-global linked list; relocating elements (as circular_deque
|
||||
// does on growth) corrupts that list and hangs the crashpad handler.
|
||||
// std::deque never relocates existing elements. See #50795.
|
||||
auto& GetExtraCrashKeys() {
|
||||
constexpr size_t kMaxCrashKeyValueSize = 20320;
|
||||
static_assert(kMaxCrashKeyValueSize < crashpad::Annotation::kValueMaxSize,
|
||||
"max crash key value length above what crashpad supports");
|
||||
using CrashKeyString = crash_reporter::CrashKeyString<kMaxCrashKeyValueSize>;
|
||||
static base::NoDestructor<base::circular_deque<CrashKeyString>> extra_keys;
|
||||
static base::NoDestructor<std::deque<CrashKeyString>> extra_keys;
|
||||
return *extra_keys;
|
||||
}
|
||||
|
||||
auto& GetExtraCrashKeyNames() {
|
||||
static base::NoDestructor<base::circular_deque<std::string>> crash_key_names;
|
||||
static base::NoDestructor<std::deque<std::string>> crash_key_names;
|
||||
return *crash_key_names;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "components/network_hints/renderer/web_prescient_networking_impl.h"
|
||||
#include "content/common/buildflags.h"
|
||||
|
||||
@@ -5641,21 +5641,6 @@ describe('BrowserWindow module', () => {
|
||||
expectBoundsEqual(w.getSize(), [400, 300]);
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'darwin')('works for a window smaller than 64x64', () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
frame: false,
|
||||
resizable: false,
|
||||
transparent: true
|
||||
});
|
||||
w.setContentSize(60, 60);
|
||||
expectBoundsEqual(w.getContentSize(), [60, 60]);
|
||||
w.setContentSize(30, 30);
|
||||
expectBoundsEqual(w.getContentSize(), [30, 30]);
|
||||
w.setContentSize(10, 10);
|
||||
expectBoundsEqual(w.getContentSize(), [10, 10]);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('do not change window with frame bounds when maximized', () => {
|
||||
const w = new BrowserWindow({
|
||||
show: true,
|
||||
|
||||
@@ -185,112 +185,4 @@ ifdescribe(!(['arm', 'arm64'].includes(process.arch)) || (process.platform !== '
|
||||
expect(parsed.traceEvents.some((x: any) => x.cat === 'disabled-by-default-v8.cpu_profiler' && x.name === 'ProfileChunk')).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('node trace categories', () => {
|
||||
it('captures performance.mark() as instant trace events', async function () {
|
||||
const { performance } = require('node:perf_hooks');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.perf.usertiming']
|
||||
});
|
||||
|
||||
performance.mark('test-trace-mark');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const markEvents = parsed.traceEvents.filter(
|
||||
(x: any) => x.cat === 'node.perf.usertiming' && x.name === 'test-trace-mark'
|
||||
);
|
||||
expect(markEvents).to.have.lengthOf.at.least(1, 'should have node.perf.usertiming events for performance.mark()');
|
||||
expect(markEvents[0].ph).to.equal('I', 'performance.mark() should emit instant (I) phase events');
|
||||
});
|
||||
|
||||
it('captures performance.measure() as nestable async begin/end trace events', async function () {
|
||||
const { performance } = require('node:perf_hooks');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.perf.usertiming']
|
||||
});
|
||||
|
||||
performance.mark('trace-measure-start');
|
||||
await setTimeout(100);
|
||||
performance.mark('trace-measure-end');
|
||||
performance.measure('test-trace-measure', 'trace-measure-start', 'trace-measure-end');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const measureEvents = parsed.traceEvents.filter(
|
||||
(x: any) => x.cat === 'node.perf.usertiming' && x.name === 'test-trace-measure'
|
||||
);
|
||||
expect(measureEvents.some((x: any) => x.ph === 'b')).to.be.true('should have nestable async begin (b) event');
|
||||
expect(measureEvents.some((x: any) => x.ph === 'e')).to.be.true('should have nestable async end (e) event');
|
||||
});
|
||||
|
||||
it('captures node.fs.sync trace events for file operations', async function () {
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.fs.sync']
|
||||
});
|
||||
|
||||
fs.readFileSync(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const fsEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.sync')
|
||||
);
|
||||
expect(fsEvents).to.have.lengthOf.at.least(1, 'should have node.fs.sync trace events');
|
||||
});
|
||||
|
||||
it('captures multiple node categories simultaneously', async function () {
|
||||
const vm = require('node:vm');
|
||||
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.async_hooks', 'node.vm.script']
|
||||
});
|
||||
|
||||
vm.runInNewContext('1 + 1');
|
||||
await fs.promises.readFile(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const asyncHooksEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.async_hooks')
|
||||
);
|
||||
const vmEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.vm.script')
|
||||
);
|
||||
expect(asyncHooksEvents).to.have.lengthOf.at.least(1, 'should have node.async_hooks events');
|
||||
expect(vmEvents).to.have.lengthOf.at.least(1, 'should have node.vm.script events');
|
||||
});
|
||||
|
||||
it('captures events using wildcard category pattern node.fs.*', async function () {
|
||||
await contentTracing.startRecording({
|
||||
included_categories: ['node.fs.*']
|
||||
});
|
||||
|
||||
fs.readFileSync(__filename, 'utf8');
|
||||
await fs.promises.readFile(__filename, 'utf8');
|
||||
|
||||
const resultPath = await contentTracing.stopRecording();
|
||||
const data = fs.readFileSync(resultPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
const syncEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.sync')
|
||||
);
|
||||
const asyncEvents = parsed.traceEvents.filter(
|
||||
(x: any) => typeof x.cat === 'string' && x.cat.includes('node.fs.async')
|
||||
);
|
||||
expect(syncEvents).to.have.lengthOf.at.least(1, 'should have node.fs.sync events from wildcard pattern');
|
||||
expect(asyncEvents).to.have.lengthOf.at.least(1, 'should have node.fs.async events from wildcard pattern');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -250,6 +250,34 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
// Regression: base::circular_deque relocates elements on growth,
|
||||
// corrupting crashpad::Annotation's self-referential pointers and
|
||||
// causing missing crash keys or a hung handler. See crash_keys.cc.
|
||||
it('does not corrupt the crashpad annotation list after deque reallocation', async function () {
|
||||
// Tight timeout so a hanging handler fails fast instead of waiting
|
||||
// for the mocha default of 120s.
|
||||
this.timeout(45000);
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer-dynamic-keys', port);
|
||||
const crash = await Promise.race([
|
||||
waitForCrash(),
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
global.setTimeout(
|
||||
() => reject(new Error('crashpad handler hung walking corrupted annotation list; crash upload did not arrive within 30s')),
|
||||
30000
|
||||
);
|
||||
})
|
||||
]);
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
const missing: string[] = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
if ((crash as any)[`dyn-key-${i}`] !== `val-${i}`) {
|
||||
missing.push(`dyn-key-${i}`);
|
||||
}
|
||||
}
|
||||
expect(missing, `missing dynamic crash keys: ${missing.join(', ')}`).to.be.empty();
|
||||
});
|
||||
|
||||
it('contains v8 crash keys when a v8 crash occurs', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
13
spec/fixtures/apps/crash/main.js
vendored
13
spec/fixtures/apps/crash/main.js
vendored
@@ -51,6 +51,19 @@ app.whenReady().then(() => {
|
||||
});
|
||||
w.loadURL(`about:blank?set_extra=${setExtraParameters ? 1 : 0}`);
|
||||
w.webContents.on('render-process-gone', () => process.exit(0));
|
||||
} else if (crashType === 'renderer-dynamic-keys') {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
w.webContents.on('render-process-gone', () => process.exit(0));
|
||||
w.webContents.on('did-finish-load', () => {
|
||||
w.webContents.executeJavaScript(`
|
||||
const { crashReporter } = require('electron');
|
||||
for (let i = 0; i < 50; i++) {
|
||||
crashReporter.addExtraParameter('dyn-key-' + i, 'val-' + i);
|
||||
}
|
||||
process.crash();
|
||||
`);
|
||||
});
|
||||
w.loadURL('about:blank');
|
||||
} else if (crashType === 'node') {
|
||||
const crashPath = path.join(__dirname, 'node-crash.js');
|
||||
const child = childProcess.fork(crashPath, { silent: true });
|
||||
|
||||
11
yarn.lock
11
yarn.lock
@@ -590,7 +590,7 @@ __metadata:
|
||||
"@types/semver": "npm:^7.5.8"
|
||||
"@types/stream-json": "npm:^1.7.8"
|
||||
"@types/temp": "npm:^0.9.4"
|
||||
"@xmldom/xmldom": "npm:^0.8.11"
|
||||
"@xmldom/xmldom": "npm:^0.8.12"
|
||||
buffer: "npm:^6.0.3"
|
||||
chalk: "npm:^4.1.0"
|
||||
check-for-leaks: "npm:^1.2.1"
|
||||
@@ -2503,7 +2503,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@xmldom/xmldom@npm:^0.8.11, @xmldom/xmldom@npm:^0.8.8":
|
||||
"@xmldom/xmldom@npm:^0.8.12":
|
||||
version: 0.8.12
|
||||
resolution: "@xmldom/xmldom@npm:0.8.12"
|
||||
checksum: 10c0/b733c84292d1bee32ef21a05aba8f9063456b51a54068d0b4a1abf5545156ee0b9894b7ae23775b5881b11c35a8a03871d1b514fb7e1b11654cdbee57e1c2707
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@xmldom/xmldom@npm:^0.8.8":
|
||||
version: 0.8.11
|
||||
resolution: "@xmldom/xmldom@npm:0.8.11"
|
||||
checksum: 10c0/e768623de72c95d3dae6b5da8e33dda0d81665047811b5498d23a328d45b13feb5536fe921d0308b96a4a8dd8addf80b1f6ef466508051c0b581e63e0dc74ed5
|
||||
|
||||
Reference in New Issue
Block a user