From d1663a5ac607b3477595d6d0b579487c0d93016d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:13:11 -0500 Subject: [PATCH 001/339] chore: emphasize documentation style guide (#45910) docs: emphasize documentation style guide Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- docs/development/api-history-migration-guide.md | 2 +- docs/{styleguide.md => development/style-guide.md} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename docs/{styleguide.md => development/style-guide.md} (98%) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79dce49f46f72..e558ae9717861 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING. - [ ] PR description included and stakeholders cc'd - [ ] `npm test` passes - [ ] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md) -- [ ] relevant documentation, tutorials, templates and examples are changed or added +- [ ] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md) - [ ] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples). #### Release Notes diff --git a/docs/development/api-history-migration-guide.md b/docs/development/api-history-migration-guide.md index f4128dbf527de..0c1ae7c54a25e 100644 --- a/docs/development/api-history-migration-guide.md +++ b/docs/development/api-history-migration-guide.md @@ -65,7 +65,7 @@ Verify that the Pull Request is correct and make a corresponding entry in the API History: > [!NOTE] -> Refer to the [API History section of `styleguide.md`](../styleguide.md#api-history) +> Refer to the [API History section of `style-guide.md`](./style-guide.md#api-history) for information on how to create API History blocks. `````markdown diff --git a/docs/styleguide.md b/docs/development/style-guide.md similarity index 98% rename from docs/styleguide.md rename to docs/development/style-guide.md index 18e5ebb98631c..cac7c7520e474 100644 --- a/docs/styleguide.md +++ b/docs/development/style-guide.md @@ -195,7 +195,7 @@ required[, optional] More detailed information on each of the arguments is noted in an unordered list below the method. The type of argument is notated by either JavaScript primitives (e.g. `string`, `Promise`, or `Object`), a custom API structure like Electron's -[`Cookie`](api/structures/cookie.md), or the wildcard `any`. +[`Cookie`](../api/structures/cookie.md), or the wildcard `any`. If the argument is of type `Array`, use `[]` shorthand with the type of value inside the array (for example,`any[]` or `string[]`). @@ -290,7 +290,7 @@ The purpose of the API History block is to describe when/where/how/why an API wa Each API change listed in the block should include a link to the PR where that change was made along with an optional short description of the change. If applicable, include the [heading id](https://gist.github.com/asabaylus/3071099) -for that change from the [breaking changes documentation](./breaking-changes.md). +for that change from the [breaking changes documentation](../breaking-changes.md). The [API History linting script][api-history-linting-script] (`lint:api-history`) validates API History blocks in the Electron documentation against the schema and From 7d0f24420fa4e9708b25bde3339f96f1d15167e7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 09:00:12 -0600 Subject: [PATCH 002/339] fix: javascript heap OOM is not raised (#45912) fix: javascript heap oom is not raised in node::OOMErrorHandler node::OOMErrorHandler terminates the process directly without raising an oom exception. To fix it, set an oom handler into node from electron. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Yang Liu --- patches/node/.patches | 1 + ...ror_callback_in_node_isolatesettings.patch | 42 +++++++++++++++++++ shell/common/node_bindings.cc | 29 +++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch diff --git a/patches/node/.patches b/patches/node/.patches index 58552a60de6f8..8d0f154143992 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -44,3 +44,4 @@ test_make_eval_snapshot_tests_more_flexible.patch build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch +feat_add_oom_error_callback_in_node_isolatesettings.patch diff --git a/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch b/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch new file mode 100644 index 0000000000000..2e9c3f9840dff --- /dev/null +++ b/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yang Liu +Date: Wed, 5 Mar 2025 17:22:39 +0800 +Subject: feat: add oom_error_callback in node::IsolateSettings + +Expose v8::OOMErrorCallback to allow setting OOM error handler outside Node.js + +As described in this issue https://github.com/electron/electron/issues/45894, +Electron fails to capture js heap oom because node::OOMErrorHandler just +terminates the process without raising an exception. + +To fix this issue, provide the interface oom_error_callback to enable a +custom oom error callback set from Electron. + +diff --git a/src/api/environment.cc b/src/api/environment.cc +index 32fc075e97eebca6c47e796ac5308915746ffa2a..e72bee385865c7d34e9eea6b90c6d911d592f8af 100644 +--- a/src/api/environment.cc ++++ b/src/api/environment.cc +@@ -241,7 +241,10 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { + auto* fatal_error_cb = s.fatal_error_callback ? + s.fatal_error_callback : OnFatalError; + isolate->SetFatalErrorHandler(fatal_error_cb); +- isolate->SetOOMErrorHandler(OOMErrorHandler); ++ ++ auto* oom_error_cb = s.oom_error_callback ? ++ s.oom_error_callback : OOMErrorHandler; ++ isolate->SetOOMErrorHandler(oom_error_cb); + + if ((s.flags & SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK) == 0) { + auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ? +diff --git a/src/node.h b/src/node.h +index afb26ec5690ccd65a3c36f8b8a1b2de416b9d843..98ad0ea649eaef43d1f5231f7bc4044e100e08d7 100644 +--- a/src/node.h ++++ b/src/node.h +@@ -489,6 +489,7 @@ struct IsolateSettings { + v8::Isolate::AbortOnUncaughtExceptionCallback + should_abort_on_uncaught_exception_callback = nullptr; + v8::FatalErrorCallback fatal_error_callback = nullptr; ++ v8::OOMErrorCallback oom_error_callback = nullptr; + v8::PrepareStackTraceCallback prepare_stack_trace_callback = nullptr; + + // Miscellaneous callbacks diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index c2dde178cf7bc..56e8bc7876740 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -10,6 +10,7 @@ #include #include +#include "base/allocator/partition_allocator/src/partition_alloc/oom.h" #include "base/base_paths.h" #include "base/command_line.h" #include "base/containers/fixed_flat_set.h" @@ -169,6 +170,33 @@ void V8FatalErrorCallback(const char* location, const char* message) { *zero = 0; } +void V8OOMErrorCallback(const char* location, const v8::OOMDetails& details) { + const char* message = + details.is_heap_oom ? "Allocation failed - JavaScript heap out of memory" + : "Allocation failed - process out of memory"; + if (location) { + LOG(ERROR) << "OOM error in V8: " << location << " " << message; + } else { + LOG(ERROR) << "OOM error in V8: " << message; + } + if (details.detail) { + LOG(ERROR) << "OOM detail: " << details.detail; + } + +#if !IS_MAS_BUILD() + electron::crash_keys::SetCrashKey("electron.v8-oom.is_heap_oom", + std::to_string(details.is_heap_oom)); + if (location) { + electron::crash_keys::SetCrashKey("electron.v8-oom.location", location); + } + if (details.detail) { + electron::crash_keys::SetCrashKey("electron.v8-oom.detail", details.detail); + } +#endif + + OOM_CRASH(0); +} + bool AllowWasmCodeGenerationCallback(v8::Local context, v8::Local source) { // If we're running with contextIsolation enabled in the renderer process, @@ -688,6 +716,7 @@ std::shared_ptr NodeBindings::CreateEnvironment( // Use a custom fatal error callback to allow us to add // crash message and location to CrashReports. is.fatal_error_callback = V8FatalErrorCallback; + is.oom_error_callback = V8OOMErrorCallback; // We don't want to abort either in the renderer or browser processes. // We already listen for uncaught exceptions and handle them there. From e74e1ab4c535960ce22de57f33af0736fe88cbfe Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:23:47 +0100 Subject: [PATCH 003/339] fix: resolve font list in default prefernce values (#45919) * fix: resolve font list in default prefernce values Co-authored-by: deepak1556 * chore: fix unsafe buffer usage Co-authored-by: deepak1556 * docs: add code comment Co-authored-by: deepak1556 --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- shell/browser/font_defaults.cc | 174 ++++++++++++++++++++++++++++++--- shell/browser/font_defaults.h | 6 ++ 2 files changed, 168 insertions(+), 12 deletions(-) diff --git a/shell/browser/font_defaults.cc b/shell/browser/font_defaults.cc index f5ebf2654ee06..884021b7d55d6 100644 --- a/shell/browser/font_defaults.cc +++ b/shell/browser/font_defaults.cc @@ -9,14 +9,101 @@ #include "base/containers/fixed_flat_map.h" #include "base/containers/map_util.h" +#include "base/strings/cstring_view.h" #include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" #include "chrome/grit/platform_locale_settings.h" #include "third_party/blink/public/common/web_preferences/web_preferences.h" +#include "third_party/icu/source/common/unicode/uchar.h" +#include "third_party/icu/source/common/unicode/uscript.h" #include "ui/base/l10n/l10n_util.h" +#if BUILDFLAG(IS_WIN) +#include +#endif + +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +// If a font name in prefs default values starts with a comma, consider it's a +// comma-separated font list and resolve it to the first available font. +#define PREFS_FONT_LIST 1 +#include "ui/gfx/font_list.h" +#else +#define PREFS_FONT_LIST 0 +#endif + namespace { +#if BUILDFLAG(IS_WIN) +// On Windows with antialiasing we want to use an alternate fixed font like +// Consolas, which looks much better than Courier New. +bool ShouldUseAlternateDefaultFixedFont(const std::string& script) { + if (!base::StartsWith(script, "courier", + base::CompareCase::INSENSITIVE_ASCII)) { + return false; + } + UINT smooth_type = 0; + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smooth_type, 0); + return smooth_type == FE_FONTSMOOTHINGCLEARTYPE; +} +#endif + +// Returns the script of the font pref |pref_name|. For example, suppose +// |pref_name| is "webkit.webprefs.fonts.serif.Hant". Since the script code for +// the script name "Hant" is USCRIPT_TRADITIONAL_HAN, the function returns +// USCRIPT_TRADITIONAL_HAN. |pref_name| must be a valid font pref name. +UScriptCode GetScriptOfFontPref(const base::cstring_view pref_name) { + // ICU script names are four letters. + static const size_t kScriptNameLength = 4; + + size_t len = pref_name.size(); + DCHECK_GT(len, kScriptNameLength); + const char* scriptName = pref_name.substr(len - kScriptNameLength).data(); + int32_t code = u_getPropertyValueEnum(UCHAR_SCRIPT, scriptName); + DCHECK(code >= 0 && code < USCRIPT_CODE_LIMIT); + return static_cast(code); +} + +// Returns the primary script used by the browser's UI locale. For example, if +// the locale is "ru", the function returns USCRIPT_CYRILLIC, and if the locale +// is "en", the function returns USCRIPT_LATIN. +UScriptCode GetScriptOfBrowserLocale(const std::string& locale) { + // For Chinese locales, uscript_getCode() just returns USCRIPT_HAN but our + // per-script fonts are for USCRIPT_SIMPLIFIED_HAN and + // USCRIPT_TRADITIONAL_HAN. + if (locale == "zh-CN") { + return USCRIPT_SIMPLIFIED_HAN; + } + if (locale == "zh-TW") { + return USCRIPT_TRADITIONAL_HAN; + } + // For Korean and Japanese, multiple scripts are returned by + // |uscript_getCode|, but we're passing a one entry buffer leading + // the buffer to be filled by USCRIPT_INVALID_CODE. We need to + // hard-code the results for them. + if (locale == "ko") { + return USCRIPT_HANGUL; + } + if (locale == "ja") { + return USCRIPT_JAPANESE; + } + + UScriptCode code = USCRIPT_INVALID_CODE; + UErrorCode err = U_ZERO_ERROR; + uscript_getCode(locale.c_str(), &code, 1, &err); + + if (U_FAILURE(err)) { + code = USCRIPT_INVALID_CODE; + } + return code; +} + +struct FontDefault { + const char* pref_name; + int resource_id; +}; + // The following list of font defaults was copied from // https://chromium.googlesource.com/chromium/src/+/69.0.3497.106/chrome/browser/ui/prefs/prefs_tab_helper.cc#152 // @@ -25,22 +112,18 @@ namespace { // // vvvvv DO NOT EDIT vvvvv -struct FontDefault { - const char* pref_name; - int resource_id; -}; - // Font pref defaults. The prefs that have defaults vary by platform, since not // all platforms have fonts for all scripts for all generic families. // TODO(falken): add proper defaults when possible for all // platforms/scripts/generic families. -const FontDefault kFontDefaults[] = { +constexpr auto kFontDefaults = std::to_array({ {prefs::kWebKitStandardFontFamily, IDS_STANDARD_FONT_FAMILY}, {prefs::kWebKitFixedFontFamily, IDS_FIXED_FONT_FAMILY}, {prefs::kWebKitSerifFontFamily, IDS_SERIF_FONT_FAMILY}, {prefs::kWebKitSansSerifFontFamily, IDS_SANS_SERIF_FONT_FAMILY}, {prefs::kWebKitCursiveFontFamily, IDS_CURSIVE_FONT_FAMILY}, {prefs::kWebKitFantasyFontFamily, IDS_FANTASY_FONT_FAMILY}, + {prefs::kWebKitMathFontFamily, IDS_MATH_FONT_FAMILY}, #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) {prefs::kWebKitStandardFontFamilyJapanese, IDS_STANDARD_FONT_FAMILY_JAPANESE}, @@ -102,7 +185,7 @@ const FontDefault kFontDefaults[] = { {prefs::kWebKitFixedFontFamilyTraditionalHan, IDS_FIXED_FONT_FAMILY_TRADITIONAL_HAN}, #endif -}; +}); // ^^^^^ DO NOT EDIT ^^^^^ @@ -125,6 +208,9 @@ auto MakeDefaultFontCopier() { WP defaults; + std::set fonts_with_defaults; + UScriptCode browser_script = + GetScriptOfBrowserLocale(g_browser_process->GetApplicationLocale()); // Populate `defaults`'s ScriptFontFamilyMaps with the values from // the kFontDefaults array in the "DO NOT EDIT" section of this file. // @@ -133,11 +219,75 @@ auto MakeDefaultFontCopier() { // "webkit.webprefs.fonts.fixed.Zyyy" splits into family name // "webkit.webprefs.fonts.fixed" and script "Zyyy". (Yes, "Zyyy" is real. // See pref_font_script_names-inl.h for the full list :) - for (const auto& [pref_name, resource_id] : kFontDefaults) { - const auto [family, script] = *base::RSplitStringOnce(pref_name, '.'); - if (auto* family_map_ptr = base::FindOrNull(FamilyMapByName, family)) { - FamilyMap& family_map = defaults.**family_map_ptr; - family_map[std::string{script}] = l10n_util::GetStringUTF16(resource_id); + for (auto [pref_name, resource_id] : kFontDefaults) { +#if BUILDFLAG(IS_WIN) + if (pref_name == prefs::kWebKitFixedFontFamily) { + if (ShouldUseAlternateDefaultFixedFont( + l10n_util::GetStringUTF8(resource_id))) { + resource_id = IDS_FIXED_FONT_FAMILY_ALT_WIN; + } + } +#endif + UScriptCode pref_script = + GetScriptOfFontPref(UNSAFE_BUFFERS(base::cstring_view(pref_name))); + // Suppress this default font pref value if it is for the primary script of + // the browser's UI locale. For example, if the pref is for the sans-serif + // font for the Cyrillic script, and the browser locale is "ru" (Russian), + // the default is suppressed. Otherwise, the default would override the + // user's font preferences when viewing pages in their native language. + // This is because users have no way yet of customizing their per-script + // font preferences. The font prefs accessible in the options UI are for + // the default, unknown script; these prefs have less priority than the + // per-script font prefs when the script of the content is known. This code + // can possibly be removed later if users can easily access per-script font + // prefs (e.g., via the extensions workflow), or the problem turns out to + // not be really critical after all. + if (browser_script != pref_script) { + std::string value = l10n_util::GetStringUTF8(resource_id); +#if PREFS_FONT_LIST + if (value.starts_with(',')) { + value = gfx::FontList::FirstAvailableOrFirst(value); + } +#else // !PREFS_FONT_LIST + DCHECK(!value.starts_with(',')) + << "This platform doesn't support default font lists. " << pref_name + << "=" << value; +#endif // PREFS_FONT_LIST + const auto [family, script] = *base::RSplitStringOnce(pref_name, '.'); + if (auto* family_map_ptr = base::FindOrNull(FamilyMapByName, family)) { + FamilyMap& family_map = defaults.**family_map_ptr; + family_map[std::string{script}] = base::UTF8ToUTF16(value); + } + fonts_with_defaults.insert(pref_name); + } + } + + // Expand the font concatenated with script name so this stays at RO memory + // rather than allocated in heap. + // clang-format off + static const auto kFontFamilyMap = std::to_array({ + #define EXPAND_SCRIPT_FONT(map_name, script_name) map_name "." script_name, + + #include "chrome/common/pref_font_script_names-inl.h" + ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_CURSIVE) + ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_FIXED) + ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_SANSERIF) + ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_SERIF) + ALL_FONT_SCRIPTS(WEBKIT_WEBPREFS_FONTS_STANDARD) + + #undef EXPAND_SCRIPT_FONT + }); + // clang-format on + + for (const char* const pref_name : kFontFamilyMap) { + if (fonts_with_defaults.find(pref_name) == fonts_with_defaults.end()) { + // We haven't already set a default value for this font preference, so set + // an empty string as the default. + const auto [family, script] = *base::RSplitStringOnce(pref_name, '.'); + if (auto* family_map_ptr = base::FindOrNull(FamilyMapByName, family)) { + FamilyMap& family_map = defaults.**family_map_ptr; + family_map[std::string{script}] = std::u16string(); + } } } diff --git a/shell/browser/font_defaults.h b/shell/browser/font_defaults.h index 6d317b0e53377..d168f3b336791 100644 --- a/shell/browser/font_defaults.h +++ b/shell/browser/font_defaults.h @@ -13,6 +13,12 @@ struct WebPreferences; namespace electron { +// Set the default font preferences. The functionality is copied from +// chrome/browser/prefs_tab_helper.cc with modifications to work +// without a preference service and cache chrome/browser/font_family_cache.cc +// that persists across app sessions. +// Keep the core logic in sync to avoid performance regressions +// Refs https://issues.chromium.org/issues/400473071 void SetFontDefaults(blink::web_pref::WebPreferences* prefs); } // namespace electron From daa37826f8332b8d980ceaaa3f61105d965ed56e Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sun, 9 Mar 2025 16:26:21 -0700 Subject: [PATCH 004/339] chore: cherry-pick 9dacf5694dfd from chromium (#45937) --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-9dacf5694dfd.patch | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 patches/chromium/cherry-pick-9dacf5694dfd.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 6464dce67d2ee..8df9952e0a462 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -141,3 +141,4 @@ feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch +cherry-pick-9dacf5694dfd.patch diff --git a/patches/chromium/cherry-pick-9dacf5694dfd.patch b/patches/chromium/cherry-pick-9dacf5694dfd.patch new file mode 100644 index 0000000000000..a67a1cbf988ed --- /dev/null +++ b/patches/chromium/cherry-pick-9dacf5694dfd.patch @@ -0,0 +1,95 @@ +From 9dacf5694dfdb735c335805783840584a50bface Mon Sep 17 00:00:00 2001 +From: Geoff Lang +Date: Thu, 06 Mar 2025 16:02:41 -0800 +Subject: [PATCH] Move WebGL primitive restart state setting to the GPU process. + +ANGLE will validate and initialize this state and errors are generated +when the WebGL client also initializes it on startup. + +Initialize it even in the passthrough command decoder temporarily so +that ANGLE can roll without breaking WebGL tests. + +Bug: 401059730 +Change-Id: I0bfee710673bbcea6f915ffc4fc9be20438a2654 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6330188 +Auto-Submit: Geoff Lang +Commit-Queue: Kenneth Russell +Reviewed-by: Kenneth Russell +Cr-Commit-Position: refs/heads/main@{#1429228} +--- + +diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc +index d835b1f..0eedac0 100644 +--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc ++++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc +@@ -3272,6 +3272,13 @@ + } + } + ++ if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL2) { ++ // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. ++ // See the section in WebGL 2 spec: ++ // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 ++ DoEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); ++ } ++ + if (group_->gpu_preferences().enable_gpu_driver_debug_logging && + feature_info_->feature_flags().khr_debug) { + InitializeGLDebugLogging(true, GLDebugMessageCallback, &logger_); +diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +index 3ccdebc1..ad23480 100644 +--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc ++++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +@@ -1064,6 +1064,17 @@ + api()->glDisableFn(GL_TEXTURE_RECTANGLE_ANGLE); + #endif + ++ // TEMPORARY: Set primitive restart to enabled by default for WebGL2. Clear ++ // errors afterwards so that when this state is initialized and validated in ++ // ANGLE, it will not generate errors during command buffer initialization. ++ if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL2) { ++ // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. ++ // See the section in WebGL 2 spec: ++ // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 ++ api()->glEnableFn(GL_PRIMITIVE_RESTART_FIXED_INDEX); ++ CheckErrorCallbackState(); ++ } ++ + // Register this object as a GPU switching observer. + if (feature_info_->IsWebGLContext()) { + ui::GpuSwitchingManager::GetInstance()->AddObserver(this); +diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +index 8e898bd..6030000 100644 +--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc ++++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +@@ -439,6 +439,13 @@ + } + #endif + ++ if (init.context_type == CONTEXT_TYPE_WEBGL2 && ++ group_->feature_info()->gl_version_info().is_es3) { ++ EXPECT_CALL(*gl_, Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX)) ++ .Times(1) ++ .RetiresOnSaturation(); ++ } ++ + if (context_->HasRobustness()) { + EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()) + .WillOnce(Return(init.lose_context_on_init ? GL_GUILTY_CONTEXT_RESET_ARB +diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +index 79597c8..7c42b98 100644 +--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc ++++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +@@ -1433,12 +1433,6 @@ + WTF::BindRepeating(&WebGLRenderingContextBase::OnErrorMessage, + WrapWeakPersistent(this))); + +- // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. +- // See the section in WebGL 2 spec: +- // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 +- if (IsWebGL2()) +- ContextGL()->Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX); +- + // This ensures that the context has a valid "lastFlushID" and won't be + // mistakenly identified as the "least recently used" context. + ContextGL()->Flush(); From d41ed8a11855e534b719343ded5290f72a3740b4 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sun, 9 Mar 2025 16:27:26 -0700 Subject: [PATCH 005/339] chore: cherry-pick 521faebc8a7c from chromium (#45942) --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-521faebc8a7c.patch | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 patches/chromium/cherry-pick-521faebc8a7c.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 8df9952e0a462..05052ba8fd5bb 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -141,4 +141,5 @@ feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch +cherry-pick-521faebc8a7c.patch cherry-pick-9dacf5694dfd.patch diff --git a/patches/chromium/cherry-pick-521faebc8a7c.patch b/patches/chromium/cherry-pick-521faebc8a7c.patch new file mode 100644 index 0000000000000..bae1cb95eb56f --- /dev/null +++ b/patches/chromium/cherry-pick-521faebc8a7c.patch @@ -0,0 +1,33 @@ +From 521faebc8a7cffe23177c6600bfcfb3c0b9ab1dc Mon Sep 17 00:00:00 2001 +From: Geoff Lang +Date: Thu, 06 Mar 2025 19:39:37 -0800 +Subject: [PATCH] Disable setting primtive restart for WebGL in the cmd decoder. + +Until it's blocked in ANGLE for WebGL contexts, disable it in the +command decoder on the service side. + +Bug: 401059730 +Change-Id: Ia9c7d951cbd122454afec2f884968e0a709cee77 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6334632 +Reviewed-by: Shahbaz Youssefi +Reviewed-by: Kenneth Russell +Commit-Queue: Kenneth Russell +Cr-Commit-Position: refs/heads/main@{#1429307} +--- + +diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +index ad23480..733c553 100644 +--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc ++++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +@@ -2170,6 +2170,11 @@ + case GL_DEBUG_OUTPUT: + return true; + ++ case GL_PRIMITIVE_RESTART_FIXED_INDEX: ++ // Disable setting primitive restart at the command decoder level until ++ // it's blocked in ANGLE for WebGL contexts. ++ return feature_info_->IsWebGLContext(); ++ + default: + return false; + } From f90de88c633c9e53b7d879cffff9e1cf40f6fd2a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:24:36 -0500 Subject: [PATCH 006/339] perf: prefer `base::SplitStringPiece()` over `base::SplitString()` (#45947) * perf: use base::SplitStringPiece() in SetNodeOptions() Co-authored-by: Charles Kerr * perf: use base::SplitStringPiece() in StringToAccelerator() Co-authored-by: Charles Kerr * refactor: StringToAccelerator() now takes a std::string_view Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/ui/accelerator_util.cc | 7 ++++--- shell/browser/ui/accelerator_util.h | 4 ++-- shell/common/node_bindings.cc | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/shell/browser/ui/accelerator_util.cc b/shell/browser/ui/accelerator_util.cc index fa80d725b3642..70954cd80c523 100644 --- a/shell/browser/ui/accelerator_util.cc +++ b/shell/browser/ui/accelerator_util.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include "base/logging.h" @@ -16,7 +17,7 @@ namespace accelerator_util { -bool StringToAccelerator(const std::string& shortcut, +bool StringToAccelerator(const std::string_view shortcut, ui::Accelerator* accelerator) { if (!base::IsStringASCII(shortcut)) { LOG(ERROR) << "The accelerator string can only contain ASCII characters, " @@ -26,14 +27,14 @@ bool StringToAccelerator(const std::string& shortcut, return false; } - std::vector tokens = base::SplitString( + const std::vector tokens = base::SplitStringPiece( shortcut, "+", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); // Now, parse it into an accelerator. int modifiers = ui::EF_NONE; ui::KeyboardCode key = ui::VKEY_UNKNOWN; std::optional shifted_char; - for (const auto& token : tokens) { + for (const std::string_view token : tokens) { ui::KeyboardCode code = electron::KeyboardCodeFromStr(token, &shifted_char); if (shifted_char) modifiers |= ui::EF_SHIFT_DOWN; diff --git a/shell/browser/ui/accelerator_util.h b/shell/browser/ui/accelerator_util.h index 3e88451a2b737..0fe816aa48a06 100644 --- a/shell/browser/ui/accelerator_util.h +++ b/shell/browser/ui/accelerator_util.h @@ -6,7 +6,7 @@ #define ELECTRON_SHELL_BROWSER_UI_ACCELERATOR_UTIL_H_ #include -#include +#include #include "base/memory/raw_ptr.h" #include "shell/browser/ui/electron_menu_model.h" @@ -21,7 +21,7 @@ typedef struct { typedef std::map AcceleratorTable; // Parse a string as an accelerator. -bool StringToAccelerator(const std::string& shortcut, +bool StringToAccelerator(std::string_view shortcut, ui::Accelerator* accelerator); // Generate a table that contains menu model's accelerators and command ids. diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 56e8bc7876740..423aafae6eb25 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -395,14 +395,14 @@ void SetNodeOptions(base::Environment* env) { if (electron::fuses::IsNodeOptionsEnabled()) { std::string options; env->GetVar("NODE_OPTIONS", &options); - std::vector parts = base::SplitString( + const std::vector parts = base::SplitStringPiece( options, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); bool is_packaged_app = electron::api::App::IsPackaged(); - for (const auto& part : parts) { + for (const std::string_view part : parts) { // Strip off values passed to individual NODE_OPTIONs - std::string option = part.substr(0, part.find('=')); + const std::string_view option = part.substr(0, part.find('=')); if (is_packaged_app && !pkg_opts.contains(option)) { // Explicitly disallow majority of NODE_OPTIONS in packaged apps From ca91235c51cf9cf6a7a3ae770c2594cf9f92c77c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:25:03 -0500 Subject: [PATCH 007/339] refactor: eliminate duplicate code in spec/api-process-spec.ts (#45952) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Milan Burda --- spec/api-process-spec.ts | 135 ++++++++------------------------------- 1 file changed, 27 insertions(+), 108 deletions(-) diff --git a/spec/api-process-spec.ts b/spec/api-process-spec.ts index 156bb6b410026..81e045a0acfec 100644 --- a/spec/api-process-spec.ts +++ b/spec/api-process-spec.ts @@ -10,24 +10,17 @@ import { defer } from './lib/spec-helpers'; import { closeAllWindows } from './lib/window-helpers'; describe('process module', () => { - describe('renderer process', () => { - let w: BrowserWindow; - before(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); - await w.loadURL('about:blank'); - }); - after(closeAllWindows); - + function generateSpecs (invoke: any>(fn: T, ...args: Parameters) => Promise>) { describe('process.getCreationTime()', () => { it('returns a creation time', async () => { - const creationTime = await w.webContents.executeJavaScript('process.getCreationTime()'); + const creationTime = await invoke(() => process.getCreationTime()); expect(creationTime).to.be.a('number').and.be.at.least(0); }); }); describe('process.getCPUUsage()', () => { it('returns a cpu usage object', async () => { - const cpuUsage = await w.webContents.executeJavaScript('process.getCPUUsage()'); + const cpuUsage = await invoke(() => process.getCPUUsage()); expect(cpuUsage.percentCPUUsage).to.be.a('number'); expect(cpuUsage.cumulativeCPUUsage).to.be.a('number'); expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number'); @@ -36,7 +29,7 @@ describe('process module', () => { describe('process.getBlinkMemoryInfo()', () => { it('returns blink memory information object', async () => { - const heapStats = await w.webContents.executeJavaScript('process.getBlinkMemoryInfo()'); + const heapStats = await invoke(() => process.getBlinkMemoryInfo()); expect(heapStats.allocated).to.be.a('number'); expect(heapStats.total).to.be.a('number'); }); @@ -44,7 +37,7 @@ describe('process module', () => { describe('process.getProcessMemoryInfo()', () => { it('resolves promise successfully with valid data', async () => { - const memoryInfo = await w.webContents.executeJavaScript('process.getProcessMemoryInfo()'); + const memoryInfo = await invoke(() => process.getProcessMemoryInfo()); expect(memoryInfo).to.be.an('object'); if (process.platform === 'linux' || process.platform === 'win32') { expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0); @@ -57,7 +50,7 @@ describe('process module', () => { describe('process.getSystemMemoryInfo()', () => { it('returns system memory info object', async () => { - const systemMemoryInfo = await w.webContents.executeJavaScript('process.getSystemMemoryInfo()'); + const systemMemoryInfo = await invoke(() => process.getSystemMemoryInfo()); expect(systemMemoryInfo.free).to.be.a('number'); expect(systemMemoryInfo.total).to.be.a('number'); }); @@ -65,14 +58,14 @@ describe('process module', () => { describe('process.getSystemVersion()', () => { it('returns a string', async () => { - const systemVersion = await w.webContents.executeJavaScript('process.getSystemVersion()'); + const systemVersion = await invoke(() => process.getSystemVersion()); expect(systemVersion).to.be.a('string'); }); }); describe('process.getHeapStatistics()', () => { it('returns heap statistics object', async () => { - const heapStats = await w.webContents.executeJavaScript('process.getHeapStatistics()'); + const heapStats = await invoke(() => process.getHeapStatistics()); expect(heapStats.totalHeapSize).to.be.a('number'); expect(heapStats.totalHeapSizeExecutable).to.be.a('number'); expect(heapStats.totalPhysicalSize).to.be.a('number'); @@ -86,6 +79,8 @@ describe('process module', () => { }); describe('process.takeHeapSnapshot()', () => { + // DISABLED-FIXME(nornagon): this seems to take a really long time when run in the + // main process, for unknown reasons. it('returns true on success', async () => { const filePath = path.join(app.getPath('temp'), 'test.heapsnapshot'); defer(() => { @@ -96,17 +91,31 @@ describe('process module', () => { } }); - const success = await w.webContents.executeJavaScript(`process.takeHeapSnapshot(${JSON.stringify(filePath)})`); + const success = await invoke((filePath: string) => process.takeHeapSnapshot(filePath), filePath); expect(success).to.be.true(); const stats = fs.statSync(filePath); expect(stats.size).not.to.be.equal(0); }); it('returns false on failure', async () => { - const success = await w.webContents.executeJavaScript('process.takeHeapSnapshot("")'); + const success = await invoke((filePath: string) => process.takeHeapSnapshot(filePath), ''); expect(success).to.be.false(); }); }); + } + + describe('renderer process', () => { + let w: BrowserWindow; + before(async () => { + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); + await w.loadURL('about:blank'); + }); + after(closeAllWindows); + + generateSpecs((fn, ...args) => { + const jsonArgs = args.map(value => JSON.stringify(value)).join(','); + return w.webContents.executeJavaScript(`(${fn.toString()})(${jsonArgs})`); + }); describe('process.contextId', () => { it('is a string', async () => { @@ -117,96 +126,6 @@ describe('process module', () => { }); describe('main process', () => { - describe('process.getCreationTime()', () => { - it('returns a creation time', () => { - const creationTime = process.getCreationTime(); - expect(creationTime).to.be.a('number').and.be.at.least(0); - }); - }); - - describe('process.getCPUUsage()', () => { - it('returns a cpu usage object', () => { - const cpuUsage = process.getCPUUsage(); - expect(cpuUsage.percentCPUUsage).to.be.a('number'); - expect(cpuUsage.cumulativeCPUUsage).to.be.a('number'); - expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number'); - }); - }); - - describe('process.getBlinkMemoryInfo()', () => { - it('returns blink memory information object', () => { - const heapStats = process.getBlinkMemoryInfo(); - expect(heapStats.allocated).to.be.a('number'); - expect(heapStats.total).to.be.a('number'); - }); - }); - - describe('process.getProcessMemoryInfo()', () => { - it('resolves promise successfully with valid data', async () => { - const memoryInfo = await process.getProcessMemoryInfo(); - expect(memoryInfo).to.be.an('object'); - if (process.platform === 'linux' || process.platform === 'win32') { - expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0); - } - expect(memoryInfo.private).to.be.a('number').greaterThan(0); - // Shared bytes can be zero - expect(memoryInfo.shared).to.be.a('number').greaterThan(-1); - }); - }); - - describe('process.getSystemMemoryInfo()', () => { - it('returns system memory info object', () => { - const systemMemoryInfo = process.getSystemMemoryInfo(); - expect(systemMemoryInfo.free).to.be.a('number'); - expect(systemMemoryInfo.total).to.be.a('number'); - }); - }); - - describe('process.getSystemVersion()', () => { - it('returns a string', () => { - const systemVersion = process.getSystemVersion(); - expect(systemVersion).to.be.a('string'); - }); - }); - - describe('process.getHeapStatistics()', () => { - it('returns heap statistics object', async () => { - const heapStats = process.getHeapStatistics(); - expect(heapStats.totalHeapSize).to.be.a('number'); - expect(heapStats.totalHeapSizeExecutable).to.be.a('number'); - expect(heapStats.totalPhysicalSize).to.be.a('number'); - expect(heapStats.totalAvailableSize).to.be.a('number'); - expect(heapStats.usedHeapSize).to.be.a('number'); - expect(heapStats.heapSizeLimit).to.be.a('number'); - expect(heapStats.mallocedMemory).to.be.a('number'); - expect(heapStats.peakMallocedMemory).to.be.a('number'); - expect(heapStats.doesZapGarbage).to.be.a('boolean'); - }); - }); - - describe('process.takeHeapSnapshot()', () => { - // DISABLED-FIXME(nornagon): this seems to take a really long time when run in the - // main process, for unknown reasons. - it('returns true on success', () => { - const filePath = path.join(app.getPath('temp'), 'test.heapsnapshot'); - defer(() => { - try { - fs.unlinkSync(filePath); - } catch { - // ignore error - } - }); - - const success = process.takeHeapSnapshot(filePath); - expect(success).to.be.true(); - const stats = fs.statSync(filePath); - expect(stats.size).not.to.be.equal(0); - }); - - it('returns false on failure', async () => { - const success = process.takeHeapSnapshot(''); - expect(success).to.be.false(); - }); - }); + generateSpecs((fn, ...args) => fn(...args)); }); }); From 0e8300399dc0d21f4cc9ae7acbf816bf66a83bcf Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:49:32 -0500 Subject: [PATCH 008/339] fix: race condition in utilityProcess tests (#45956) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/api-utility-process-spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts index 5e6d4fe0380ce..aa75aedda8671 100644 --- a/spec/api-utility-process-spec.ts +++ b/spec/api-utility-process-spec.ts @@ -228,7 +228,6 @@ describe('utilityProcess module', () => { const child = utilityProcess.fork(fixtureFile, [], { stdio: 'pipe' }); - await once(child, 'spawn'); expect(child.stdout).to.not.be.null(); let log = ''; child.stdout!.on('data', (chunk) => { @@ -293,7 +292,6 @@ describe('utilityProcess module', () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: 'pipe' }); - await once(child, 'spawn'); expect(child.stdout).to.not.be.null(); let log = ''; child.stdout!.on('data', (chunk) => { @@ -326,7 +324,6 @@ describe('utilityProcess module', () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: ['ignore', 'pipe', 'pipe'] }); - await once(child, 'spawn'); expect(child.stderr).to.not.be.null(); let log = ''; child.stderr!.on('data', (chunk) => { From c39683b2cbdbcec6f1460e40bbd4534fc3034dc4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 22:29:39 +0100 Subject: [PATCH 009/339] fix: remove redundant `MediaCaptureDevicesDispatcher::GetInstance()` call (#45961) fix: remove redundant MediaCaptureDevicesDispatcher::GetInstance() call This appears to be a copy-paste error introduced in 465dee2c Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_main_parts.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index 66c3cef19ed88..dfdec38f16096 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -344,9 +344,6 @@ int ElectronBrowserMainParts::PreCreateThreads() { // Force MediaCaptureDevicesDispatcher to be created on UI thread. MediaCaptureDevicesDispatcher::GetInstance(); - // Force MediaCaptureDevicesDispatcher to be created on UI thread. - MediaCaptureDevicesDispatcher::GetInstance(); - #if BUILDFLAG(IS_MAC) ui::InitIdleMonitor(); Browser::Get()->ApplyForcedRTL(); From 9f0fb54918840df97cf057efba5089ea877a1c35 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 16:45:26 -0500 Subject: [PATCH 010/339] docs: Add 'Native Code and Electron' (#45968) * docs: Add 'Native Code and Electron' Co-authored-by: Felix Rieseberg * docs: Add node-gyp requirements Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Cross-platform clean command Co-authored-by: Felix Rieseberg * Mention napi-rs Co-authored-by: Felix Rieseberg * Apply suggestions from code review Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Fix lint, fix more comments Co-authored-by: Felix Rieseberg * Apply suggestions from code review Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Felix Rieseberg --- docs/tutorial/native-code-and-electron.md | 380 ++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 docs/tutorial/native-code-and-electron.md diff --git a/docs/tutorial/native-code-and-electron.md b/docs/tutorial/native-code-and-electron.md new file mode 100644 index 0000000000000..70ca945db4e77 --- /dev/null +++ b/docs/tutorial/native-code-and-electron.md @@ -0,0 +1,380 @@ +# Native Code and Electron + +One of Electron's most powerful features is the ability to combine web technologies with native code - both for compute-intensive logic as well as for the occasional native user interface, where desired. + +Electron does so by building on top of "Native Node.js Addons". You've probably already come across a few of them - packages like the famous [sqlite](https://www.npmjs.com/package/sqlite3) use native code to combine JavaScript and native technologies. You can use this feature to extend your Electron application with anything a fully native application can do: + +* Access native platform APIs not available in JavaScript. Any macOS, Windows, or Linux operating system API is available to you. +* Create UI components that interact with native desktop frameworks. +* Integrate with existing native libraries. +* Implement performance-critical code that runs faster than JavaScript. + +Native Node.js addons are dynamically-linked shared objects (on Unix-like systems) or DLL files (on Windows) that can be loaded into Node.js or Electron using the `require()` or `import` functions. They behave just like regular JavaScript modules but provide an interface to code written in C++, Rust, or other languages that can compile to native code. + +# Tutorial: Creating a Native Node.js Addon for Electron + +This tutorial will walk you through building a basic Node.js native addon that can be used in Electron applications. We'll focus on concepts common to all platforms, using C++ as the implementation language. Once you complete this tutorial common to all native Node.js addons, you can move on to one of our platform-specific tutorials. + +## Requirements + +This tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling code on your platform (like Visual Studio on Windows, Xcode on macOS, or GCC/Clang on Linux). You can find detailed instructions in the [`node-gyp` readme](https://github.com/nodejs/node-gyp?tab=readme-ov-file). + +### Requirements: macOS + +To build native Node.js addons on macOS, you'll need the Xcode Command Line Tools. These provide the necessary compilers and build tools (namely, `clang`, `clang++`, and `make`). The following command will prompt you to install the Command Line Tools if they aren't already installed. + +```sh +xcode-select --install +``` + +### Requirements: Windows + +The official Node.js installer offers the optional installation of "Tools for Native Modules", which installs everything required for the basic compilation of C++ modules - specifically, Python 3 and the "Visual Studio Desktop development with C++" workload. Alternatively, you can use `chocolatey`, `winget`, or the Windows Store. + +### Requirements: Linux + +* [A supported version of Python](https://devguide.python.org/versions/) +* `make` +* A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) + +## 1) Creating a package + +First, create a new Node.js package that will contain your native addon: + +```sh +mkdir my-native-addon +cd my-native-addon +npm init -y +``` + +This creates a basic `package.json` file. Next, we'll install the necessary dependencies: + +```sh +npm install node-addon-api bindings +``` + +* `node-addon-api`: This is a C++ wrapper for the low-level Node.js API that makes it easier to build addons. It provides a C++ object-oriented API that's more convenient and safer to use than the raw C-style API. +* `bindings`: A helper module that simplifies the process of loading your compiled native addon. It handles finding your compiled `.node` file automatically. + +Now, let's update our `package.json` to include the appropriate build scripts. We will explain what these specifically do further below. + +```json title='package.json' +{ + "name": "my-native-addon", + "version": "1.0.0", + "description": "A native addon for Electron", + "main": "js/index.js", + "scripts": { + "clean": "node -e \"require('fs').rmSync('build', { recursive: true, force: true })\"", + "build": "node-gyp configure && node-gyp build" + }, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + }, + "devDependencies": { + "node-gyp": "^11.1.0" + } +} +``` + +These scripts will: + +* `clean`: Remove the build directory, allowing for a fresh build +* `build`: Run the standard node-gyp build process to compile your addon + +## 2) Setting up the build system + +Node.js addons use a build system called `node-gyp`, which is a cross-platform command-line tool written in Node.js. It compiles native addon modules for Node.js using platform-specific build tools behind the scenes: + +* On Windows: Visual Studio +* On macOS: Xcode or command-line tools +* On Linux: GCC or similar compilers + +### Configuring `node-gyp` + +The `binding.gyp` file is a JSON-like configuration file that tells node-gyp how to build your native addon. It's similar to a make file or a project file but in a platform-independent format. Let's create a basic `binding.gyp` file: + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "my_addon", + "sources": [ + "src/my_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " + +namespace cpp_code { + // A simple function that takes a string input and returns a string + std::string hello_world(const std::string& input); +} // namespace cpp_code +``` + +The `#pragma once` directive is a header guard that prevents the file from being included multiple times in the same compilation unit. The actual function declaration is inside a namespace to avoid potential name conflicts. + +Next, let's implement the function in `src/cpp_code.cc`: + +```cpp title='src/cpp_code.cc' +#include +#include "../include/cpp_code.h" + +namespace cpp_code { + std::string hello_world(const std::string& input) { + // Simply concatenate strings and return + return "Hello from C++! You said: " + input; + } +} // namespace cpp_code +``` + +This is a simple implementation that just adds some text to the input string and returns it. + +Now, let's create the addon code that bridges our C++ code with the Node.js/JavaScript world. Create `src/my_addon.cc`: + +```cpp title='src/my_addon.cc' +#include +#include +#include "../include/cpp_code.h" + +// Create a class that will be exposed to JavaScript +class MyAddon : public Napi::ObjectWrap { +public: + // This static method defines the class for JavaScript + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + // Define the JavaScript class with method(s) + Napi::Function func = DefineClass(env, "MyAddon", { + InstanceMethod("helloWorld", &MyAddon::HelloWorld) + }); + + // Create a persistent reference to the constructor + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + // Set the constructor on the exports object + exports.Set("MyAddon", func); + return exports; + } + + // Constructor + MyAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) {} + +private: + // Method that will be exposed to JavaScript + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + // Validate arguments (expecting one string) + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + // Convert JavaScript string to C++ string + std::string input = info[0].As(); + + // Call our C++ function + std::string result = cpp_code::hello_world(input); + + // Convert C++ string back to JavaScript string and return + return Napi::String::New(env, result); + } +}; + +// Initialize the addon +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return MyAddon::Init(env, exports); +} + +// Register the initialization function +NODE_API_MODULE(my_addon, Init) +``` + +Let's break down this code: + +1. We define a `MyAddon` class that inherits from `Napi::ObjectWrap`, which handles wrapping our C++ class for JavaScript. +2. The `Init` static method: + 2.1 Defines a JavaScript class with a method called `helloWorld` + 2.2 Creates a persistent reference to the constructor (to prevent garbage collection) + 2.3 Exports the class constructor +3. The constructor simply passes its arguments to the parent class. +4. The `HelloWorld` method: + 4.1 Gets the Napi environment + 4.2 Validates input arguments (expecting a string) + 4.3 Converts the JavaScript string to a C++ string + 4.4 Calls our C++ function + 4.5 Converts the result back to a JavaScript string and returns it +5. We define an initialization function and register it with NODE_API_MODULE macro, which makes our module loadable by Node.js. + +Now, let's create a JavaScript wrapper to make the addon easier to use. Create `js/index.js`: + +```js title='js/index.js' @ts-expect-error=[5] +const EventEmitter = require('events') + +// Load the native addon using the 'bindings' module +// This will look for the compiled .node file in various places +const bindings = require('bindings') +const native = bindings('my_addon') + +// Create a nice JavaScript wrapper +class MyNativeAddon extends EventEmitter { + constructor () { + super() + + // Create an instance of our C++ class + this.addon = new native.MyAddon() + } + + // Wrap the C++ method with a nicer JavaScript API + helloWorld (input = '') { + if (typeof input !== 'string') { + throw new TypeError('Input must be a string') + } + return this.addon.helloWorld(input) + } +} + +// Export a singleton instance +if (process.platform === 'win32' || process.platform === 'darwin' || process.platform === 'linux') { + module.exports = new MyNativeAddon() +} else { + // Provide a fallback for unsupported platforms + console.warn('Native addon not supported on this platform') + + module.exports = { + helloWorld: (input) => `Hello from JS! You said: ${input}` + } +} +``` + +This JavaScript wrapper: + +1. Uses `bindings` to load our compiled native addon +1. Creates a class that extends EventEmitter (useful for future extensions that might emit events) +1. Instantiates our C++ class and provides a simpler API +1. Adds some input validation on the JavaScript side +1. Exports a singleton instance of our wrapper +1. Handles unsupported platforms gracefully + +### Building and testing the addon + +Now we can build our native addon: + +```sh +npm run build +``` + +This will run `node-gyp configure` and `node-gyp build` to compile our C++ code into a `.node` file. +Let's create a simple test script to verify everything works. Create `test.js` in the project root: + +```js title='test.js' @ts-expect-error=[2] +// Load our addon +const myAddon = require('./js') + +// Try the helloWorld function +const result = myAddon.helloWorld('This is a test') + +// Should print: "Hello from C++! You said: This is a test" +console.log(result) +``` + +Run the test: + +```sh +node test.js +``` + +If everything works correctly, you should see: + +```txt +Hello from C++! You said: This is a test +``` + +### Using the addon in Electron + +To use this addon in an Electron application, you would: + +1. Include it as a dependency in your Electron project +1. Build it targeting your specific Electron version. `electron-forge` handles this step automatically for you - for more details, see [Native Node Modules](./using-native-node-modules.md). +1. Import and use it just like any other module in a process that has Node.js enabled. + +```js @ts-expect-error=[2] +// In your main process +const myAddon = require('my-native-addon') +console.log(myAddon.helloWorld('Electron')) +``` + +## References and further learning + +Native addon development can be written in several languages beyond C++. Rust can be used with crates like [`napi-rs`](https://github.com/napi-rs/napi-rs), [`neon`](https://neon-rs.dev/), or [`node-bindgen`](https://github.com/infinyon/node-bindgen). Objective-C/Swift can be used through Objective-C++ on macOS. + +The specific implementation details differ significantly by platform, especially when accessing platform-specific APIs or UI frameworks, like Windows' Win32 API, COM components, UWP/WinRT - or macOS's Cocoa, AppKit, or ObjectiveC runtime. + +This means that you'll likely use two groups of references for your native code: First, on the Node.js side, use the [N-API documentation](https://nodejs.org/api/n-api.html) to learn about creating and exposing complex structures to JavaScript - like asynchronous thread-safe function calls or creating JavaScript-native objects (`error`, `promise`, etc). Secondly, on the side of the technology you're working with, you'll likely be looking at their lower-level documentation: + +* [Microsoft C++, C, and Assembler documentation](https://learn.microsoft.com/en-us/cpp/?view=msvc-170) +* [C++/WinRT](https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/) +* [MSVC-170 C++ Documentation](https://learn.microsoft.com/en-us/cpp/cpp/?view=msvc-170) +* [Apple Developer Documentation](https://developer.apple.com/documentation) +* [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210) From 313aee6cbbdcbf3c566a12e593180128a8b9063b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:30:45 -0400 Subject: [PATCH 011/339] test: fix timing issue in `utilityProcess` test fixtures (#45978) * fix: potential timing issue in utilityProcess test Co-authored-by: Charles Kerr * fix: potential timing issue in utilityProcess esm test Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/fixtures/api/utility-process/dns-result-order.js | 6 ++++-- spec/fixtures/api/utility-process/esm.mjs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/spec/fixtures/api/utility-process/dns-result-order.js b/spec/fixtures/api/utility-process/dns-result-order.js index 413aaec58134e..cb8a7f7f43fd2 100644 --- a/spec/fixtures/api/utility-process/dns-result-order.js +++ b/spec/fixtures/api/utility-process/dns-result-order.js @@ -1,4 +1,6 @@ const dns = require('node:dns'); -console.log(dns.getDefaultResultOrder()); -process.exit(0); +const write = (writable, chunk) => new Promise((resolve) => writable.write(chunk, resolve)); + +write(process.stdout, `${dns.getDefaultResultOrder()}\n`) + .then(() => process.exit(0)); diff --git a/spec/fixtures/api/utility-process/esm.mjs b/spec/fixtures/api/utility-process/esm.mjs index 748ec782dfc25..655de0870aaa1 100644 --- a/spec/fixtures/api/utility-process/esm.mjs +++ b/spec/fixtures/api/utility-process/esm.mjs @@ -1,2 +1,4 @@ -console.log(import.meta.url); -process.exit(0); +const write = (writable, chunk) => new Promise((resolve) => writable.write(chunk, resolve)); + +write(process.stdout, `${import.meta.url}\n`) + .then(() => process.exit(0)); From c62db7d49de912b9acc583aaf31f799612fcca40 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 09:26:50 +0100 Subject: [PATCH 012/339] build: roll sysroots to pick up glibc fix (#45984) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- script/sysroots.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/sysroots.json b/script/sysroots.json index 6dfc62e20a01b..7948eb68c101d 100644 --- a/script/sysroots.json +++ b/script/sysroots.json @@ -1,14 +1,14 @@ { "bullseye_amd64": { "Key": "20230611T210420Z-2", - "Sha256Sum": "7c93e71bf9c4cd0825aa59fb2479054d981e36ba9be34ecf4c1d73051cae40fe", + "Sha256Sum": "0be326b106f0df7b8547ec8d3b58ccb95435c8414a45cda675c4805821d4d860", "SysrootDir": "debian_bullseye_amd64-sysroot", "Tarball": "debian_bullseye_amd64_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_arm64": { "Key": "20230611T210420Z-2", - "Sha256Sum": "d649177e37aef2c043e54e670e42934f18558c0e730c1b854719ca7463e2b1eb", + "Sha256Sum": "1225cd518c1609e54033bb6a1c687875d4f4c21b2dbd5d5d81c4b5927d0fc0f1", "SysrootDir": "debian_bullseye_arm64-sysroot", "Tarball": "debian_bullseye_arm64_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" @@ -22,28 +22,28 @@ }, "bullseye_armhf": { "Key": "20230611T210420Z-2", - "Sha256Sum": "fe3b9203e30e70f533776d565501bc1e3d4ebf6eeb909b2c2bfca0df807e1be0", + "Sha256Sum": "ed5a71ce5fc6d1691817c3b203139328ee88395c9e54ff726f67c84ee1561e65", "SysrootDir": "debian_bullseye_armhf-sysroot", "Tarball": "debian_bullseye_armhf_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_i386": { "Key": "20230611T210420Z-2", - "Sha256Sum": "4efcb1870129da1ad5c3b634903a4efcc0ca9abd67dd5a993cca144ea9b5d31f", + "Sha256Sum": "57f800042b0c4bd00a8755da165823cc70decc0481b78d751924db7470af6b5e", "SysrootDir": "debian_bullseye_i386-sysroot", "Tarball": "debian_bullseye_i386_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_mips64el": { "Key": "20230611T210420Z-2", - "Sha256Sum": "c3a3bf3b0aa40ec90747c951942d40077ff6796b336818eaba6fa21564249a00", + "Sha256Sum": "187b8644a949d0124cb00fa099a8a5842e9a88bb48d8d1c682604ebf546796b7", "SysrootDir": "debian_bullseye_mips64el-sysroot", "Tarball": "debian_bullseye_mips64el_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_mipsel": { "Key": "20230611T210420Z-2", - "Sha256Sum": "564de884ed1810e1cf3a20d94edfa21972ac2be9568bf1526d093c31f15ef225", + "Sha256Sum": "f08771dc7a813e7f0fd540b49a1b611416979630b0009e9ecc51f999a7543081", "SysrootDir": "debian_bullseye_mipsel-sysroot", "Tarball": "debian_bullseye_mipsel_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" From 98adbbb593e54d8b1dfcbe359b0719eb729dfb44 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Wed, 12 Mar 2025 09:37:36 -0400 Subject: [PATCH 013/339] chore: bump chromium to 136.0.7062.0 (#45987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: bump chromium to 135.0.7049.7 (main) (#45900) chore: bump chromium in DEPS to 135.0.7049.7 (cherry picked from commit bb1c3dff21af48979799377086fd3d36d694a385) * chore: bump chromium to 136.0.7053.1 (main) (#45906) * chore: bump chromium in DEPS to 136.0.7052.0 * chore: update mas_avoid_private_macos_api_usage.patch.patch https://chromium-review.googlesource.com/c/chromium/src/+/6318359 patch applied manually due to context shear * chore: update preconnect_manager.patch Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6318420 patch applied manually due to context shear * chore: e patches all * chore: bump chromium to 136.0.7053.1 * chore: update fix_remove_profiles_from_spellcheck_service.patch Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6326575 patch applied manually due to context shear * chore: e patches all * chore: revert removal of v8 API used by Node.js * devtools: Remove DevToolsUIBindings::SendJsonRequest() | https://chromium-review.googlesource.com/c/chromium/src/+/6326236 * 6244461: Merge //content/common/user_agent.cc into //components/embedder_support:user_agent | https://chromium-review.googlesource.com/c/chromium/src/+/6244461 * 6313744: Migrate views::Background factory methods to ColorVariant | https://chromium-review.googlesource.com/c/chromium/src/+/6313744 * 6314545: Remove multiple argument support from base::ToString() | https://chromium-review.googlesource.com/c/chromium/src/+/6314545 * 6317362: [Extensions] Inline MessagingDelegate::CreateReceiverForTab() | https://chromium-review.googlesource.com/c/chromium/src/+/6317362 * 6308998: Add SettingAccess structured metrics event for DevTools | https://chromium-review.googlesource.com/c/chromium/src/+/6308998 * 6295214: Remove redundant state field in per-extension preferences | https://chromium-review.googlesource.com/c/chromium/src/+/6295214 NB: this change is copied from the upstream change to extensions/shell/browser/shell_extension_loader.cc * fix: ui/ linter error This is showing up in an eslint build step in Electron: > /__w/electron/electron/src/out/Default/gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts > 77:23 error This assertion is unnecessary since the receiver accepts the original type of the expression @typescript-eslint/no-unnecessary-type-assertion > > ✖ 1 problem (1 error, 0 warnings) > 1 error and 0 warnings potentially fixable with the `--fix` option. However, removing the assertion causes a typescript build failure: > gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts:77:23 - error TS2345: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. > Type 'null' is not assignable to type 'HTMLElement'. > > 77 items.indexOf(this.querySelector(':focus')); > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ So I think the two different steps may be picking up typescript definitions. This patch should be removed after the issue is tracked down and fixed in a followup task. * fix: -Wnonnull warning Fixes this warning: > 2025-03-07T01:05:01.8637705Z ../../third_party/electron_node/src/debug_utils.cc(257,12): error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] > 2025-03-07T01:05:01.8638267Z 257 | return nullptr; > 2025-03-07T01:05:01.8638481Z | ^~~~~~~ > 2025-03-07T01:05:01.8638700Z 1 error generated. Not sure why this warning was never triggered before; `git blame` indicates this code hasn't changed in ages: > c40a8273ef2 (Michaël Zasso 2024-05-10 09:50:20 +0200 255) #endif // DEBUG > 8e2d33f1562 (Anna Henningsen 2018-06-07 16:54:29 +0200 256) } > 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 257) return nullptr; > 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 258) } Presumably this is failing in this Chromium roll due to a clang version bump. We should remove this patch after upstreaming it. * docs: add upstream pr link for Node patch --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr (cherry picked from commit 458b14b8ed819fcf6bc20e9b42c4dca1c323e626) * chore!: bump chromium to 136.0.7054.0 (main) (#45923) * chore: bump chromium in DEPS to 136.0.7054.0 * chore: update allow_in-process_windows_to_have_different_web_prefs.patch Xref: https://chromium-review.googlesource.com/c/chromium/src/+/5906158 patch applied manually due to context shear * chore: e patches all * refactor!: Session.clearStorageData(syncable) Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6309405 Remove syncable type from opts.quota in Session.clearStorageData(opts) because it that category has been removed upstream. BREAKING CHANGE: Removed ses.clearDataStorage({ quota: 'syncable' }) * docs: deprecate Session.clearDataStorage({ quota }) --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr (cherry picked from commit 20414f66cad2d64f395435c4c52007b6f534c228) * chore: bump chromium to 136.0.7058.1 (main) (#45928) * chore: bump chromium in DEPS to 136.0.7056.0 * chore: update mas_avoid_private_macos_api_usage.patch.patch no manual changes; patch applied with fuzz * chore: update fix_adapt_exclusive_access_for_electron_needs.patch patch applied manually due to context shear 6319958: [FS] Replace GURL with url::Origin for Excluisve Access Bubble | https://chromium-review.googlesource.com/c/chromium/src/+/6319958 * chore: update feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch no manual changes; patch applied with fuzz 6311876: Expose captured surface resolution for MacOS | https://chromium-review.googlesource.com/c/chromium/src/+/6311876 * chore: e patches all * 6319958: [FS] Replace GURL with url::Origin for Excluisve Access Bubble | https://chromium-review.googlesource.com/c/chromium/src/+/6319958 * 6326673: views: Delete the single-parameter Widget::InitParams constructor. | https://chromium-review.googlesource.com/c/chromium/src/+/6326673 * https://chromium-review.googlesource.com/c/chromium/src/+/6331102 * 6331102: [A11yPerformance] Rename AXMode::kScreenReader to kExtendedProperties | https://chromium-review.googlesource.com/c/chromium/src/+/6331102 Sync with shell/browser/ui/webui/accessibility_ui.cc to upstream chrome/browser/accessibility/accessibility_ui.cc changes in 4af8657 * chore: bump Chromium 136.0.7058.1 (#45933) chore: bump chromium in DEPS to 136.0.7058.1 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr (cherry picked from commit b0c11371e0bb37fc1e1eb69e23c218493be54823) * chore: bump chromium to 136.0.7062.0 (main) (#45957) * chore: bump chromium in DEPS to 136.0.7059.0 * chore: bump chromium in DEPS to 136.0.7060.0 * chore: bump chromium in DEPS to 136.0.7062.0 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> (cherry picked from commit 2de8fd7d9307f3ebde45f5458434e516b0c23aea) * fixup! chore: bump chromium to 136.0.7053.1 (main) (#45906) chore: fix patch shear * chore: remove cherry-pick-521faebc8a7c.patch fixed upstream @ 521faeb 6334632: Disable setting primtive restart for WebGL in the cmd decoder. | https://chromium-review.googlesource.com/c/chromium/src/+/6334632 * chore: remove cherry-pick-9dacf5694dfd.patch fixed upstream @ 9dacf56 6330188: Move WebGL primitive restart state setting to the GPU process. | https://chromium-review.googlesource.com/c/chromium/src/+/6330188 * chore: e patches all --------- Co-authored-by: Charles Kerr Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- docs/api/session.md | 2 +- docs/breaking-changes.md | 13 ++ ...ack_ssl_error_zero_return_explicitly.patch | 6 +- patches/chromium/.patches | 3 +- .../add_didinstallconditionalfeatures.patch | 20 +-- ...adjust_accessibility_ui_for_electron.patch | 2 +- ..._scheduler_throttling_per_renderview.patch | 12 +- ..._windows_to_have_different_web_prefs.patch | 28 ++-- patches/chromium/blink_local_frame.patch | 4 +- ..._mojom_interfaces_to_depend_on_blink.patch | 4 +- ..._depend_on_packed_resource_integrity.patch | 16 +- patches/chromium/build_gn.patch | 2 +- patches/chromium/can_create_window.patch | 22 +-- .../chromium/cherry-pick-521faebc8a7c.patch | 33 ----- .../chromium/cherry-pick-9dacf5694dfd.patch | 95 ------------ ...dle_synthetic_mouse_events_for_touch.patch | 12 +- ..._introduce_blocking_api_for_electron.patch | 8 +- .../chromium/chore_partial_revert_of.patch | 4 +- ...tition_attribute_dcheck_for_webviews.patch | 2 +- ...screationoverridden_with_full_params.patch | 18 +-- ..._conflicting_allow_unsafe_libc_calls.patch | 2 +- ...e_browser_v8_snapshot_file_name_fuse.patch | 6 +- .../disable_compositor_recycling.patch | 2 +- patches/chromium/disable_hidden.patch | 8 +- .../chromium/enable_reset_aspect_ratio.patch | 6 +- ...xpose_setuseragent_on_networkcontext.patch | 16 +- .../extend_apply_webpreferences.patch | 4 +- ...dd_set_theme_source_to_allow_apps_to.patch | 10 +- ...to_add_observers_on_created_hunspell.patch | 6 +- ...sharingpicker_on_supported_platforms.patch | 30 ++-- ...screen_rendering_with_viz_compositor.patch | 12 +- ..._raw_response_headers_from_urlloader.patch | 32 ++-- ...indows_in_the_current_application_in.patch | 4 +- ...ivate_background_material_on_windows.patch | 6 +- ..._exclusive_access_for_electron_needs.patch | 26 ++-- ...ables_headless_mode_on_native_widget.patch | 4 +- .../fix_aspect_ratio_with_max_size.patch | 4 +- ...ding_non-standard_schemes_in_iframes.patch | 10 +- ..._background_throttling_in_compositor.patch | 6 +- ...x_harden_blink_scriptstate_maybefrom.patch | 4 +- patches/chromium/fix_linter_error.patch | 43 ++++++ ...board_hides_on_input_blur_in_webview.patch | 18 +-- ...x_remove_caption-removing_style_call.patch | 4 +- ...ove_profiles_from_spellcheck_service.patch | 8 +- ...original_resize_performance_on_macos.patch | 2 +- ...from_localframe_requestexecutescript.patch | 32 ++-- patches/chromium/frame_host_manager.patch | 8 +- .../gin_enable_disable_v8_platform.patch | 6 +- .../chromium/gritsettings_resource_ids.patch | 4 +- ...key_appusermodel_toastactivatorclsid.patch | 4 +- ...reate_a_console_if_logging_to_stderr.patch | 4 +- .../chromium/make_gtk_getlibgtk_public.patch | 6 +- ..._avoid_private_macos_api_usage.patch.patch | 58 ++++---- ...emote_certificate_verification_logic.patch | 22 +-- .../chromium/notification_provenance.patch | 6 +- ...eated_to_allow_for_browser_initiated.patch | 4 +- patches/chromium/preconnect_manager.patch | 16 +- patches/chromium/printing.patch | 4 +- ...r_changes_to_the_webcontentsobserver.patch | 14 +- ...efactor_unfilter_unresponsive_events.patch | 4 +- .../render_widget_host_view_base.patch | 4 +- .../render_widget_host_view_mac.patch | 8 +- patches/chromium/resource_file_conflict.patch | 6 +- ...ean_up_stale_macwebcontentsocclusion.patch | 6 +- ...ssivethrottlingwithwebsocket_feature.patch | 6 +- patches/chromium/scroll_bounce_flag.patch | 4 +- .../support_mixed_sandbox_with_zygote.patch | 2 +- patches/chromium/web_contents.patch | 12 +- patches/chromium/webview_fullscreen.patch | 10 +- .../worker_context_will_destroy.patch | 4 +- ...feat_add_hook_to_notify_script_ready.patch | 4 +- ...i_to_allow_electron_to_set_dock_side.patch | 4 +- patches/node/.patches | 1 + patches/node/fix_-wnonnull_warning.patch | 43 ++++++ patches/v8/.patches | 1 + ...8_object_setinternalfieldfornodecore.patch | 4 +- ...ated_attachcppheap_and_detachcppheap.patch | 137 ++++++++++++++++++ shell/browser/api/electron_api_session.cc | 2 - shell/browser/api/electron_api_view.cc | 3 +- .../browser/api/electron_api_web_contents.cc | 2 +- shell/browser/api/electron_api_web_contents.h | 2 +- .../extensions/electron_extension_loader.cc | 12 +- .../electron_extensions_browser_client.cc | 1 - .../extensions/electron_messaging_delegate.cc | 47 ------ .../extensions/electron_messaging_delegate.h | 7 - shell/browser/ui/devtools_manager_delegate.cc | 1 - shell/browser/ui/inspectable_web_contents.cc | 15 +- shell/browser/ui/inspectable_web_contents.h | 4 +- shell/browser/ui/views/autofill_popup_view.cc | 4 +- shell/browser/ui/views/menu_bar.cc | 2 +- shell/browser/ui/webui/accessibility_ui.cc | 12 +- shell/common/application_info.cc | 4 +- .../extensions/electron_extensions_client.cc | 1 - shell/renderer/api/electron_api_web_frame.cc | 5 +- spec/ts-smoke/electron/main.ts | 2 +- 96 files changed, 599 insertions(+), 556 deletions(-) delete mode 100644 patches/chromium/cherry-pick-521faebc8a7c.patch delete mode 100644 patches/chromium/cherry-pick-9dacf5694dfd.patch create mode 100644 patches/chromium/fix_linter_error.patch create mode 100644 patches/node/fix_-wnonnull_warning.patch create mode 100644 patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch diff --git a/DEPS b/DEPS index 176b3014c68d1..0b5fee7db3bd0 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '135.0.7049.5', + '136.0.7062.0', 'node_version': 'v22.14.0', 'nan_version': diff --git a/docs/api/session.md b/docs/api/session.md index 65a3a36be0063..16c382d0c3a96 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -651,7 +651,7 @@ Clears the session’s HTTP cache. `shadercache`, `websql`, `serviceworkers`, `cachestorage`. If not specified, clear all storage types. * `quotas` string[] (optional) - The types of quotas to clear, can be - `temporary`, `syncable`. If not specified, clear all quotas. + `temporary`. If not specified, clear all quotas. Returns `Promise` - resolves when the storage data has been cleared. diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index e7d3eeff07e82..f09a7a3f3d7c1 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -19,6 +19,19 @@ This document uses the following convention to categorize breaking changes: These properties have been removed from the PrinterInfo Object because they have been removed from upstream Chromium. +### Removed: `quota` type `syncable` in `Session.clearStorageData(options)` + +When calling `Session.clearStorageData(options)`, the `options.quota` type +`syncable` is no longer supported because it has been +[removed](https://chromium-review.googlesource.com/c/chromium/src/+/6309405) +from upstream Chromium. + +### Deprecated: `quota` property in `Session.clearStorageData(options)` + +When calling `Session.clearStorageData(options)`, the `options.quota` +property is deprecated. Since the `syncable` type was removed, there +is only type left -- `'temporary'` -- so specifying it is unnecessary. + ### Deprecated: Extension methods and events on `session` `session.loadExtension`, `session.removeExtension`, `session.getExtension`, diff --git a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch index e1f1d51156825..e88ef421922c4 100644 --- a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch +++ b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch @@ -20,10 +20,10 @@ index 2cdcbc346175eeee69402ecee7f169e61c655199..f7226fe711e4214b216ea2c5173a0212 case ssl_open_record_error: diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index c859ea612ce2e6be80c6351d85fae230ca5bc2dd..5f4ffedb3b19dee4eef62e2033b0000847167111 100644 +index c0c65798b61650aec08b971b417b21aa4b2305c6..6212d3ae2b11f35a576fcd02afe7f55965206e08 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -1196,7 +1196,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { +@@ -1204,7 +1204,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { } if (ret_code == 0) { @@ -32,7 +32,7 @@ index c859ea612ce2e6be80c6351d85fae230ca5bc2dd..5f4ffedb3b19dee4eef62e2033b00008 return SSL_ERROR_ZERO_RETURN; } // An EOF was observed which violates the protocol, and the underlying -@@ -2563,13 +2563,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { +@@ -2571,13 +2571,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 05052ba8fd5bb..5a18b7a6a7d0c 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -141,5 +141,4 @@ feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch -cherry-pick-521faebc8a7c.patch -cherry-pick-9dacf5694dfd.patch +fix_linter_error.patch diff --git a/patches/chromium/add_didinstallconditionalfeatures.patch b/patches/chromium/add_didinstallconditionalfeatures.patch index 6a808112999a6..5258899fc7dda 100644 --- a/patches/chromium/add_didinstallconditionalfeatures.patch +++ b/patches/chromium/add_didinstallconditionalfeatures.patch @@ -23,10 +23,10 @@ index 44da0544b778d6ff4c14b6f4e8463cb8260d2f0d..8ae8939af4141a684b7a6d50a43e1abb int32_t world_id) {} virtual void DidClearWindowObject() {} diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index e995c76b1ecc50502c47862607408d0663e91738..9d675b45688572d3b38d29e7d2074e0fb4b737ac 100644 +index 3afbd7c4e88d97f1a5c744e82eba2da19cd9e53f..143c42b98965c879557213670a9fbbe460faec5b 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -4787,6 +4787,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, +@@ -4794,6 +4794,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, observer.DidCreateScriptContext(context, world_id); } @@ -40,10 +40,10 @@ index e995c76b1ecc50502c47862607408d0663e91738..9d675b45688572d3b38d29e7d2074e0f int world_id) { for (auto& observer : observers_) diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h -index 398676cc0642115ff825beb1a593c3d14d4a5531..cc7cd3c73d72e933807b8d6f4e9e4ac43cbea00d 100644 +index bf073c9180d47243f09fdd052a43dfe7e34aa4aa..d1642b675e474568c302553a288253b639ed1efd 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h -@@ -654,6 +654,8 @@ class CONTENT_EXPORT RenderFrameImpl +@@ -653,6 +653,8 @@ class CONTENT_EXPORT RenderFrameImpl void DidObserveLayoutShift(double score, bool after_input_or_scroll) override; void DidCreateScriptContext(v8::Local context, int world_id) override; @@ -53,10 +53,10 @@ index 398676cc0642115ff825beb1a593c3d14d4a5531..cc7cd3c73d72e933807b8d6f4e9e4ac4 int world_id) override; void DidChangeScrollOffset() override; diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h -index d0d0ccafdd25cbac7fa7daa8e615b8ca1018a68b..6bf6b6b8a2847d4677383b9ee4574ebe6894c287 100644 +index 038c13c1e697d4f21160e0329f659b609802ecc1..60fd9aa69a86c32abf79ef8737e810b0017e1083 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h -@@ -665,6 +665,9 @@ class BLINK_EXPORT WebLocalFrameClient { +@@ -664,6 +664,9 @@ class BLINK_EXPORT WebLocalFrameClient { virtual void DidCreateScriptContext(v8::Local, int32_t world_id) {} @@ -92,10 +92,10 @@ index 8bb6b0465069529f79aaec21792e8b159535b3e9..f9782531c639d07002dda07732750a00 int32_t world_id) = 0; virtual bool AllowScriptExtensions() = 0; diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc -index cfcabe9a443adae146c816419e0845cb6fc18404..717587c2cfe0b0228ae40adce04139cb54a34d0e 100644 +index 66cc44003157657c68e90d295f877789395eca8d..19cd8460711d5d5b1b76c9e4cb82ed7fc71b410c 100644 --- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc +++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc -@@ -296,6 +296,13 @@ void LocalFrameClientImpl::DidCreateScriptContext( +@@ -295,6 +295,13 @@ void LocalFrameClientImpl::DidCreateScriptContext( web_frame_->Client()->DidCreateScriptContext(context, world_id); } @@ -123,10 +123,10 @@ index 4c7375a27a22f04694e14fecc17d633734d4cccc..ea2a32b151aaf07b5704e463d01714eb int32_t world_id) override; diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h -index 6b967bd0b430732732ef30fb404e615a55c7c364..6bba885bca8f65744bcd03efcb98725649687856 100644 +index 8ca461c8fc3f2e508be5783406b570062ccf5de0..3c67d214ecd2b3b2f3e8955836e8ef7c0f0a525d 100644 --- a/third_party/blink/renderer/core/loader/empty_clients.h +++ b/third_party/blink/renderer/core/loader/empty_clients.h -@@ -415,6 +415,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { +@@ -416,6 +416,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { void DidCreateScriptContext(v8::Local, int32_t world_id) override {} diff --git a/patches/chromium/adjust_accessibility_ui_for_electron.patch b/patches/chromium/adjust_accessibility_ui_for_electron.patch index de7a05bb53129..85f71b85b7724 100644 --- a/patches/chromium/adjust_accessibility_ui_for_electron.patch +++ b/patches/chromium/adjust_accessibility_ui_for_electron.patch @@ -10,7 +10,7 @@ usage of BrowserList and Browser as we subclass related methods and use our WindowList. diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc -index ff25e6eb48deb339ab92152eb09ad856068fd589..4a1f81429f0857e129e31103b026079de13fd826 100644 +index 230c9cb619498f315fc0913da54837b725fc9024..0fa1120ab952864e53085e7746608bb491ec14b2 100644 --- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc +++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc @@ -48,6 +48,7 @@ diff --git a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch index 225e8ad5768b9..377c31242d6be 100644 --- a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch +++ b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch @@ -23,7 +23,7 @@ index 6c679ef877067297ec3bf1a23af6c03a2af8dcf5..1ac93433189580c13b69cd52ce62681a return receiver_.BindNewEndpointAndPassDedicatedRemote(); } diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc -index a387789fe4270739408a9beb32a5ad1a8e51e5f6..43c108bd6b51d293e0f8b1b5393c76c5d167ccef 100644 +index 6c1e46e58d975137eee6c0fd7b01d2ae970a2e10..5ca29616369e76e298eb03188e18ca545c76a162 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -765,6 +765,11 @@ void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) { @@ -39,7 +39,7 @@ index a387789fe4270739408a9beb32a5ad1a8e51e5f6..43c108bd6b51d293e0f8b1b5393c76c5 return is_active(); } diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h -index 5fb8a3dc69dc5fc5bfa08e01d8f03707a23c9274..41774b60b8cb7e0a22cedc597dc07ad15c96988c 100644 +index 56e52f079d1ad7c7a22764b976f0c8b2cc48dff2..1231fe522ad103e94d3c30ad7d5e5d23df9b3554 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -135,6 +135,7 @@ class CONTENT_EXPORT RenderViewHostImpl @@ -51,7 +51,7 @@ index 5fb8a3dc69dc5fc5bfa08e01d8f03707a23c9274..41774b60b8cb7e0a22cedc597dc07ad1 void SendRendererPreferencesToRenderer( const blink::RendererPreferences& preferences); diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 92085aca6bd0c95a73b98e4173c0128d596b77f4..9503beefcbcfe7d99674582ece10a7e551fae96d 100644 +index 19d2df47b0c929558ac8cfc208742dfef463a68b..7e9f2e71f08c1324a805462064d4fa485041c19f 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -579,8 +579,8 @@ void RenderWidgetHostViewAura::ShowImpl(PageVisibilityState page_visibility) { @@ -116,10 +116,10 @@ index b1689844282d6917b9750fbc6a875848ddf84b70..f1cc159b7c3448a33a6d9e213f8fbd3b // Visibility ----------------------------------------------------------- diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index c6a4f97c483667eaae354d8b99a01a6a3e765700..e4ab513573c7e327627b1ebd5b263f389098727a 100644 +index 3de7e34536eceb241de943908875a9c3e5e99f7e..10c3795839969bde6a747da67d5cc7caa44a7179 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -2468,6 +2468,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( +@@ -2461,6 +2461,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( TRACE_EVENT2("navigation", "WebViewImpl::SetPageLifecycleStateInternal", "old_state", old_state, "new_state", new_state); @@ -130,7 +130,7 @@ index c6a4f97c483667eaae354d8b99a01a6a3e765700..e4ab513573c7e327627b1ebd5b263f38 bool storing_in_bfcache = new_state->is_in_back_forward_cache && !old_state->is_in_back_forward_cache; bool restoring_from_bfcache = !new_state->is_in_back_forward_cache && -@@ -3994,10 +3998,23 @@ PageScheduler* WebViewImpl::Scheduler() const { +@@ -3987,10 +3991,23 @@ PageScheduler* WebViewImpl::Scheduler() const { return GetPage()->GetPageScheduler(); } diff --git a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch index 1af964c5d7b33..2b6beb632e5b1 100644 --- a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch +++ b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch @@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on process-level command line switches, as before. diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc -index f17c7ae24086ab0112bd1245291f6f512a74e223..a1a23d263e4842996c0f8dde4dca8d8b35802fda 100644 +index 2494aeb1cd363c07e1b662743b1453da646fe17d..927dce4724263b45ff27b046ef024ae2a04706a9 100644 --- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc +++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc -@@ -147,6 +147,19 @@ bool StructTraitsv8_cache_options = data.v8_cache_options(); out->record_whole_document = data.record_whole_document(); out->stylus_handwriting_enabled = data.stylus_handwriting_enabled(); @@ -32,7 +32,7 @@ index f17c7ae24086ab0112bd1245291f6f512a74e223..a1a23d263e4842996c0f8dde4dca8d8b out->accelerated_video_decode_enabled = data.accelerated_video_decode_enabled(); diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h -index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d6d962747 100644 +index b7b817c01fcec0bf60bd88544ec66b53248a1ed8..82a03a14e19840924c90cb768a3bbd715fdf82d4 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences.h +++ b/third_party/blink/public/common/web_preferences/web_preferences.h @@ -9,6 +9,7 @@ @@ -43,7 +43,7 @@ index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d #include "build/build_config.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/common/common_export.h" -@@ -442,6 +443,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { +@@ -447,6 +448,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { // when feature DynamicSafeAreaInsets is enabled. bool dynamic_safe_area_insets_enabled = false; @@ -65,18 +65,18 @@ index 95e6b4c64bca410586378c88df288ef2bb0c6992..783c44be399a3bcf251b52c47340c26d // chrome, except for the cases where it would require lots of extra work for // the embedder to use the same default value. diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h -index 68f936bc7103accc4521da0a12e5e0fad05cc86c..24f209d5694cdf7128a8fb58af4957f0905058a0 100644 +index d686fc396ce4d53c209be5cbb4d4ddfecc4cca1b..cd1110adc4131404ba32595ea5533b220554b0c4 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h +++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h -@@ -6,6 +6,7 @@ - #define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PREFERENCES_WEB_PREFERENCES_MOJOM_TRAITS_H_ +@@ -8,6 +8,7 @@ + #include #include "build/build_config.h" +#include "mojo/public/cpp/base/file_path_mojom_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/common/common_export.h" -@@ -434,6 +435,52 @@ struct BLINK_COMMON_EXPORT StructTraitsDetached(type); diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 401679f818e7bba02be454f9aa3424e84d36c4af..4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c 100644 +index dbb0843658e356f3c47dcaef470789c9e047aeee..feb30ced77cfc7d0a1faa637cd8bdec399155504 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc @@ -746,10 +746,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { diff --git a/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch index 1176ce679765f..c6c5e13bcf57f 100644 --- a/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch +++ b/patches/chromium/build_allow_electron_mojom_interfaces_to_depend_on_blink.patch @@ -10,10 +10,10 @@ Needed for: 2) //electron/shell/common:web_contents_utility diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn -index dee8a4413f62ac5535f4bce504ff0dc934db743a..064a62b40679f05feac3c81f9d6e6c2c90972727 100644 +index 55921115094f7437ce10db1cac1debe9adc10ea9..7040d83af340f052f5cab4437433e3dd0a688390 100644 --- a/content/public/common/BUILD.gn +++ b/content/public/common/BUILD.gn -@@ -380,6 +380,8 @@ mojom("interfaces") { +@@ -379,6 +379,8 @@ mojom("interfaces") { "//content/common/*", "//extensions/common:mojom", "//extensions/common:mojom_blink", diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index 416bd10512c85..d366c327411ce 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -11,10 +11,10 @@ if we ever align our .pak file generation with Chrome we can remove this patch. diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index bdf6d5865fb0069f4df368613167069d2fb50c86..93a126365406badf911d938dde2dcd8b140b7f6f 100644 +index b38442f018b218944c7b85c9f8bd8b8eb6137b9e..dd15f6cf5dc40f2d54134c833d35508f2e22967b 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -196,11 +196,16 @@ if (!is_android && !is_mac) { +@@ -199,11 +199,16 @@ if (!is_android && !is_mac) { "common/crash_keys.h", ] @@ -33,10 +33,10 @@ index bdf6d5865fb0069f4df368613167069d2fb50c86..93a126365406badf911d938dde2dcd8b "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 0c9c08fe6c1303bc624464582857eb05b422f14c..adb35ecb5ba8fa69b2f20a6b4fd6a154a792bee3 100644 +index d668c23e5eb34602fab4f9002c341a9d38f13995..55e5e98c332fdc99b7ce824665d71ceb826d647f 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4565,7 +4565,7 @@ static_library("browser") { +@@ -4564,7 +4564,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index 0c9c08fe6c1303bc624464582857eb05b422f14c..adb35ecb5ba8fa69b2f20a6b4fd6a154 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51c3404302 100644 +index ceefd25c55645ddc0bc04c7509962edec1148506..bfb8ee6710ac4fa9e35cd92d109fc8b1647c552d 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7052,9 +7052,12 @@ test("unit_tests") { +@@ -7024,9 +7024,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51 "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -8015,6 +8018,10 @@ test("unit_tests") { +@@ -7990,6 +7993,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index 77056dcfa38390f5e047a9a9fea2e86357c33237..9c9f2c4bd3de71928e63ee8ea8f33a51 sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8070,7 +8077,6 @@ test("unit_tests") { +@@ -8045,7 +8052,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/build_gn.patch b/patches/chromium/build_gn.patch index ccfcfe1f169bc..bd1b1df06c7e8 100644 --- a/patches/chromium/build_gn.patch +++ b/patches/chromium/build_gn.patch @@ -7,7 +7,7 @@ These are variables we add to the root BUILDCONFIG so that they're available everywhere, without having to import("//electron/.../flags.gni"). diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn -index b5c5cb75e5fe2c58b1ee9051a9fe8cc082b88efa..8bf8d75205e984878219dcc5016b8665b9f65577 100644 +index 9e64769566a136b41cab4ab5e31798ae33d2ebd1..0303488f688c1e18d0cdbafc0c481cccbbcc96d0 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -123,6 +123,9 @@ if (current_os == "") { diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index ea0c4c63c43d0..6b09d4fa7c36a 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index d7f1dc68e780f08af438c6ce67a3c856ef31daef..09752e9cda20f0020976015d58f73f320031d11e 100644 +index 6708ec005561fb174ed9557ad92a85f5c33666ee..37af686964489bd77d84be95d5f3aba9cd0a7a0c 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9612,6 +9612,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9629,6 +9629,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,10 +21,10 @@ index d7f1dc68e780f08af438c6ce67a3c856ef31daef..09752e9cda20f0020976015d58f73f32 &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index c3e88374849c6062195fb78072123f05e1929fe8..79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496 100644 +index c28aabb66b8ff483a140a6c18b4cd7fbc82c649d..60ed731f7648497d62fbc7b43181a47a82743c4b 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5032,6 +5032,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5061,6 +5061,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( SetPartitionedPopinOpenerOnNewWindowIfNeeded(new_contents_impl, params, opener); @@ -37,7 +37,7 @@ index c3e88374849c6062195fb78072123f05e1929fe8..79bc1f7a5c44a0dd255b5ca676d95c9f // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses -@@ -5073,12 +5079,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5102,12 +5108,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( AddWebContentsDestructionObserver(new_contents_impl); } @@ -66,7 +66,7 @@ index 91dcf6c9c4a2d840fb50cb329fe3ef1bba9103c3..cbc887a3034605a93468e73a310e9ca6 // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index eacc673508fded48288c90920f424e93f99f96cd..7eb3bb261a66b3bf8e5eaaea82962838376f3765 100644 +index 54014f1d8374b4286b3f4358cca1b6964ce370f8..50c5073b4281239a485ea8b0e08d687d7e9a3cf3 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc @@ -805,6 +805,8 @@ bool ContentBrowserClient::CanCreateWindow( @@ -79,10 +79,10 @@ index eacc673508fded48288c90920f424e93f99f96cd..7eb3bb261a66b3bf8e5eaaea82962838 bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index cafc62e4b6da45e63cb77d27de9794d4b34cb408..da93d31b658577bd4817b2dcfe366d444dbc2e92 100644 +index f3750ed03dff3ca9885b189692dde2c919ab3eb3..faf574d7778e24d6fc9e3f539b39c9cb1c149bbc 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -199,6 +199,7 @@ class NetworkService; +@@ -198,6 +198,7 @@ class NetworkService; class TrustedURLLoaderHeaderClient; } // namespace mojom struct ResourceRequest; @@ -90,7 +90,7 @@ index cafc62e4b6da45e63cb77d27de9794d4b34cb408..da93d31b658577bd4817b2dcfe366d44 } // namespace network namespace sandbox { -@@ -1357,6 +1358,8 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -1356,6 +1357,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -148,10 +148,10 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 // typically happens when popups are created. virtual void WebContentsCreated(WebContents* source_contents, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index d7f25fd51ce82e20146d2df0978644deb6297fbb..e995c76b1ecc50502c47862607408d0663e91738 100644 +index 602bea4189f0d23c19cbc7afd56583927cbb7e0c..3afbd7c4e88d97f1a5c744e82eba2da19cd9e53f 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -6908,6 +6908,10 @@ WebView* RenderFrameImpl::CreateNewWindow( +@@ -6916,6 +6916,10 @@ WebView* RenderFrameImpl::CreateNewWindow( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); diff --git a/patches/chromium/cherry-pick-521faebc8a7c.patch b/patches/chromium/cherry-pick-521faebc8a7c.patch deleted file mode 100644 index bae1cb95eb56f..0000000000000 --- a/patches/chromium/cherry-pick-521faebc8a7c.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 521faebc8a7cffe23177c6600bfcfb3c0b9ab1dc Mon Sep 17 00:00:00 2001 -From: Geoff Lang -Date: Thu, 06 Mar 2025 19:39:37 -0800 -Subject: [PATCH] Disable setting primtive restart for WebGL in the cmd decoder. - -Until it's blocked in ANGLE for WebGL contexts, disable it in the -command decoder on the service side. - -Bug: 401059730 -Change-Id: Ia9c7d951cbd122454afec2f884968e0a709cee77 -Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6334632 -Reviewed-by: Shahbaz Youssefi -Reviewed-by: Kenneth Russell -Commit-Queue: Kenneth Russell -Cr-Commit-Position: refs/heads/main@{#1429307} ---- - -diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -index ad23480..733c553 100644 ---- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -@@ -2170,6 +2170,11 @@ - case GL_DEBUG_OUTPUT: - return true; - -+ case GL_PRIMITIVE_RESTART_FIXED_INDEX: -+ // Disable setting primitive restart at the command decoder level until -+ // it's blocked in ANGLE for WebGL contexts. -+ return feature_info_->IsWebGLContext(); -+ - default: - return false; - } diff --git a/patches/chromium/cherry-pick-9dacf5694dfd.patch b/patches/chromium/cherry-pick-9dacf5694dfd.patch deleted file mode 100644 index a67a1cbf988ed..0000000000000 --- a/patches/chromium/cherry-pick-9dacf5694dfd.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 9dacf5694dfdb735c335805783840584a50bface Mon Sep 17 00:00:00 2001 -From: Geoff Lang -Date: Thu, 06 Mar 2025 16:02:41 -0800 -Subject: [PATCH] Move WebGL primitive restart state setting to the GPU process. - -ANGLE will validate and initialize this state and errors are generated -when the WebGL client also initializes it on startup. - -Initialize it even in the passthrough command decoder temporarily so -that ANGLE can roll without breaking WebGL tests. - -Bug: 401059730 -Change-Id: I0bfee710673bbcea6f915ffc4fc9be20438a2654 -Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6330188 -Auto-Submit: Geoff Lang -Commit-Queue: Kenneth Russell -Reviewed-by: Kenneth Russell -Cr-Commit-Position: refs/heads/main@{#1429228} ---- - -diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc -index d835b1f..0eedac0 100644 ---- a/gpu/command_buffer/service/gles2_cmd_decoder.cc -+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc -@@ -3272,6 +3272,13 @@ - } - } - -+ if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL2) { -+ // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. -+ // See the section in WebGL 2 spec: -+ // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 -+ DoEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); -+ } -+ - if (group_->gpu_preferences().enable_gpu_driver_debug_logging && - feature_info_->feature_flags().khr_debug) { - InitializeGLDebugLogging(true, GLDebugMessageCallback, &logger_); -diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -index 3ccdebc1..ad23480 100644 ---- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc -@@ -1064,6 +1064,17 @@ - api()->glDisableFn(GL_TEXTURE_RECTANGLE_ANGLE); - #endif - -+ // TEMPORARY: Set primitive restart to enabled by default for WebGL2. Clear -+ // errors afterwards so that when this state is initialized and validated in -+ // ANGLE, it will not generate errors during command buffer initialization. -+ if (feature_info_->context_type() == CONTEXT_TYPE_WEBGL2) { -+ // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. -+ // See the section in WebGL 2 spec: -+ // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 -+ api()->glEnableFn(GL_PRIMITIVE_RESTART_FIXED_INDEX); -+ CheckErrorCallbackState(); -+ } -+ - // Register this object as a GPU switching observer. - if (feature_info_->IsWebGLContext()) { - ui::GpuSwitchingManager::GetInstance()->AddObserver(this); -diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc -index 8e898bd..6030000 100644 ---- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc -+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc -@@ -439,6 +439,13 @@ - } - #endif - -+ if (init.context_type == CONTEXT_TYPE_WEBGL2 && -+ group_->feature_info()->gl_version_info().is_es3) { -+ EXPECT_CALL(*gl_, Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX)) -+ .Times(1) -+ .RetiresOnSaturation(); -+ } -+ - if (context_->HasRobustness()) { - EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()) - .WillOnce(Return(init.lose_context_on_init ? GL_GUILTY_CONTEXT_RESET_ARB -diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc -index 79597c8..7c42b98 100644 ---- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc -+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc -@@ -1433,12 +1433,6 @@ - WTF::BindRepeating(&WebGLRenderingContextBase::OnErrorMessage, - WrapWeakPersistent(this))); - -- // If WebGL 2, the PRIMITIVE_RESTART_FIXED_INDEX should be always enabled. -- // See the section in WebGL 2 spec: -- // https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.4 -- if (IsWebGL2()) -- ContextGL()->Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX); -- - // This ensures that the context has a valid "lastFlushID" and won't be - // mistakenly identified as the "least recently used" context. - ContextGL()->Flush(); diff --git a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch b/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch index 47583a50ac415..23dd5ed59e3f2 100644 --- a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch +++ b/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch @@ -7,10 +7,10 @@ With WCO, allow chromium to handle synthetic mouse events generated for touch actions in the non-client caption area. 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 2d05856687cd9669f72553d33c8033fd9083b4f8..645b7dd2cc20ce64ffa541c74930f541f083f931 100644 +index 55c426aee12da4d4d1f62dc7d489133e8d21dc49..40c88bf14d2d4fe841e29b32414361688ae438e5 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 -@@ -1357,6 +1357,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( +@@ -1362,6 +1362,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( window()->SetProperty(aura::client::kHeadlessBoundsKey, bounds); } @@ -22,7 +22,7 @@ index 2d05856687cd9669f72553d33c8033fd9083b4f8..645b7dd2cc20ce64ffa541c74930f541 DesktopWindowTreeHostWin::GetSingletonDesktopNativeCursorManager() { return new DesktopNativeCursorManagerWin(); 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 932351e288f37fd09ae1a43f44e8b51fb0caa4b8..4a0616bc210d234e51e564daabdd2ebd5ac9fc16 100644 +index 4865d79c95c34d8cead96d3bb8063a0e2bd6076b..ebfa09ed15dca98b75a013e3dcbb566ce84d7cb7 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 @@ -267,6 +267,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, @@ -34,10 +34,10 @@ index 932351e288f37fd09ae1a43f44e8b51fb0caa4b8..4a0616bc210d234e51e564daabdd2ebd // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 2bd015be3178ab8dea012d6b1f71d13dd0548c14..759f00dd4e674d1dfca690b82e6e1820900ebf0c 100644 +index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..679efe5af4954d9ddca7bc5ccee6b8d6fac966a3 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3144,15 +3144,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3146,15 +3146,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } // We must let Windows handle the caption buttons if it's drawing them, or // they won't work. @@ -60,7 +60,7 @@ index 2bd015be3178ab8dea012d6b1f71d13dd0548c14..759f00dd4e674d1dfca690b82e6e1820 } } diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h -index 04dea68d74ea4f559db60f716c919e555db9ec80..2f8bd1a3c156bb6c04663c74b7279bb59926fc3d 100644 +index de8fd5657e6885f74a5970bdd49647a6f1616387..4af87792edc7a147468077b834582510550e35e6 100644 --- a/ui/views/win/hwnd_message_handler_delegate.h +++ b/ui/views/win/hwnd_message_handler_delegate.h @@ -255,6 +255,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { diff --git a/patches/chromium/chore_introduce_blocking_api_for_electron.patch b/patches/chromium/chore_introduce_blocking_api_for_electron.patch index d72de4fe61705..adb711334e88f 100644 --- a/patches/chromium/chore_introduce_blocking_api_for_electron.patch +++ b/patches/chromium/chore_introduce_blocking_api_for_electron.patch @@ -7,7 +7,7 @@ This patch comes after Chromium removed the ScopedAllowIO API in favor of explicitly adding ScopedAllowBlocking calls as friends. diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h -index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e0f597937 100644 +index 075e8173bcf1cfe420b87afe216bb06459a0d8bc..a300ed1e8c226f43fbfe6d82f72f9fb741fa342e 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -132,6 +132,7 @@ class KeyStorageLinux; @@ -18,7 +18,7 @@ index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e class Profile; class ProfileImpl; class ScopedAllowBlockingForProfile; -@@ -278,6 +279,9 @@ class BackendImpl; +@@ -277,6 +278,9 @@ class BackendImpl; class InFlightIO; bool CleanupDirectorySync(const base::FilePath&); } // namespace disk_cache @@ -28,7 +28,7 @@ index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e namespace enterprise_connectors { class LinuxKeyRotationCommand; } // namespace enterprise_connectors -@@ -575,6 +579,7 @@ class BASE_EXPORT ScopedAllowBlocking { +@@ -574,6 +578,7 @@ class BASE_EXPORT ScopedAllowBlocking { friend class ::DesktopNotificationBalloon; friend class ::FirefoxProfileLock; friend class ::GaiaConfig; @@ -36,7 +36,7 @@ index 56ddbc2d4fa336bcdbe9aaacd4bf8bbaa3573239..7859ce547dc00c2842fe1aa3699cf20e friend class ::ProfileImpl; friend class ::ScopedAllowBlockingForProfile; friend class ::StartupTabProviderImpl; -@@ -615,6 +620,7 @@ class BASE_EXPORT ScopedAllowBlocking { +@@ -613,6 +618,7 @@ class BASE_EXPORT ScopedAllowBlocking { friend class crypto::ScopedAllowBlockingForNSS; // http://crbug.com/59847 friend class drive::FakeDriveService; friend class extensions::DesktopAndroidExtensionSystem; diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index a9c60128ec457..aeec7988bdc9e 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,10 +14,10 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 86a1a0244a3744d0571c046d3e6fc9413c9fbbf0..1396f371c8ae82a1ce52096c176a135cf02b2d65 100644 +index a16c2368a4e37756642544c45d3e4b1fe7d64b26..f3d223efcfa8f909e810ec43dd5ef90c9fc5c620 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4951,7 +4951,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4980,7 +4980,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( : IsGuest(); // While some guest types do not have a guest SiteInstance, the ones that // don't all override WebContents creation above. diff --git a/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch b/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch index 7070caeb5f22c..ccad2444864a5 100644 --- a/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch +++ b/patches/chromium/chore_patch_out_partition_attribute_dcheck_for_webviews.patch @@ -14,7 +14,7 @@ This change patches it out to prevent the DCHECK. It can be removed once/if we see a better solution to the problem. diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc -index d4dd73e7fa1e550d0d13b4c09da0ce6899894494..679a59db98261eca3fbbd7ddb20516ab4f618cfb 100644 +index adaa1cd426c138972b088d0d0093b0e1653af231..be4684c94ba2214255c5dbe9cdcf1ea316c60c06 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -229,7 +229,7 @@ scoped_refptr SiteInstanceImpl::CreateForGuest( diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index 828ecedd74c09..ff7e01427518c 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -80,10 +80,10 @@ index 4fd8dff1089cd6afa6a66dc185734d7671657281..0a1f4268ea771a3d5d4a2668928c6e5d content::WebContents* source, const content::OpenURLParams& params, diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc -index fafd84d08e33622aee01f1792c043180a13dd48d..4490915e0b5c8e93255c6d526d5f37eb6f6b362d 100644 +index 8232c0081baaeb3df9f2f6224f8fe31a2dcb1d5b..081c01bc00dfdef66c23a9ef640ed159f4f7557b 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc -@@ -2222,12 +2222,11 @@ bool Browser::IsWebContentsCreationOverridden( +@@ -2264,12 +2264,11 @@ bool Browser::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -99,10 +99,10 @@ index fafd84d08e33622aee01f1792c043180a13dd48d..4490915e0b5c8e93255c6d526d5f37eb WebContents* Browser::CreateCustomWebContents( diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h -index 8fda0dfbe8e10a3830bd03a994e7d39817584d36..baf24bbeb9f57fe2609a1af51a6ee4f5abd86ce2 100644 +index 7a61c64db93db7f3b31b8d39897257a6315383c2..7db684020fbaf7061ed61a8b1cefba8c757f2fe5 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h -@@ -980,8 +980,7 @@ class Browser : public TabStripModelObserver, +@@ -981,8 +981,7 @@ class Browser : public TabStripModelObserver, content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -155,10 +155,10 @@ index 83a0a538fef0da1d3674293d20dac7b8a252273f..155c8a0af46e20f68f8b028c056092b4 } content::WebContents* CreateCustomWebContents( diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.cc b/components/embedder_support/android/delegate/web_contents_delegate_android.cc -index fb08ed752db063c5a295c479bc5ad990826104cd..659e642cb660263a1cba9bb485ed3f67c2939c0f 100644 +index e0264aff5b60bb5e76ac7687222403dfba516e66..915abd2475f6be95d973f827522f7ef12052a81c 100644 --- a/components/embedder_support/android/delegate/web_contents_delegate_android.cc +++ b/components/embedder_support/android/delegate/web_contents_delegate_android.cc -@@ -188,14 +188,13 @@ bool WebContentsDelegateAndroid::IsWebContentsCreationOverridden( +@@ -189,14 +189,13 @@ bool WebContentsDelegateAndroid::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -176,7 +176,7 @@ index fb08ed752db063c5a295c479bc5ad990826104cd..659e642cb660263a1cba9bb485ed3f67 java_gurl); } diff --git a/components/embedder_support/android/delegate/web_contents_delegate_android.h b/components/embedder_support/android/delegate/web_contents_delegate_android.h -index 45d0fc1b9dbeb1c1b5a28a07fb8cf26d3388bfd9..d311ea8dc016659ee8355bc4e7742b4e3ebc413c 100644 +index 67a01b48ae88c7e25aeb5c5103b26afa037f4f97..0ceb539c9c3051b5521236cf866ccb107727c8a9 100644 --- a/components/embedder_support/android/delegate/web_contents_delegate_android.h +++ b/components/embedder_support/android/delegate/web_contents_delegate_android.h @@ -82,8 +82,7 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate { @@ -218,10 +218,10 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9f18308873306e841bbc15d9ba36c93ba5603a15..9d6ad368980202f74d36785623d27354beef3f03 100644 +index e84ef165670048dd2f030a48ff2d9796b5170b52..591c87ffc5f56b38d0f329da8b983a77af9662ee 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4914,8 +4914,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4943,8 +4943,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( // TODO(crbug.com/40202416): Support a way for MPArch guests to support this. if (delegate_ && delegate_->IsWebContentsCreationOverridden( source_site_instance, params.window_container_type, diff --git a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch index 91d27c1ec9608..8715b6c8872b9 100644 --- a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch +++ b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch @@ -32,7 +32,7 @@ index 7d5d0106a3675b3fa21b0e00a755f5c0ed11c87b..d26c645d70b54b31815c8140954ee6d0 // (netwerk/cookie/src/nsCookieService.cpp) /* ***** BEGIN LICENSE BLOCK ***** diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc -index 813f2f7f274bf02b6679b9321ae83948ab634697..2c61297669ba7d513f8493dfb6f478245f5c7c58 100644 +index 2235a264ff87ef400734a7308c2735886c63c92c..2310a951c5b4d34447a39c7585bdf1027a9953cc 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -2,11 +2,6 @@ diff --git a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch index 87e64a51fd108..553772fa6b448 100644 --- a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch +++ b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch @@ -95,10 +95,10 @@ index 801bfd401ea4a8e72417d88efaa718cc6fb60883..663fec68d0c2855cdf83bb259b85c229 friend class ContentClientCreator; friend class ContentClientInitializer; diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index e72331d0821a14507e3c973f07889adcf45d3bdf..3acfc0d49787579d0250703eed3d6e31b79c567a 100644 +index 578e200a88d89b356e991b3317ff1e71f25ff75e..ae49b7b5e830a7127812219df1c8888b7ba4b348 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -657,8 +657,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, +@@ -672,8 +672,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, #if defined(V8_USE_EXTERNAL_STARTUP_DATA) @@ -108,7 +108,7 @@ index e72331d0821a14507e3c973f07889adcf45d3bdf..3acfc0d49787579d0250703eed3d6e31 if (g_mapped_snapshot) { // TODO(crbug.com/40558459): Confirm not loading different type of snapshot // files in a process. -@@ -667,10 +666,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { +@@ -682,10 +681,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { base::MemoryMappedFile::Region file_region; base::File file = diff --git a/patches/chromium/disable_compositor_recycling.patch b/patches/chromium/disable_compositor_recycling.patch index 3974a48b6a1b6..afb2922976a33 100644 --- a/patches/chromium/disable_compositor_recycling.patch +++ b/patches/chromium/disable_compositor_recycling.patch @@ -6,7 +6,7 @@ Subject: fix: disabling compositor recycling Compositor recycling is useful for Chrome because there can be many tabs and spinning up a compositor for each one would be costly. In practice, Chrome uses the parent compositor code path of browser_compositor_view_mac.mm; the NSView of each tab is detached when it's hidden and attached when it's shown. For Electron, there is no parent compositor, so we're forced into the "own compositor" code path, which seems to be non-optimal and pretty ruthless in terms of the release of resources. Electron has no real concept of multiple tabs per window, so it should be okay to disable this ruthless recycling altogether in Electron. diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index 943e3b8875c68178e84ae79c241c7a31e9305c6c..6055992c0f61348a95823b64c73574ca6b24904b 100644 +index 71db145609652dbbe733dbc96d2630b686f6c8c9..6aa0fd5422ee0057acc3e5c423ca2847a4ebb17e 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -560,7 +560,11 @@ diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index c1e5fe18c56fe..00a29bfd44cf4 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -6,7 +6,7 @@ Subject: disable_hidden.patch Electron uses this to disable background throttling for hidden windows. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 79a679ac59c3ebe4dc1180389cf7073560e843e6..8aa812aa9bb3de4c7bc58192baf45c90402d6ff4 100644 +index f4475e34d3d6cf78b1d5b5492e398d1551c2bd90..f13799f1cc440a20d8e3c55ee35f64e6505e5590 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -832,6 +832,10 @@ void RenderWidgetHostImpl::WasHidden() { @@ -21,10 +21,10 @@ index 79a679ac59c3ebe4dc1180389cf7073560e843e6..8aa812aa9bb3de4c7bc58192baf45c90 // Prompts should remain open and functional across tab switches. if (!delegate_ || !delegate_->IsWaitingForPointerLockPrompt(this)) { diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h -index 068b8036ad91357a5bb274f377ffb44f939d87be..c636170388f81f5afdcd3e86f85ec9f2ec944bb0 100644 +index 99fe44aab8599bffe256e4683ec36441c06925b8..72c5afe5d028c77acb76757698c15a013379860d 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h -@@ -1018,6 +1018,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl +@@ -1017,6 +1017,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Requests a commit and forced redraw in the renderer compositor. void ForceRedrawForTesting(); @@ -35,7 +35,7 @@ index 068b8036ad91357a5bb274f377ffb44f939d87be..c636170388f81f5afdcd3e86f85ec9f2 // |routing_id| must not be MSG_ROUTING_NONE. // If this object outlives |delegate|, DetachDelegate() must be called when diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 1d08f25b91496dbafc3a164e7fb3e66d1a48557b..92085aca6bd0c95a73b98e4173c0128d596b77f4 100644 +index 70f368ab0c59e7f495cb13094160d72f601e0938..19d2df47b0c929558ac8cfc208742dfef463a68b 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -643,7 +643,7 @@ void RenderWidgetHostViewAura::HideImpl() { diff --git a/patches/chromium/enable_reset_aspect_ratio.patch b/patches/chromium/enable_reset_aspect_ratio.patch index 6f2ff870b3609..07d3c9dadbc65 100644 --- a/patches/chromium/enable_reset_aspect_ratio.patch +++ b/patches/chromium/enable_reset_aspect_ratio.patch @@ -6,7 +6,7 @@ Subject: feat: enable setting aspect ratio to 0 Make SetAspectRatio accept 0 as valid input, which would reset to null. 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 88d8d9985c6b4c7051f00cba9dfa51b3fcfa524b..2d05856687cd9669f72553d33c8033fd9083b4f8 100644 +index 24f63e82a1a170b392bdc8e868729ddd5f9238fa..55c426aee12da4d4d1f62dc7d489133e8d21dc49 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 @@ -637,7 +637,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { @@ -19,10 +19,10 @@ index 88d8d9985c6b4c7051f00cba9dfa51b3fcfa524b..2d05856687cd9669f72553d33c8033fd excluded_margin); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index b3dc46c34f2aff45b3bd8ea041f2e55ba61d50f9..a802f4b710b6f8fa154d11846c061720a91e15e4 100644 +index a10d781e667416e74f53583081867d879f9f1eae..d1146216b6969562876a318586420636331f63d1 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -988,8 +988,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, +@@ -990,8 +990,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, void HWNDMessageHandler::SetAspectRatio(float aspect_ratio, const gfx::Size& excluded_margin) { diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index ef2408bbf90e3..dfb7eb432725b 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,10 +33,10 @@ index 0ab8187b0db8ae6db46d81738f653a2bc4c566f6..de3d55e85c22317f7f9375eb94d0d5d4 } // namespace net diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index e346cfb127a1fa8656bfbc1eba829907e19bc22f..e18583e520b4ab66110605b67c703a1c48b770a4 100644 +index 771fee7092bb25e4f7546941a04647d060e966c2..356d7e8fd5e8a82a8d506d1172844706bc76c99f 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc -@@ -1810,6 +1810,13 @@ void NetworkContext::SetNetworkConditions( +@@ -1814,6 +1814,13 @@ void NetworkContext::SetNetworkConditions( std::move(network_conditions)); } @@ -51,10 +51,10 @@ index e346cfb127a1fa8656bfbc1eba829907e19bc22f..e18583e520b4ab66110605b67c703a1c // This may only be called on NetworkContexts created with the constructor // that calls MakeURLRequestContext(). diff --git a/services/network/network_context.h b/services/network/network_context.h -index fe04f634b32e503f6e93ae6f20c8c7f7c85d02be..990df479da79b513cf8797aa2fcacba10c4fbbd9 100644 +index 930e0bd987c48d111b2c8d71147c09e4418bda6c..9373a53c5cac879c689fcea77f1dbbb32cf35b9e 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h -@@ -317,6 +317,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -323,6 +323,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void CloseIdleConnections(CloseIdleConnectionsCallback callback) override; void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override; @@ -63,10 +63,10 @@ index fe04f634b32e503f6e93ae6f20c8c7f7c85d02be..990df479da79b513cf8797aa2fcacba1 void SetEnableReferrers(bool enable_referrers) override; #if BUILDFLAG(IS_CT_SUPPORTED) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index e928d012d77ccffe16263fd0ee7c2769adfe4bac..1be1b691fa240fa3c5e2c572821977a968841211 100644 +index 773049b72e7784d396bb9cd1ebd29c3a523e7454..eccbcaf01b06d3c7613556d8f7617502c823e074 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -1253,6 +1253,9 @@ interface NetworkContext { +@@ -1261,6 +1261,9 @@ interface NetworkContext { SetNetworkConditions(mojo_base.mojom.UnguessableToken throttling_profile_id, NetworkConditions? conditions); @@ -77,10 +77,10 @@ index e928d012d77ccffe16263fd0ee7c2769adfe4bac..1be1b691fa240fa3c5e2c572821977a9 SetAcceptLanguage(string new_accept_language); diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h -index 97e8f9f145cecad9b0cd8b43a2de4af29cec7778..b9539a731d2252c8301d51ccb7a1bd7b1a23ca53 100644 +index da8551db16849cf82a6e3a7eaeb451cae0707f2d..6e20b8dd4f134ea7a8808223cd59064d89f5ce09 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h -@@ -156,6 +156,7 @@ class TestNetworkContext : public mojom::NetworkContext { +@@ -158,6 +158,7 @@ class TestNetworkContext : public mojom::NetworkContext { void CloseIdleConnections(CloseIdleConnectionsCallback callback) override {} void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override {} diff --git a/patches/chromium/extend_apply_webpreferences.patch b/patches/chromium/extend_apply_webpreferences.patch index e76d41ad4160d..75db37c172f62 100644 --- a/patches/chromium/extend_apply_webpreferences.patch +++ b/patches/chromium/extend_apply_webpreferences.patch @@ -12,7 +12,7 @@ Ideally we could add an embedder observer pattern here but that can be done in future work. diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index e4ab513573c7e327627b1ebd5b263f389098727a..4d5b74aa60a40e49bd0c9131e06eb399d1af4f22 100644 +index 10c3795839969bde6a747da67d5cc7caa44a7179..9810991e0a5d8b931a70e056b6651b8e5fdb9881 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc @@ -169,6 +169,7 @@ @@ -23,7 +23,7 @@ index e4ab513573c7e327627b1ebd5b263f389098727a..4d5b74aa60a40e49bd0c9131e06eb399 #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" -@@ -1866,6 +1867,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, +@@ -1859,6 +1860,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, #if BUILDFLAG(IS_MAC) web_view_impl->SetMaximumLegibleScale( prefs.default_maximum_page_scale_factor); diff --git a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch index 9add45122b7d3..59bc1acc40fc2 100644 --- a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch +++ b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch @@ -13,7 +13,7 @@ uses internally for things like menus and devtools. We can remove this patch once it has in some shape been upstreamed. diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc -index 95876b4040844b598ea7219732a044b4423f8d97..4e02620b7887d1b70b2a9b9e21b819d99efdca18 100644 +index 1ebd8809abb3208d402b6bd728114f7fea3ac52c..a564078fd9fe0c605a0dcb9eb21beabda389771f 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc @@ -210,6 +210,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors, @@ -26,10 +26,10 @@ index 95876b4040844b598ea7219732a044b4423f8d97..4e02620b7887d1b70b2a9b9e21b819d9 } diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h -index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d691ccb393e 100644 +index 2e657f24bb625c7a7af14686553aebdc06ad8eda..6f2384338ac4a48a78bc8aac8b4bb9d330d686b3 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h -@@ -458,6 +458,23 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -458,6 +458,23 @@ class COMPONENT_EXPORT(NATIVE_THEME) NativeTheme { scoped_refptr custom_theme, bool use_custom_frame = true) const; @@ -53,7 +53,7 @@ index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d69 // Returns a shared instance of the native theme that should be used for web // rendering. Do not use it in a normal application context (i.e. browser). // The returned object should not be deleted by the caller. This function is -@@ -713,6 +730,7 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -713,6 +730,7 @@ class COMPONENT_EXPORT(NATIVE_THEME) NativeTheme { PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference; std::optional caret_blink_interval_; bool use_overlay_scrollbars_ = false; @@ -62,7 +62,7 @@ index 4e825d649919c88aad26e4c3b2fd80575ca57db0..de1d8fea0113b55065e5229b07f95d69 SEQUENCE_CHECKER(sequence_checker_); }; diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index 2b2a12cd9d1ac17a245f049cf0775b46c0596531..350586d35b29fb8d772ea70615768d0e62dc5474 100644 +index b5ed5d33890bf4f98237c92bbe642063d27d0092..82c1705ae0dbc7ac33eb90e7978d48c7de7f1c8f 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc @@ -688,6 +688,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { diff --git a/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch index 1e95cc47416d0..ae499eab7f3b9 100644 --- a/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch +++ b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch @@ -7,10 +7,10 @@ Subject: feat: allow embedders to add observers on created hunspell This patch is used by Electron to implement spellchecker events. diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc -index 5140efab98b7d8d357ea0d97ee25814ea2e1ed8f..979630ba7ffe84d78804a02b832ce7787aecf2d9 100644 +index c2dd0fdc774a939dfe489180e019718ba4b192f1..910534aac947890b16b176b10ac8eae9857bd2d7 100644 --- a/chrome/browser/spellchecker/spellcheck_service.cc +++ b/chrome/browser/spellchecker/spellcheck_service.cc -@@ -478,6 +478,8 @@ void SpellcheckService::LoadDictionaries() { +@@ -477,6 +477,8 @@ void SpellcheckService::LoadDictionaries() { std::make_unique( dictionary, platform_spellcheck_language, context_, this)); hunspell_dictionaries_.back()->AddObserver(this); @@ -19,7 +19,7 @@ index 5140efab98b7d8d357ea0d97ee25814ea2e1ed8f..979630ba7ffe84d78804a02b832ce778 hunspell_dictionaries_.back()->Load(); } -@@ -532,6 +534,20 @@ bool SpellcheckService::IsSpellcheckEnabled() const { +@@ -531,6 +533,20 @@ bool SpellcheckService::IsSpellcheckEnabled() const { (!hunspell_dictionaries_.empty() || enable_if_uninitialized); } diff --git a/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch b/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch index 5498107b3194f..af85984794495 100644 --- a/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch +++ b/patches/chromium/feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch @@ -20,7 +20,7 @@ index afb657b7c9e1ede1273532b16428d37cc5d75c59..16707cf516cd34682c84ea2ccebddaa0 requested_format_.pixel_format = media::PIXEL_FORMAT_NV12; DCHECK_GT(requested_format_.frame_size.GetArea(), 0); diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc -index e59ed011a70358a4ff61342e4db3e0ce4cad8ab0..e3fa18fbade4c11f5317705bc89a1e2456c924bc 100644 +index 4ec4c895423151d2c907d97de1461cbde8c8a639..0e59797833b81c07299b8c342f591e192183c6f1 100644 --- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc +++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc @@ -20,7 +20,7 @@ void IOSurfaceCaptureDeviceBase::AllocateAndStart( @@ -33,7 +33,7 @@ index e59ed011a70358a4ff61342e4db3e0ce4cad8ab0..e3fa18fbade4c11f5317705bc89a1e24 void IOSurfaceCaptureDeviceBase::StopAndDeAllocate() { diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.h b/content/browser/media/capture/io_surface_capture_device_base_mac.h -index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124bf2ee1f1 100644 +index e2771b7b281274cdcb601a5bc78a948ad592087b..48d116823a28213e50775f378e6ce04ce3af5072 100644 --- a/content/browser/media/capture/io_surface_capture_device_base_mac.h +++ b/content/browser/media/capture/io_surface_capture_device_base_mac.h @@ -25,7 +25,7 @@ class CONTENT_EXPORT IOSurfaceCaptureDeviceBase @@ -46,11 +46,11 @@ index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124 // OnStop is called by StopAndDeAllocate. virtual void OnStop() = 0; diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm -index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378719b14fe 100644 +index 7e17594c30ac3cf8cb484b53563b03fc75bd2e0b..0e4a68f2fd8179640f877cb258b4049610fd49da 100644 --- a/content/browser/media/capture/screen_capture_kit_device_mac.mm +++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm -@@ -26,6 +26,61 @@ - std::optional, +@@ -27,6 +27,61 @@ + std::optional, bool)>; using ErrorCallback = base::RepeatingClosure; +using CancelCallback = base::RepeatingClosure; @@ -111,7 +111,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 namespace { API_AVAILABLE(macos(12.3)) -@@ -120,18 +175,22 @@ @interface ScreenCaptureKitDeviceHelper +@@ -123,18 +178,22 @@ @interface ScreenCaptureKitDeviceHelper : NSObject - (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback @@ -134,7 +134,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 _errorCallback = errorCallback; } return self; -@@ -207,29 +266,53 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize +@@ -211,29 +270,53 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac : public IOSurfaceCaptureDeviceBase, @@ -192,7 +192,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 void OnShareableContentCreated(SCShareableContent* content) { DCHECK(device_task_runner_->RunsTasksInCurrentSequence()); -@@ -297,7 +380,7 @@ void CreateStream(SCContentFilter* filter) { +@@ -301,7 +384,7 @@ void CreateStream(SCContentFilter* filter) { return; } @@ -201,7 +201,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 // Update the content size. This step is neccessary when used together // with SCContentSharingPicker. If the Chrome picker is used, it will // change to retina resolution if applicable. -@@ -306,6 +389,9 @@ void CreateStream(SCContentFilter* filter) { +@@ -310,6 +393,9 @@ void CreateStream(SCContentFilter* filter) { filter.contentRect.size.height * filter.pointPixelScale); } @@ -211,7 +211,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 gfx::RectF dest_rect_in_frame; actual_capture_format_ = capture_params().requested_format; actual_capture_format_.pixel_format = media::PIXEL_FORMAT_NV12; -@@ -319,6 +405,7 @@ void CreateStream(SCContentFilter* filter) { +@@ -323,6 +409,7 @@ void CreateStream(SCContentFilter* filter) { stream_ = [[SCStream alloc] initWithFilter:filter configuration:config delegate:helper_]; @@ -219,7 +219,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 { NSError* error = nil; bool add_stream_output_result = -@@ -472,7 +559,7 @@ void OnStreamError() { +@@ -480,7 +567,7 @@ void OnStreamError() { if (fullscreen_module_) { fullscreen_module_->Reset(); } @@ -228,7 +228,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 } else { client()->OnError(media::VideoCaptureError::kScreenCaptureKitStreamError, FROM_HERE, "Stream delegate called didStopWithError"); -@@ -495,23 +582,41 @@ void OnUpdateConfigurationError() { +@@ -503,23 +590,41 @@ void OnUpdateConfigurationError() { } // IOSurfaceCaptureDeviceBase: @@ -285,7 +285,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 } void OnStop() override { DCHECK(device_task_runner_->RunsTasksInCurrentSequence()); -@@ -569,8 +674,9 @@ void ResetStreamTo(SCWindow* window) override { +@@ -577,8 +682,9 @@ void ResetStreamTo(SCWindow* window) override { } private: @@ -296,7 +296,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 const scoped_refptr device_task_runner_; // The actual format of the video frames that are sent to `client`. -@@ -586,6 +692,10 @@ void ResetStreamTo(SCWindow* window) override { +@@ -594,6 +700,10 @@ void ResetStreamTo(SCWindow* window) override { // Helper class that acts as output and delegate for `stream_`. ScreenCaptureKitDeviceHelper* __strong helper_; @@ -307,7 +307,7 @@ index 8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c..8749fa47bd841fd5ae57899cb3447378 // This is used to detect when a captured presentation enters fullscreen mode. // If this happens, the module will call the ResetStreamTo function. std::unique_ptr fullscreen_module_; -@@ -598,6 +708,8 @@ void ResetStreamTo(SCWindow* window) override { +@@ -606,6 +716,8 @@ void ResetStreamTo(SCWindow* window) override { base::WeakPtrFactory weak_factory_{this}; }; diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch index ad6cfd909bc43..6ff014b7abe3a 100644 --- a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -90,10 +90,10 @@ index 8af69cac78b7488d28f1f05ccb174793fe5148cd..9f74e511c263d147b5fbe81fe100d217 private: const HWND hwnd_; diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index 610ed4c922705697b27af569af55064daeff5d25..9cb52cb768e648235f89f65e870d853d45268900 100644 +index 41c3a5128b9936f48587d6fc6ddd2050bffc9c36..4dbcba1150645596532c53b99e309c0e52d4759f 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn -@@ -168,6 +168,8 @@ viz_component("service") { +@@ -178,6 +178,8 @@ viz_component("service") { "display_embedder/skia_output_surface_impl_on_gpu_debug_capture.h", "display_embedder/skia_render_copy_results.cc", "display_embedder/skia_render_copy_results.h", @@ -522,7 +522,7 @@ index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109cc waiting_on_draw_ack_ = true; diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -index d119eb1e217cf90c85bf752f1530345b1eec01f2..655ed4ee5525a126309f173775a28e183b4dd29a 100644 +index e7ba8edf2b6e2228bc142bb164c9cd5a147ce5ad..a278a3eca9eedf53ca3878fc8c41d42de3a1b708 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc @@ -112,7 +112,8 @@ RootCompositorFrameSinkImpl::Create( @@ -564,7 +564,7 @@ index 399fba1a3d4e601dc2cdd5f1f4def8b7fd7a3011..8bcbe0d26c80323155d536c0d3a177a1 gpu::SyncPointManager* GetSyncPointManager() override; gpu::Scheduler* GetGpuScheduler() override; diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc -index 378910976c702d81c263973d29e17646c3963494..84553085b75b8cb9296c4af726f9a7bfc0318980 100644 +index d0ea177f54b8db8837840e22f5cdb6b64a0c9605..94e4a27fefff406e1d09e7867c0e84e43e6027cf 100644 --- a/content/browser/compositor/viz_process_transport_factory.cc +++ b/content/browser/compositor/viz_process_transport_factory.cc @@ -441,8 +441,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( @@ -620,7 +620,7 @@ index 2f462f0deb5fc8a637457243fb5d5849fc214d14..695869b83cefaa24af93a2e11b39de05 + Draw(gfx.mojom.Rect damage_rect) => (); }; diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492cff428e1 100644 +index d353cd4613c734c34f5333607335cc9a434ad88e..cabb39363b30c97f63d323d7a8156774b67f6b32 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h @@ -92,6 +92,7 @@ class DisplayPrivate; @@ -657,7 +657,7 @@ index 7f71cc6b8812cc112262d3e9153884e428a7e495..8a61b0cc5d45a55ac188babf63b59492 // Sets the root of the layer tree drawn by this Compositor. The root layer // must have no parent. The compositor's root layer is reset if the root layer // is destroyed. NULL can be passed to reset the root layer, in which case the -@@ -620,6 +633,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -616,6 +629,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, simple_begin_frame_observers_; std::unique_ptr host_begin_frame_observer_; diff --git a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch index 113ebc72ed50b..9d378513a70ec 100644 --- a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch +++ b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch @@ -17,10 +17,10 @@ headers, moving forward we should find a way in upstream to provide access to these headers for loader clients created on the browser process. diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc -index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d06ed7152 100644 +index b60fd23a39eb423450b57275526ac4ba36058225..deafb4cd5a55853eb11a1371ca8331ee5f6050b2 100644 --- a/services/network/public/cpp/resource_request.cc +++ b/services/network/public/cpp/resource_request.cc -@@ -177,6 +177,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=( +@@ -178,6 +178,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=( allow_cookies_from_browser = other.allow_cookies_from_browser; include_request_cookies_with_response = other.include_request_cookies_with_response; @@ -28,7 +28,7 @@ index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d cookie_observer = Clone(&const_cast&>( other.cookie_observer)); -@@ -211,6 +212,7 @@ bool ResourceRequest::TrustedParams::EqualsForTesting( +@@ -212,6 +213,7 @@ bool ResourceRequest::TrustedParams::EqualsForTesting( const TrustedParams& other) const { return isolation_info.IsEqualForTesting(other.isolation_info) && disable_secure_dns == other.disable_secure_dns && @@ -37,10 +37,10 @@ index babcf42e01be00da8b853a1207e869614765b2bb..1c051a64d77e3ea8909dced73d93764d allow_cookies_from_browser == other.allow_cookies_from_browser && include_request_cookies_with_response == diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h -index 3d1e70bd812c18206846f4188b85321e931964bb..cc3aad56c873bd97adb1179b1626d14ef346378c 100644 +index 6430943ce0a4817d2806f701ef39b4263648ce6e..4b8eddb96f739e86cb35f7bfe2d9742de8dcecda 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h -@@ -76,6 +76,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { +@@ -77,6 +77,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { bool has_user_activation = false; bool allow_cookies_from_browser = false; bool include_request_cookies_with_response = false; @@ -49,10 +49,10 @@ index 3d1e70bd812c18206846f4188b85321e931964bb..cc3aad56c873bd97adb1179b1626d14e mojo::PendingRemote trust_token_observer; mojo::PendingRemote diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc -index de0780c4f29026d3cc7d2742d8e80acddc6916ad..76c724151ee12fccecd36d2e9a6b7f264a9907d7 100644 +index 33f4def7593dac366034b5a8e83b5079a726f4b7..cd83b5d95099af724d1d04642cad313905c5d3a1 100644 --- a/services/network/public/cpp/url_request_mojom_traits.cc +++ b/services/network/public/cpp/url_request_mojom_traits.cc -@@ -48,6 +48,7 @@ bool StructTraitsallow_cookies_from_browser = data.allow_cookies_from_browser(); out->include_request_cookies_with_response = data.include_request_cookies_with_response(); @@ -61,10 +61,10 @@ index de0780c4f29026d3cc7d2742d8e80acddc6916ad..76c724151ee12fccecd36d2e9a6b7f26 mojo::PendingRemote>(); out->trust_token_observer = data.TakeTrustTokenObserver< diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h -index ebd70d33c9e7df5abd35ed3683f39d0e7fc8b34d..45dd29445589dec900221edb65f8fe566b81ca6e 100644 +index cdf5283b5b3e58f77f37e9501d2b867493fa6cf7..86e41493fcffb0d9fc9b4306714b910e2f2963e0 100644 --- a/services/network/public/cpp/url_request_mojom_traits.h +++ b/services/network/public/cpp/url_request_mojom_traits.h -@@ -70,6 +70,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) +@@ -71,6 +71,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) const network::ResourceRequest::TrustedParams& trusted_params) { return trusted_params.include_request_cookies_with_response; } @@ -76,10 +76,10 @@ index ebd70d33c9e7df5abd35ed3683f39d0e7fc8b34d..45dd29445589dec900221edb65f8fe56 cookie_observer( const network::ResourceRequest::TrustedParams& trusted_params) { diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom -index 3e5dae789846c667f7537b04252a49b28298d53d..a229e56674cc75e8dfb78c26962f108ffd59c5e9 100644 +index 8fae9662ac05c94dc3545a125cb5e838664c97c2..5abf38ed936163b14a4bfd613728f03f1494c6df 100644 --- a/services/network/public/mojom/url_request.mojom +++ b/services/network/public/mojom/url_request.mojom -@@ -73,6 +73,9 @@ struct TrustedUrlRequestParams { +@@ -74,6 +74,9 @@ struct TrustedUrlRequestParams { // client which should not be able to see them. bool include_request_cookies_with_response = false; @@ -112,10 +112,10 @@ index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b string mime_type; diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc -index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf333f5d799 100644 +index d11d199c44c1f4d72765714d772066d4a36ad8e2..3d5c61726679cedfc6d9e9587eaedfe4ada2f1f9 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc -@@ -666,6 +666,9 @@ URLLoader::URLLoader( +@@ -667,6 +667,9 @@ URLLoader::URLLoader( mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunner::GetCurrentDefault()), per_factory_orb_state_(context.GetMutableOrbState()), @@ -125,7 +125,7 @@ index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf3 devtools_request_id_(request.devtools_request_id), options_(PopulateOptions(options, factory_params_->is_orb_enabled, -@@ -963,7 +966,7 @@ void URLLoader::ConfigureRequest( +@@ -964,7 +967,7 @@ void URLLoader::ConfigureRequest( &URLLoader::IsSharedDictionaryReadAllowed, base::Unretained(this))); } @@ -155,10 +155,10 @@ index 4a55e0381a239294a868b5aece518bed26985e83..7daf3e28f12084a3ba8ca132d846dcf3 // Parse and remove the Trust Tokens response headers, if any are expected, diff --git a/services/network/url_loader.h b/services/network/url_loader.h -index 5e7d17112b41157117839c65cc13dbc56311d5f0..b40bafde39101124368dd5e5e221240a6eb7fece 100644 +index c45d947ad9059df5694eccd2c20774248e951c6f..d4d37eb83fcae5145922346db8d7200ddd33002d 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h -@@ -710,6 +710,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader +@@ -711,6 +711,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader std::unique_ptr resource_scheduler_request_handle_; diff --git a/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch b/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch index 3928f3d7244c7..5da28b3c084f9 100644 --- a/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch +++ b/patches/chromium/feat_filter_out_non-shareable_windows_in_the_current_application_in.patch @@ -7,10 +7,10 @@ Subject: feat: filter out non-shareable windows in the current application in This patch ensures that windows protected via win.setContentProtection(true) do not appear in full display captures via desktopCapturer. This patch could be upstreamed but as the check is limited to in-process windows it doesn't make a lot of sense for Chromium itself. This patch currently has a limitation that it only function for windows created / protected BEFORE the stream is started. There is theoretical future work we can do via polling / observers to automatically update the SCContentFilter when new windows are made but for now this will solve 99+% of the problem and folks can re-order their logic a bit to get it working for their use cases. diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm -index 8e7fb47f5f28f40e8e3bd00d2bb1c928112976e4..8d5a5d16fa08b9eb4abcf167a0d5acd8351a582c 100644 +index 42705a5c59fb76ba4a6a17a060e215436307de49..7e17594c30ac3cf8cb484b53563b03fc75bd2e0b 100644 --- a/content/browser/media/capture/screen_capture_kit_device_mac.mm +++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm -@@ -250,8 +250,17 @@ void OnShareableContentCreated(SCShareableContent* content) { +@@ -254,8 +254,17 @@ void OnShareableContentCreated(SCShareableContent* content) { // fallback. See https://crbug.com/325530044. if (source_.id == display.displayID || source_.id == webrtc::kFullDesktopScreenId) { diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index 9131aa33730a5..ae8980e16502f 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,10 +14,10 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 9888ac709c51cd30228e3bca6915f7d89071cb83..e7b2bc1905958d2ff9c34ed7a22eb53e7ea3b9b8 100644 +index e061dc5e64fde1a9c531dbedae0c137105700ac7..3d0cc571b471bb79d845f8b0a1da168ace33bb6b 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -932,13 +932,13 @@ void HWNDMessageHandler::FrameTypeChanged() { +@@ -934,13 +934,13 @@ void HWNDMessageHandler::FrameTypeChanged() { void HWNDMessageHandler::PaintAsActiveChanged() { if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || @@ -33,7 +33,7 @@ index 9888ac709c51cd30228e3bca6915f7d89071cb83..e7b2bc1905958d2ff9c34ed7a22eb53e } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -2327,17 +2327,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, +@@ -2329,17 +2329,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, delegate_->SchedulePaint(); } diff --git a/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch b/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch index ea3f64becd912..aed4072d110f6 100644 --- a/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch +++ b/patches/chromium/fix_adapt_exclusive_access_for_electron_needs.patch @@ -16,7 +16,7 @@ Linux or Windows to un-fullscreen in some circumstances without this change. diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc -index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f25d04780 100644 +index 91db1bbd912807833243c101e126e42ad5579d2b..7c624e0babddbca7c0deef1d805ccbfc21e6e9a9 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc @@ -20,12 +20,16 @@ @@ -50,7 +50,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // Keep the current state. |SetTabWithExclusiveAccess| may change the return // value of |IsWindowFullscreenForTabOrPending|. -@@ -389,12 +395,14 @@ void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) { +@@ -388,12 +394,14 @@ void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) { void FullscreenController::FullscreenTabOpeningPopup( content::WebContents* opener, content::WebContents* popup) { @@ -65,7 +65,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f } void FullscreenController::OnTabDeactivated( -@@ -484,10 +492,12 @@ void FullscreenController::FullscreenTransitionCompleted() { +@@ -483,10 +491,12 @@ void FullscreenController::FullscreenTransitionCompleted() { #endif // DCHECK_IS_ON() tab_fullscreen_target_display_id_ = display::kInvalidDisplayId; started_fullscreen_transition_ = false; @@ -78,7 +78,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f } void FullscreenController::RunOrDeferUntilTransitionIsComplete( -@@ -612,18 +622,17 @@ void FullscreenController::EnterFullscreenModeInternal( +@@ -616,18 +626,17 @@ void FullscreenController::EnterFullscreenModeInternal( // Do not enter fullscreen mode if disallowed by pref. This prevents the user // from manually entering fullscreen mode and also disables kiosk mode on // desktop platforms. @@ -97,20 +97,20 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f +#if 0 bool entering_tab_fullscreen = option == TAB && !tab_fullscreen_; +#endif - GURL url; + url::Origin origin; +#if 0 if (option == TAB) { - url = GetRequestingOrigin(); + origin = GetRequestingOrigin(); tab_fullscreen_ = true; -@@ -658,6 +667,7 @@ void FullscreenController::EnterFullscreenModeInternal( - url = extension_caused_fullscreen_; +@@ -662,6 +671,7 @@ void FullscreenController::EnterFullscreenModeInternal( + origin = url::Origin::Create(extension_url_.value()); } } +#endif fullscreen_start_time_ = base::TimeTicks::Now(); if (option == BROWSER) { -@@ -679,6 +689,7 @@ void FullscreenController::ExitFullscreenModeInternal() { +@@ -683,6 +693,7 @@ void FullscreenController::ExitFullscreenModeInternal() { return; } @@ -118,7 +118,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // `fullscreen_start_time_` is null if a fullscreen tab moves to a new window. if (fullscreen_start_time_ && exclusive_access_tab()) { ukm::SourceId source_id = -@@ -690,15 +701,16 @@ void FullscreenController::ExitFullscreenModeInternal() { +@@ -694,15 +705,16 @@ void FullscreenController::ExitFullscreenModeInternal() { .Record(ukm::UkmRecorder::Get()); fullscreen_start_time_.reset(); } @@ -136,9 +136,9 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f -#endif + exclusive_access_manager()->context()->ExitFullscreen(); - extension_caused_fullscreen_ = GURL(); + extension_url_.reset(); exclusive_access_manager()->UpdateBubble(base::NullCallback()); -@@ -762,8 +774,12 @@ GURL FullscreenController::GetEmbeddingOrigin() const { +@@ -766,8 +778,12 @@ url::Origin FullscreenController::GetEmbeddingOrigin() const { void FullscreenController::RecordMetricsOnFullscreenApiRequested( content::RenderFrameHost* requesting_frame) { history::HistoryService* service = @@ -152,7 +152,7 @@ index 458029244ad9db7beace040778117d82d90f4bea..66202201b11de10bf2d781a52640a81f // Check if the origin has been visited more than a day ago and whether it's // on an allowlist, then record those bits of information in a metric. diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h -index 7fdf883310b13e3334ba007580a1a4287d49d592..d7c2bdaa08cae1645bf52f1d9b9d84b1cdb78c66 100644 +index 5854fd8401f3934cabd6abfe48da332d7a670af3..d714c44a6fd2206f9192dbfe82787a4b06042ed0 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller.h +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.h @@ -250,10 +250,12 @@ class FullscreenController : public ExclusiveAccessControllerBase { diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch index eb2207994d0b7..44376494c4f73 100644 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch @@ -12,10 +12,10 @@ ui problems (like dissapearing popup during typing in html's input list. diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index 842ea6f7eb2415170a59e46ca955165d656ba149..1ea926753f65baa8f9671fdd63f98fb94c28ba6c 100644 +index bfea25523fd1df85d9393fce80259a8654b8a7c2..e3cd3083bc8c19fdc29729d7ff6db7d0fc562ce6 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h -@@ -1200,6 +1200,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, +@@ -1209,6 +1209,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // True if widget was created in headless mode. bool is_headless() const { return is_headless_; } diff --git a/patches/chromium/fix_aspect_ratio_with_max_size.patch b/patches/chromium/fix_aspect_ratio_with_max_size.patch index 82dc65b890e2e..a9a44c05296b8 100644 --- a/patches/chromium/fix_aspect_ratio_with_max_size.patch +++ b/patches/chromium/fix_aspect_ratio_with_max_size.patch @@ -11,10 +11,10 @@ enlarge window above dimensions set during creation of the BrowserWindow. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index a802f4b710b6f8fa154d11846c061720a91e15e4..2bd015be3178ab8dea012d6b1f71d13dd0548c14 100644 +index d1146216b6969562876a318586420636331f63d1..7f29d902ae0a2f980a56e77e6e25935dab3c0685 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3747,15 +3747,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, +@@ -3749,15 +3749,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, delegate_->GetMinMaxSize(&min_window_size, &max_window_size); min_window_size = delegate_->DIPToScreenSize(min_window_size); max_window_size = delegate_->DIPToScreenSize(max_window_size); diff --git a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch index 1e72fde2fe54e..1336f863a8bda 100644 --- a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch +++ b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch @@ -28,10 +28,10 @@ The patch should be removed in favor of either: Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397. diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc -index 73f61aa418db85558a0f8cfa574ee2e2f575ef90..4f376522e87fc8075eca6705b1bb781c86cde947 100644 +index 0bd3df72a9a5f127cb8215ab4182e143ca99548d..f8b00313490e898c0d70667b996b3e66a0063c4b 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc -@@ -11048,6 +11048,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { +@@ -11059,6 +11059,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { "blob"); } @@ -45,10 +45,10 @@ index 73f61aa418db85558a0f8cfa574ee2e2f575ef90..4f376522e87fc8075eca6705b1bb781c // origin of |common_params.url| and/or |common_params.initiator_origin|. url::Origin resolved_origin = url::Origin::Resolve( diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc -index 0c7b5af098a53a8709cdf62d455520ccef222dbb..1b6a7942c1506abc2fbe7d1efe58c0964a4e3be0 100644 +index ab265777ded9c875ccafd83b44853d92e35c60fb..074fa3544afd423555578cf0df835171ab559201 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc -@@ -2336,6 +2336,10 @@ Frame* DocumentLoader::CalculateOwnerFrame() { +@@ -2334,6 +2334,10 @@ Frame* DocumentLoader::CalculateOwnerFrame() { scoped_refptr DocumentLoader::CalculateOrigin( Document* owner_document) { scoped_refptr origin; @@ -59,7 +59,7 @@ index 0c7b5af098a53a8709cdf62d455520ccef222dbb..1b6a7942c1506abc2fbe7d1efe58c096 StringBuilder debug_info_builder; // Whether the origin is newly created within this call, instead of copied // from an existing document's origin or from `origin_to_commit_`. If this is -@@ -2389,6 +2393,10 @@ scoped_refptr DocumentLoader::CalculateOrigin( +@@ -2387,6 +2391,10 @@ scoped_refptr DocumentLoader::CalculateOrigin( // the end of this function. origin = origin_to_commit_; debug_info_builder.Append("use_origin_to_commit"); diff --git a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch index e1e6bb6555055..4134ae484789e 100644 --- a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch +++ b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch @@ -53,10 +53,10 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb void Compositor::SetSeamlessRefreshRates( const std::vector& seamless_refresh_rates) { diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index 8a61b0cc5d45a55ac188babf63b59492cff428e1..07200e5320c999f1a067ce785b7ad5c7952586d7 100644 +index cabb39363b30c97f63d323d7a8156774b67f6b32..54e012fa4b0253d8c8f88cc2021be0dcf77cbdee 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -523,6 +523,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -519,6 +519,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, const cc::LayerTreeSettings& GetLayerTreeSettings() const; @@ -67,7 +67,7 @@ index 8a61b0cc5d45a55ac188babf63b59492cff428e1..07200e5320c999f1a067ce785b7ad5c7 size_t saved_events_metrics_count_for_testing() const { return host_->saved_events_metrics_count_for_testing(); } -@@ -714,6 +718,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -710,6 +714,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // See go/report-ux-metrics-at-painting for details. bool animation_started_ = false; diff --git a/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch b/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch index f70fb46267ec1..f0002496c6df6 100644 --- a/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch +++ b/patches/chromium/fix_harden_blink_scriptstate_maybefrom.patch @@ -40,7 +40,7 @@ accessing uninitialized lower indexes can return garbage values that cannot be n Refer to v8::EmbedderDataSlot::store_aligned_pointer for context. diff --git a/gin/public/gin_embedders.h b/gin/public/gin_embedders.h -index 8d7c5631fd8f1499c67384286f0e3c4037673b32..99b2e2f63be8a46c5546dd53bc9b05e8c54e857c 100644 +index 8d7c5631fd8f1499c67384286f0e3c4037673b32..2b7bdfbac06a42e6bc51eb65e023c3673e6eb885 100644 --- a/gin/public/gin_embedders.h +++ b/gin/public/gin_embedders.h @@ -20,6 +20,8 @@ enum GinEmbedder : uint16_t { @@ -86,7 +86,7 @@ index e4a27a24c83dd1a478b2ada8b6c8220076790791..c76dc818f38a62fff63852dbecbc85e3 // Cut the reference from ScriptState to V8 context. diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h -index b3cc8d819b06108386aed9465cab4f27a28b675f..a1757901e52360a9c2ec3c573adb20d03cd6ecae 100644 +index b3cc8d819b06108386aed9465cab4f27a28b675f..9c8818f10de59fdd2a3fd44d9cd23d40a93b53a7 100644 --- a/third_party/blink/renderer/platform/bindings/script_state.h +++ b/third_party/blink/renderer/platform/bindings/script_state.h @@ -185,7 +185,12 @@ class PLATFORM_EXPORT ScriptState : public GarbageCollected { diff --git a/patches/chromium/fix_linter_error.patch b/patches/chromium/fix_linter_error.patch new file mode 100644 index 0000000000000..d9b2b3bb335b1 --- /dev/null +++ b/patches/chromium/fix_linter_error.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Charles Kerr +Date: Thu, 6 Mar 2025 17:06:49 -0600 +Subject: fix: linter error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +fix: linter error + +This is showing up in an eslint build step in Electron: + +> /__w/electron/electron/src/out/Default/gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts +> 77:23 error This assertion is unnecessary since the receiver accepts the original type of the expression @typescript-eslint/no-unnecessary-type-assertion +> +> ✖ 1 problem (1 error, 0 warnings) +> 1 error and 0 warnings potentially fixable with the `--fix` option. + +However, removing the assertion causes a typescript build failure: + +> gen/ui/webui/resources/cr_elements/preprocessed/cr_menu_selector/cr_menu_selector.ts:77:23 - error TS2345: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. +> Type 'null' is not assignable to type 'HTMLElement'. +> +> 77 items.indexOf(this.querySelector(':focus')); +> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So I think the two different steps may be picking up typescript definitions. + +This patch should be removed after the issue is tracked down +andfixed in a followup task. + +diff --git a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts +index 0a83b8041b8201c95442e680c77555d4c11bc06a..abdb8e9bfbbfb1fce6fa38e226e50a35477e49a2 100644 +--- a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts ++++ b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts +@@ -74,6 +74,7 @@ export class CrMenuSelector extends CrMenuSelectorBase { + const items = this.getAllFocusableItems_(); + assert(items.length >= 1); + const currentFocusedIndex = ++ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + items.indexOf(this.querySelector(':focus')!); + + let newFocusedIndex = currentFocusedIndex; diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index 0b51bde982d26..16b2a230f311e 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -9,10 +9,10 @@ focus node change via TextInputManager. chromium-bug: https://crbug.com/1369605 diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 9503beefcbcfe7d99674582ece10a7e551fae96d..32302e3dae2098319ed2d1f1403649fe8db2887e 100644 +index 7e9f2e71f08c1324a805462064d4fa485041c19f..2b4543ca40eac0f56c6408e27aac523827093724 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc -@@ -3229,6 +3229,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( +@@ -3220,6 +3220,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( } } @@ -39,7 +39,7 @@ index 06fc7daf2761ae728f3adf9eb2ef3910a14cf827..8d34f9dfd9a8625a3fab397f7b96c12c // Detaches |this| from the input method object. // is_removed flag is true if this is called while the window is diff --git a/content/browser/renderer_host/text_input_manager.cc b/content/browser/renderer_host/text_input_manager.cc -index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc41cf5021 100644 +index 4f1d98e960b58cf417d53c487f7eafa2b15c2c1e..1de7a9b74555e01e17ea89ac457dc4d011001a3c 100644 --- a/content/browser/renderer_host/text_input_manager.cc +++ b/content/browser/renderer_host/text_input_manager.cc @@ -183,6 +183,7 @@ void TextInputManager::UpdateTextInputState( @@ -50,7 +50,7 @@ index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc // We reached here because an IPC is received to reset the TextInputState // for |view|. But |view| != |active_view_|, which suggests that at least // one other view has become active and we have received the corresponding -@@ -485,6 +486,12 @@ void TextInputManager::NotifyObserversAboutInputStateUpdate( +@@ -473,6 +474,12 @@ void TextInputManager::NotifyObserversAboutInputStateUpdate( observer.OnUpdateTextInputStateCalled(this, updated_view, did_update_state); } @@ -64,10 +64,10 @@ index 9a0aad0d4a3dada0faec031d155c6d346355e8a5..8852c53847bbe2f906aff4bdacc355fc TextInputManager::SelectionRegion::SelectionRegion( diff --git a/content/browser/renderer_host/text_input_manager.h b/content/browser/renderer_host/text_input_manager.h -index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f9394fc9a72 100644 +index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac46df71d6a 100644 --- a/content/browser/renderer_host/text_input_manager.h +++ b/content/browser/renderer_host/text_input_manager.h -@@ -72,6 +72,10 @@ class CONTENT_EXPORT TextInputManager { +@@ -69,6 +69,10 @@ class CONTENT_EXPORT TextInputManager { virtual void OnTextSelectionChanged( TextInputManager* text_input_manager, RenderWidgetHostViewBase* updated_view) {} @@ -78,7 +78,7 @@ index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f93 }; // Text selection bounds. -@@ -308,6 +312,7 @@ class CONTENT_EXPORT TextInputManager { +@@ -304,6 +308,7 @@ class CONTENT_EXPORT TextInputManager { void NotifyObserversAboutInputStateUpdate(RenderWidgetHostViewBase* view, bool did_update_state); @@ -87,10 +87,10 @@ index 51522e60d6dc14f1113cc438558b6b393c3fe73a..153ed02f493a83ef9ca354cc18736f93 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 55840c1cf5323d1796f1c3b056602411f513a220..86a1a0244a3744d0571c046d3e6fc9413c9fbbf0 100644 +index 6f323c5863f4bb869a66d265fac1d372c5243187..a16c2368a4e37756642544c45d3e4b1fe7d64b26 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9739,7 +9739,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9768,7 +9768,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index fcf8f6764eb5e..1811e95183f35 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,10 +18,10 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 759f00dd4e674d1dfca690b82e6e1820900ebf0c..9888ac709c51cd30228e3bca6915f7d89071cb83 100644 +index 679efe5af4954d9ddca7bc5ccee6b8d6fac966a3..e061dc5e64fde1a9c531dbedae0c137105700ac7 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1786,7 +1786,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { +@@ -1788,7 +1788,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); diff --git a/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch b/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch index 429054930724c..68178f7d8751c 100644 --- a/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch +++ b/patches/chromium/fix_remove_profiles_from_spellcheck_service.patch @@ -6,13 +6,13 @@ Subject: fix: remove profiles from spellcheck_service Electron doesn't support profiles. diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc -index 979630ba7ffe84d78804a02b832ce7787aecf2d9..3b1cdaab5d3229c802c8da53a4fa17a464978391 100644 +index 910534aac947890b16b176b10ac8eae9857bd2d7..1bb22b89b6ec4d620354bb7747c37cae9dd3a57e 100644 --- a/chrome/browser/spellchecker/spellcheck_service.cc +++ b/chrome/browser/spellchecker/spellcheck_service.cc -@@ -21,8 +21,10 @@ +@@ -20,8 +20,10 @@ + #include "base/synchronization/waitable_event.h" #include "base/values.h" #include "build/build_config.h" - #include "build/chromeos_buildflags.h" +#if 0 #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profiles_state.h" @@ -20,7 +20,7 @@ index 979630ba7ffe84d78804a02b832ce7787aecf2d9..3b1cdaab5d3229c802c8da53a4fa17a4 #include "chrome/browser/spellchecker/spellcheck_factory.h" #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" #include "components/language/core/browser/pref_names.h" -@@ -67,7 +69,10 @@ SpellcheckService::SpellCheckerBinder& GetSpellCheckerBinderOverride() { +@@ -66,7 +68,10 @@ SpellcheckService::SpellCheckerBinder& GetSpellCheckerBinderOverride() { // Only record spelling-configuration metrics for profiles in which the user // can configure spelling. bool RecordSpellingConfigurationMetrics(content::BrowserContext* context) { diff --git a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch index 6e1b26daf7139..92c7ec90f2052 100644 --- a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch +++ b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch @@ -11,7 +11,7 @@ This patch should be upstreamed as a conditional revert of the logic in desktop vs mobile runtimes. i.e. restore the old logic only on desktop platforms diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 574e31bcfeb16cdb9a6c0121ffbc1abe175bb4ab..70b56daa9f59db356755b98014f0fce84980c554 100644 +index e46e5eb74c54bc00ead6e2e4eff99fd500e09262..c07ffdb0ae8b275a72d789e9091a807362379474 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -2114,9 +2114,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { diff --git a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch index a109363b5a616..736bd84f9e9cd 100644 --- a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch +++ b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch @@ -20,10 +20,10 @@ index fc9cb7e68bdad4c40fab63f70243e09ad005ab80..199fbceda530da31aab9126d78b4bd21 injector_->ExpectsResults(), injector_->ShouldWaitForPromise()); } diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h -index a23931e7a94da9965e70c9f03767b4decf2ae6ea..20340191c2a1e8f9246d4800e3d790340232142f 100644 +index 3758966f3cdfb8c37f3b23b1a903996b9d1ac5fb..a1c908e3281a03600268332d6cde2051aa225ff6 100644 --- a/third_party/blink/public/web/web_local_frame.h +++ b/third_party/blink/public/web/web_local_frame.h -@@ -458,6 +458,7 @@ class BLINK_EXPORT WebLocalFrame : public WebFrame { +@@ -460,6 +460,7 @@ class BLINK_EXPORT WebLocalFrame : public WebFrame { mojom::EvaluationTiming, mojom::LoadEventBlockingOption, WebScriptExecutionCallback, @@ -59,10 +59,10 @@ index cba373664bec3a32abad6fe0396bd67b53b7e67f..a54f1b3351efd2d8f324436f7f35cd43 #endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_EXECUTION_CALLBACK_H_ diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182485b201d 100644 +index feb30ced77cfc7d0a1faa637cd8bdec399155504..6137dc5905604e4309466dd82b2d2fe31e5cd6a7 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -3092,6 +3092,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3097,6 +3097,7 @@ void LocalFrame::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -70,7 +70,7 @@ index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182 BackForwardCacheAware back_forward_cache_aware, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_behavior) { -@@ -3124,7 +3125,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3129,7 +3130,7 @@ void LocalFrame::RequestExecuteScript( PausableScriptExecutor::CreateAndRun( script_state, std::move(script_sources), execute_script_policy, user_gesture, evaluation_timing, blocking_option, want_result_option, @@ -80,10 +80,10 @@ index 4da759d7ab6d6fb6b6d4d1648da0e62d7629ad5c..7e537b54b11220f3bc6619b027677182 void LocalFrame::SetEvictCachedSessionStorageOnFreezeOrUnload() { diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h -index b90e58cb52d32c10d5dcabf3de001b510e1a4cc4..c2ba0c05a74fc786684b50093720e509c6f723c3 100644 +index 80006535d1b7a7bfff69ebc46a1d9f70ee8c609d..842aa5f37e5f09235969b8510d4aa914e385d1ad 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h -@@ -814,6 +814,7 @@ class CORE_EXPORT LocalFrame final +@@ -815,6 +815,7 @@ class CORE_EXPORT LocalFrame final mojom::blink::EvaluationTiming, mojom::blink::LoadEventBlockingOption, WebScriptExecutionCallback, @@ -104,10 +104,10 @@ index b77ad041a717ef8f317a9d15edd61af3c465b2aa..e3955c64ff894682b91c4cc3c49c6726 wants_result ? mojom::blink::WantResultOption::kWantResultDateAndRegExpAllowed diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc -index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e0564efb3096 100644 +index 2d674cef2d653ed555cd99848f37c67e29eb00fa..cbc8b8b642376959f366a03bd04eace55f9d02bb 100644 --- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc +++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc -@@ -246,7 +246,7 @@ void PausableScriptExecutor::CreateAndRun( +@@ -243,7 +243,7 @@ void PausableScriptExecutor::CreateAndRun( script_state, mojom::blink::UserActivationOption::kDoNotActivate, mojom::blink::LoadEventBlockingOption::kDoNotBlock, want_result_option, mojom::blink::PromiseResultOption::kDoNotWait, @@ -116,7 +116,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 MakeGarbageCollected( script_state->GetIsolate(), function, receiver, argc, argv)); executor->Run(); -@@ -261,10 +261,11 @@ void PausableScriptExecutor::CreateAndRun( +@@ -258,10 +258,11 @@ void PausableScriptExecutor::CreateAndRun( mojom::blink::LoadEventBlockingOption blocking_option, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_result_option, @@ -130,7 +130,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 MakeGarbageCollected(std::move(sources), execute_script_policy)); switch (evaluation_timing) { -@@ -286,6 +287,14 @@ void PausableScriptExecutor::ContextDestroyed() { +@@ -283,6 +284,14 @@ void PausableScriptExecutor::ContextDestroyed() { ScriptState::Scope script_scope(script_state_); std::move(callback_).Run({}, {}); } @@ -145,7 +145,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 Dispose(); } -@@ -296,10 +305,12 @@ PausableScriptExecutor::PausableScriptExecutor( +@@ -293,10 +302,12 @@ PausableScriptExecutor::PausableScriptExecutor( mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_result_option, WebScriptExecutionCallback callback, @@ -158,7 +158,7 @@ index 9b344e09e545b4a24deb2272d0f093d551866f97..f4d0431f981b3c521c4ce0620886e056 user_activation_option_(user_activation_option), blocking_option_(blocking_option), want_result_option_(want_result_option), -@@ -423,6 +434,13 @@ void PausableScriptExecutor::HandleResults( +@@ -420,6 +431,13 @@ void PausableScriptExecutor::HandleResults( std::move(callback_).Run(std::move(value), start_time_); } @@ -215,10 +215,10 @@ index 2e2aa78d308157642cf27941fc22a211f6396a0d..c5bcbe1f933f2e79003f7eb9f6368174 mojom::blink::WantResultOption::kWantResult, wait_for_promise); } diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -index cdd397ef9e9cba0b25fde78ddd7642ccf3cbdee9..331905c5b4fe488685fc8c156367dee15c602698 100644 +index 7f9cc8cfe501692d4dab847416298ff980aaa500..6524313c1c2a7520b5cf3c4e6abeb2e300c67ec0 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -@@ -1097,14 +1097,15 @@ void WebLocalFrameImpl::RequestExecuteScript( +@@ -1107,14 +1107,15 @@ void WebLocalFrameImpl::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -237,7 +237,7 @@ index cdd397ef9e9cba0b25fde78ddd7642ccf3cbdee9..331905c5b4fe488685fc8c156367dee1 bool WebLocalFrameImpl::IsInspectorConnected() { diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h -index 7bc07b1705207447f7727592049c201b011e6b5c..626d0501c0344d7c171f26c96dc78f5e85fb2a81 100644 +index 9ae322eb9d30c684ef18addf59201aad4474d34c..3a320e56db9b43c813b3e63f0394cc1c93f9c3b8 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h @@ -196,6 +196,7 @@ class CORE_EXPORT WebLocalFrameImpl final diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index ba06e6937f1e2..32a598712c035 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -6,10 +6,10 @@ Subject: frame_host_manager.patch Allows embedder to intercept site instances created by chromium. diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc -index 537b9313ca471e342c7dd894dd82e0ab67e23d2b..5573b22d28d1df7dfaa57b702511b78f9606d8d7 100644 +index e9f000ae316ac64cd23827f1533ec1416b3ed040..c3fb6dbd8f4c39e1087be567f6a2c2df69b19ef8 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc -@@ -4729,6 +4729,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( +@@ -4752,6 +4752,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( request->ResetStateForSiteInstanceChange(); } @@ -20,10 +20,10 @@ index 537b9313ca471e342c7dd894dd82e0ab67e23d2b..5573b22d28d1df7dfaa57b702511b78f } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index da93d31b658577bd4817b2dcfe366d444dbc2e92..96f76da6ea600f0b13bb78161a84c9c9dab19234 100644 +index faf574d7778e24d6fc9e3f539b39c9cb1c149bbc..ff3255b51e73aea4ec47cc30dfc0032de474ec06 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -340,6 +340,11 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -339,6 +339,11 @@ class CONTENT_EXPORT ContentBrowserClient { virtual ~ContentBrowserClient() = default; diff --git a/patches/chromium/gin_enable_disable_v8_platform.patch b/patches/chromium/gin_enable_disable_v8_platform.patch index 939c9ea078c92..d9ea89a0c86c9 100644 --- a/patches/chromium/gin_enable_disable_v8_platform.patch +++ b/patches/chromium/gin_enable_disable_v8_platform.patch @@ -41,10 +41,10 @@ index ff42cfbb6a228e902317c7e3ab035d8437d5dd62..e27f177ce27e177abf6cee84cd466e7a // Returns whether `Initialize` has already been invoked in the process. // Initialization is a one-way operation (i.e., this method cannot return diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index 532f0a1b5a7dbe6d8fd8c734e7f6b72dbc1df4ad..e72331d0821a14507e3c973f07889adcf45d3bdf 100644 +index 51c8cd7cc3f44936e1e33d9dc937d5a68d737dd6..578e200a88d89b356e991b3317ff1e71f25ff75e 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -541,7 +541,8 @@ void SetFeatureFlags() { +@@ -555,7 +555,8 @@ void SetFeatureFlags() { void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, const std::string& js_command_line_flags, bool disallow_v8_feature_flag_overrides, @@ -54,7 +54,7 @@ index 532f0a1b5a7dbe6d8fd8c734e7f6b72dbc1df4ad..e72331d0821a14507e3c973f07889adc static bool v8_is_initialized = false; if (v8_is_initialized) return; -@@ -555,7 +556,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, +@@ -570,7 +571,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, } SetFlags(mode, js_command_line_flags); diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index ec7f438c0a510..1325460b2d3ec 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,10 +6,10 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec -index 2cd6ca756a745e1387469914eb3d649dff104ccb..45ce48afff4178af11d3cb55b859c9b48aa5ab60 100644 +index dd7c5df4907dd369ff4df8f5d7286f83674f74f3..9215843a93e795123fd97848bcb2507f0960ed12 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec -@@ -1472,6 +1472,11 @@ +@@ -1476,6 +1476,11 @@ "<(SHARED_INTERMEDIATE_DIR)/third_party/blink/public/strings/permission_element_generated_strings.grd": { "META": {"sizes": {"messages": [2000],}}, "messages": [10080], diff --git a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch index 1df2ebda76fc8..ce72a672507a2 100644 --- a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +++ b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch @@ -11,10 +11,10 @@ Bug: N/A Change-Id: I9fc472212b2d3afac2c8e18a2159bc2d50bbdf98 diff --git a/AUTHORS b/AUTHORS -index 66e37c775006eaba80440a48b92cce825efd2944..8d03ca899968f24ef73c8563dfe8f13bf4fc6ee8 100644 +index 143ea35ef1591719589fa9a750177c7bae526fec..5883c4d63181be3bc95ee3ddbb0e33fb935e070b 100644 --- a/AUTHORS +++ b/AUTHORS -@@ -340,6 +340,7 @@ David Futcher +@@ -341,6 +341,7 @@ David Futcher David Jin David Lechner David Leen diff --git a/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch b/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch index f823d122dd8f2..3b90c3847d68c 100644 --- a/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch +++ b/patches/chromium/logging_win32_only_create_a_console_if_logging_to_stderr.patch @@ -9,10 +9,10 @@ be created for each child process, despite logs being redirected to a file. diff --git a/content/app/content_main.cc b/content/app/content_main.cc -index ae8261b3f62a1eec298c5a361c2966ccd3550cdd..d8ba4151c235b9beaade79d478a89d15ab9ba9f7 100644 +index ffc5c377428008a9cee3969e958e19357b3ecead..892fa6258a77a6ec3ddfa209a9d3fd63a372406c 100644 --- a/content/app/content_main.cc +++ b/content/app/content_main.cc -@@ -331,16 +331,14 @@ NO_STACK_PROTECTOR int RunContentProcess( +@@ -330,16 +330,14 @@ NO_STACK_PROTECTOR int RunContentProcess( #if BUILDFLAG(IS_WIN) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/patches/chromium/make_gtk_getlibgtk_public.patch b/patches/chromium/make_gtk_getlibgtk_public.patch index a63d5783f1edf..ef7434072eeda 100644 --- a/patches/chromium/make_gtk_getlibgtk_public.patch +++ b/patches/chromium/make_gtk_getlibgtk_public.patch @@ -7,10 +7,10 @@ Allows embedders to get a handle to the gdk_pixbuf library already loaded in the process. diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc -index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c70379358 100644 +index d6ac3233de3165d2be3e5d4fb3e221dca8b8d415..4f15069af07b7adc3e3c45e67e23ec8525668c21 100644 --- a/ui/gtk/gtk_compat.cc +++ b/ui/gtk/gtk_compat.cc -@@ -66,11 +66,6 @@ void* GetLibGio() { +@@ -69,11 +69,6 @@ void* GetLibGio() { return libgio; } @@ -22,7 +22,7 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c void* GetLibGdk3() { static void* libgdk3 = DlOpen("libgdk-3.so.0"); return libgdk3; -@@ -134,6 +129,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) { +@@ -150,6 +145,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) { } // namespace diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 058f6be906a3a..97ec5e3c6876c 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,7 +35,7 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index a8684cc2bf48a7df896847ad1b8f34308db3c28f..de55a37e5acaa72dbe8c898b6c909fc3b4b53dbc 100644 +index 998d8906b6bd33f48199dc5a67bbd149742eb561..90015e0be79e850a9225263e90b662fa3fbe005f 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -1031,6 +1031,7 @@ component("base") { @@ -269,11 +269,11 @@ index e9f4e5131238b9fb5f1b4b3e90a0cb84a7fc15b4..8b5f4cae3123ac5480ad73f0c873fca0 } // namespace diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm -index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e20955cf8d 100644 +index 92646b4978457724d46eb526d6f34cd308562f0b..c3e669ed8fb0682f7f973bb1c69d069bd6a3aa0c 100644 --- a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm +++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm @@ -9,7 +9,9 @@ - #include "components/remote_cocoa/app_shim/features.h" + #include "base/mac/mac_util.h" #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" +#include "electron/mas.h" @@ -298,7 +298,7 @@ index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e2 + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle { // - NSThemeFrame and its subclasses will be nil if it's missing at runtime. if ([BrowserWindowFrame class]) -@@ -163,6 +168,8 @@ - (BOOL)_usesCustomDrawing { +@@ -164,6 +169,8 @@ - (BOOL)_usesCustomDrawing { return NO; } @@ -307,7 +307,7 @@ index 3a8b44a2a295119f37ca37d5866dfcfa21121db0..b408e9c73fe97dd8885b5479923481e2 // Handle "Move focus to the window toolbar" configured in System Preferences -> // Keyboard -> Shortcuts -> Keyboard. Usually Ctrl+F5. The argument (|unknown|) // tends to just be nil. -@@ -173,8 +180,8 @@ - (void)_handleFocusToolbarHotKey:(id)unknown { +@@ -174,8 +181,8 @@ - (void)_handleFocusToolbarHotKey:(id)unknown { } - (void)setAlwaysShowTrafficLights:(BOOL)alwaysShow { @@ -449,7 +449,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec bool shouldShowWindowTitle = YES; if (_bridge) diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -index bcd3ea598687c5a85cddc862cdb16eab2d8dbd5d..2ed210685453409a2aa330bf718694f70a3d862d 100644 +index 9145261d8c3350e3df8844bf4109d9fda7b6ed46..b83c0faf8a6d4898e382d989348c8035086d0510 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm @@ -41,6 +41,7 @@ @@ -474,10 +474,10 @@ index bcd3ea598687c5a85cddc862cdb16eab2d8dbd5d..2ed210685453409a2aa330bf718694f7 // Beware: This view was briefly removed (in favor of a bare CALayer) in // https://crrev.com/c/1236675. The ordering of unassociated layers relative diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index b1cb8132b0e8d7bb18848d49634ecaaa939eefd4..610ed4c922705697b27af569af55064daeff5d25 100644 +index 3c4ab01a87d5011925e08ca296722d99ac7f33d4..41c3a5128b9936f48587d6fc6ddd2050bffc9c36 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn -@@ -371,6 +371,7 @@ viz_component("service") { +@@ -384,6 +384,7 @@ viz_component("service") { "frame_sinks/external_begin_frame_source_mac.h", ] } @@ -485,7 +485,7 @@ index b1cb8132b0e8d7bb18848d49634ecaaa939eefd4..610ed4c922705697b27af569af55064d } if (is_android || use_ozone) { -@@ -671,6 +672,7 @@ viz_source_set("unit_tests") { +@@ -684,6 +685,7 @@ viz_source_set("unit_tests") { "display_embedder/software_output_device_mac_unittest.mm", ] frameworks = [ "IOSurface.framework" ] @@ -545,7 +545,7 @@ index dbf334caa3a6d10017b69ad76802e389a011436b..da828823e8195cc9e497866363c9af93 void ForwardKeyboardEvent(const input::NativeWebKeyboardEvent& key_event, diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c85430bf1d76 100644 +index b712b8af0e770aa3acbeb1167b1a20bc1547c98a..fdb476a7e470c4b32649d4b3b7e4e44559b5b1c1 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -35,6 +35,7 @@ @@ -556,7 +556,7 @@ index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c854 #include "skia/ext/skia_utils_mac.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/input/input_handler.mojom.h" -@@ -2151,15 +2152,21 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -2142,15 +2143,21 @@ - (NSAccessibilityRole)accessibilityRole { // Since this implementation doesn't have to wait any IPC calls, this doesn't // make any key-typing jank. --hbono 7/23/09 // @@ -579,10 +579,10 @@ index 08186f30eaa97caf18ac2fe47655526e7733d9df..e4cd1f436ad1734f357429cb3ad6c854 return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 32ec2d854847d28b8c9ad665a1b8a50a6bad3100..885d487afbaae02c0401b322dd59273116c91ad0 100644 +index 2413228f605776322caff19098c6b60183005df5..b392e1ee627c982c4c0b099a6b6ea8a9e698b601 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -333,6 +333,7 @@ source_set("browser") { +@@ -339,6 +339,7 @@ source_set("browser") { "//ui/webui/resources", "//v8", "//v8:v8_version", @@ -591,7 +591,7 @@ index 32ec2d854847d28b8c9ad665a1b8a50a6bad3100..885d487afbaae02c0401b322dd592731 public_deps = [ diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h -index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476acbb2c915d 100644 +index d73737088c819274c854db491a7d4d26f9367eb2..b72427517b88e5cb4179dbc551f3f4f321fd8b4e 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -24,6 +24,7 @@ @@ -612,7 +612,7 @@ index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476ac @class RenderWidgetHostViewCocoa; namespace content { -@@ -693,9 +696,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac +@@ -691,9 +694,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // EnsureSurfaceSynchronizedForWebTest(). uint32_t latest_capture_sequence_number_ = 0u; @@ -625,7 +625,7 @@ index b58722615ebd4012816ed3246145cb1a088bfe17..1fde1a7ae8a8885bc3210fe0298476ac // Used to force the NSApplication's focused accessibility element to be the // content::BrowserAccessibilityCocoa accessibility tree when the NSView for diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31e9305c6c 100644 +index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b686f6c8c9 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -49,6 +49,7 @@ @@ -647,7 +647,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 // Reset `ns_view_` before resetting `remote_ns_view_` to avoid dangling // pointers. `ns_view_` gets reinitialized later in this method. -@@ -1627,8 +1630,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1626,8 +1629,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, gfx::NativeViewAccessible RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() { @@ -658,7 +658,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 return [GetInProcessNSView() window]; } -@@ -1677,9 +1682,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1676,9 +1681,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, } void RenderWidgetHostViewMac::SetAccessibilityWindow(NSWindow* window) { @@ -670,7 +670,7 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 } bool RenderWidgetHostViewMac::SyncIsWidgetForMainFrame( -@@ -2206,20 +2213,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -2205,20 +2212,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, void RenderWidgetHostViewMac::GetRenderWidgetAccessibilityToken( GetRenderWidgetAccessibilityTokenCallback callback) { base::ProcessId pid = getpid(); @@ -698,10 +698,10 @@ index 9924e2e37dc04b12cc08b3451769734267b600bf..943e3b8875c68178e84ae79c241c7a31 /////////////////////////////////////////////////////////////////////////////// diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn -index 9de0d44a70c4c2587a7e4283783b614174393819..87dfae661a98819118d48cd973862d8fabc7bfa6 100644 +index fb23dfd8f90ad3eeb55e4db373ca67f75d8e5ce1..df902b0b01ed4d092070a92fdf77cf69fde475e7 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn -@@ -285,6 +285,7 @@ source_set("common") { +@@ -283,6 +283,7 @@ source_set("common") { "//ui/shell_dialogs", "//url", "//url/ipc:url_ipc", @@ -792,7 +792,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn -index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870d5be441c 100644 +index 482a1721cb2157f0221c901423423aab37efdaf0..b736432543ab619af6483070895f8968248fd71f 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -658,6 +658,7 @@ static_library("test_support") { @@ -819,10 +819,10 @@ index 23d2ac7768ec651a6542b14eb1b9d3636f90b192..c5d33465dba18c7fd1258e8a0e78f870 ] if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) { -@@ -3251,6 +3254,7 @@ test("content_unittests") { - "//ui/latency:test_support", +@@ -3263,6 +3266,7 @@ test("content_unittests") { "//ui/shell_dialogs:shell_dialogs", "//ui/webui:test_support", + "//url", + "//electron/build/config:generate_mas_config" ] @@ -1431,7 +1431,7 @@ index eb81a70e4d5d5cd3e6ae9b45f8cd1c795ea76c51..9921ccb10d3455600eddd85f77f10228 } // namespace sandbox diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn -index cffd2db61ae3e2e7fdd2e0faeb18b742249197f5..c4ceda3eb634ac5e35219196105e59f4a35ca3fd 100644 +index 3ff395b3ff3b646a64c43503d2c172776bf68c84..e40030307e8d9df7e00a402e241e00664b236c7d 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn @@ -414,6 +414,7 @@ component("core") { @@ -1579,10 +1579,10 @@ index dcf493d62990018040a3f84b6f875af737bd2214..3d1c4dcc9ee0bbfdac15f40d9c74e9f3 void DisplayCALayerTree::GotIOSurfaceFrame( diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn -index 2f7f2a8f81ec0e033cb83f82daac4835a2863cbc..21388d7de99fbb97c79eecbab658934d5cac5650 100644 +index 8b25953be73da43fa2e0b5957569ae481dc6a082..7f9175e7eb67ef27fe110ee72f5e9c885018c2ac 100644 --- a/ui/accessibility/platform/BUILD.gn +++ b/ui/accessibility/platform/BUILD.gn -@@ -283,6 +283,7 @@ component("platform") { +@@ -288,6 +288,7 @@ component("platform") { "AppKit.framework", "Foundation.framework", ] @@ -1673,7 +1673,7 @@ index c8171f0527fe5194f0ea73b57c4444d4c630fbc4..c2ac4da580e3e7f749a0a4de1e859af6 // Accessible object if (AXElementWrapper::IsValidElement(value)) { diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn -index 04702a2c6fec9f84a279e587a59c27c42ae20655..fdda175688baaeabf4991173b4a68ee8fce60d73 100644 +index 4b91bad93384b435bed2e8da5e185603fb0a66a3..d68202e3fe92d01b148bf494a3599e2a8d8ced91 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn @@ -363,6 +363,13 @@ component("base") { @@ -1690,7 +1690,7 @@ index 04702a2c6fec9f84a279e587a59c27c42ae20655..fdda175688baaeabf4991173b4a68ee8 if (is_ios) { sources += [ "device_form_factor_ios.mm", -@@ -514,6 +521,12 @@ component("base") { +@@ -516,6 +523,12 @@ component("base") { "//url", ] diff --git a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch index d0463e32239fd..51f58454f8bd5 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -7,7 +7,7 @@ This adds a callback from the network service that's used to implement session.setCertificateVerifyCallback. diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907e19bc22f 100644 +index 8529c98678d7a671cea947a864d0c13e031a153a..771fee7092bb25e4f7546941a04647d060e966c2 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -159,6 +159,11 @@ @@ -22,7 +22,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 #if BUILDFLAG(IS_CT_SUPPORTED) // gn check does not account for BUILDFLAG(). So, for iOS builds, it will // complain about a missing dependency on the target exposing this header. Add a -@@ -602,6 +607,99 @@ void RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason reason) { +@@ -598,6 +603,99 @@ void RecordHSTSPreconnectUpgradeReason(HSTSRedirectUpgradeReason reason) { } // namespace @@ -122,7 +122,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess; NetworkContext::NetworkContextHttpAuthPreferences:: -@@ -998,6 +1096,13 @@ void NetworkContext::SetClient( +@@ -994,6 +1092,13 @@ void NetworkContext::SetClient( client_.Bind(std::move(client)); } @@ -136,7 +136,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) { -@@ -2583,6 +2688,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( +@@ -2617,6 +2722,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( cert_verifier = std::make_unique( std::make_unique( std::move(cert_verifier))); @@ -148,7 +148,7 @@ index 1c73ead38a713a3b4c582c857ab062040625cceb..e346cfb127a1fa8656bfbc1eba829907 builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( diff --git a/services/network/network_context.h b/services/network/network_context.h -index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7c85d02be 100644 +index f2dcec57a22d95892a08f1fa43696d6eea46a820..930e0bd987c48d111b2c8d71147c09e4418bda6c 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h @@ -113,6 +113,7 @@ class URLMatcher; @@ -159,7 +159,7 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 class CookieManager; class HostResolver; class MdnsResponderManager; -@@ -245,6 +246,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -249,6 +250,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) override; @@ -168,7 +168,7 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 void ResetURLLoaderFactories() override; void GetViaObliviousHttp( mojom::ObliviousHttpRequestPtr request, -@@ -932,6 +935,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -962,6 +965,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext std::vector dismount_closures_; #endif // BUILDFLAG(IS_DIRECTORY_TRANSFER_REQUIRED) @@ -178,10 +178,10 @@ index dea6260bc08d4edf93df4afd4d01252f638694c0..fe04f634b32e503f6e93ae6f20c8c7f7 std::unique_ptr internal_host_resolver_; std::set, base::UniquePtrComparator> diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769adfe4bac 100644 +index 5cfbf71f644b391009d533132621a4ece224bfd3..773049b72e7784d396bb9cd1ebd29c3a523e7454 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -307,6 +307,17 @@ struct SocketBrokerRemotes { +@@ -308,6 +308,17 @@ struct SocketBrokerRemotes { pending_remote server; }; @@ -199,7 +199,7 @@ index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769 // Parameters for constructing a network context. struct NetworkContextParams { // The user agent string. -@@ -935,6 +946,9 @@ interface NetworkContext { +@@ -939,6 +950,9 @@ interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote client); @@ -210,7 +210,7 @@ index d2cc00a218d5cf01562fc9b1dc55cc2d2ca275db..e928d012d77ccffe16263fd0ee7c2769 CreateURLLoaderFactory( pending_receiver url_loader_factory, diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h -index fc22fd2d7fa864dc565c99b0d36b2d31a8e13eaf..97e8f9f145cecad9b0cd8b43a2de4af29cec7778 100644 +index e9579ae8911ebf4a89404564cad13de200cce9ea..da8551db16849cf82a6e3a7eaeb451cae0707f2d 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h @@ -63,6 +63,8 @@ class TestNetworkContext : public mojom::NetworkContext { diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index c15d5e3dbbd71..194ff0869c531 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -7,10 +7,10 @@ Pass RenderFrameHost through to PlatformNotificationService so Electron can identify which renderer a notification came from. diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc -index fcc5767027f6b9f2675d954f532779fafb94fd3a..dae6c3a8a8e5f61852896e661e1b169a87e05cdf 100644 +index 8dd86234b4520c420c5d5a4e5f69d67e27734762..3f50bb8fe51a9a46f5d8b08ddd9c40d45b8cffff 100644 --- a/chrome/browser/notifications/platform_notification_service_impl.cc +++ b/chrome/browser/notifications/platform_notification_service_impl.cc -@@ -243,6 +243,7 @@ bool PlatformNotificationServiceImpl::WasClosedProgrammatically( +@@ -244,6 +244,7 @@ bool PlatformNotificationServiceImpl::WasClosedProgrammatically( // TODO(awdf): Rename to DisplayNonPersistentNotification (Similar for Close) void PlatformNotificationServiceImpl::DisplayNotification( @@ -133,7 +133,7 @@ index 05d3a12dd84c7005d46cc73b312f97ef418d96f5..4765de982802541b3efc7211d106acc7 const GURL& document_url, const WeakDocumentPtr& weak_document_ptr, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 2bc557c1d9673dc30230a69e75e9c3baf69ae36f..1b82a6df5ada9c9c8536225b8bc7a9dd4aa7e8dd 100644 +index 5a13f11b6007eb87c59c5bdfaa02d1ab8f27a15e..c7025b0e43bcc9dd1e4b89dac39b5440e1a6ee30 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -2146,7 +2146,7 @@ void RenderProcessHostImpl::CreateNotificationService( diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 671ea90564c5d..03cc14405aa21 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,10 +10,10 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index b7a1be346c77fcef6c288789bc11f9849d6a126d..2b6d2a4c068757fa6dd16c7a0ffb7d478993d7d8 100644 +index 4ffe6251a941cb51b60a5d19fd6307b011a49f39..6f3d5528c289444c16691ea42a00b87f6f7faeaa 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -802,8 +802,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( +@@ -806,8 +806,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( // TODO(crbug.com/40092527): Consider adding a separate boolean that // tracks this instead of piggybacking `origin_calculation_debug_info`. if (renderer_side_origin.opaque() && diff --git a/patches/chromium/preconnect_manager.patch b/patches/chromium/preconnect_manager.patch index 323ba2ba432e1..fcc53324fc503 100644 --- a/patches/chromium/preconnect_manager.patch +++ b/patches/chromium/preconnect_manager.patch @@ -10,11 +10,11 @@ in favor of defining PreconnectRequest in this file since we don't build the header. diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc -index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e0890bd75 100644 +index 4c90ccfdc5f1357989f7cd86f572915ea05c80da..cba3346fd84a2f75c40e453aeab1602b7c146dd4 100644 --- a/chrome/browser/predictors/preconnect_manager.cc +++ b/chrome/browser/predictors/preconnect_manager.cc -@@ -14,9 +14,11 @@ - #include "base/trace_event/trace_event.h" +@@ -15,9 +15,11 @@ + #include "base/types/optional_util.h" #include "chrome/browser/predictors/predictors_features.h" #include "chrome/browser/predictors/predictors_traffic_annotations.h" +#if 0 @@ -25,7 +25,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -@@ -27,6 +29,20 @@ namespace predictors { +@@ -28,6 +30,20 @@ namespace predictors { const bool kAllowCredentialsOnPreconnectByDefault = true; @@ -46,7 +46,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e PreconnectedRequestStats::PreconnectedRequestStats(const url::Origin& origin, bool was_preconnected) : origin(origin), was_preconnected(was_preconnected) {} -@@ -90,12 +106,15 @@ PreconnectManager::PreconnectManager(base::WeakPtr delegate, +@@ -94,12 +110,15 @@ PreconnectManager::PreconnectManager(base::WeakPtr delegate, PreconnectManager::~PreconnectManager() = default; bool PreconnectManager::IsEnabled() { @@ -63,7 +63,7 @@ index cf15cdbec9ace9a4e8ffb99811f449030fe2f833..35ca134003a31f692201738e6d1dd93e void PreconnectManager::Start(const GURL& url, diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h -index 05963f9d2bff9dd438845c09143039fabe818328..0ab1a08e84b0cd360fd4690f8dd683b7e1697e52 100644 +index 107c0f79ccf10eb5d16181213e12c93f1abc62d6..9babefff03899de91519474956ba1297fbd1c95d 100644 --- a/chrome/browser/predictors/preconnect_manager.h +++ b/chrome/browser/predictors/preconnect_manager.h @@ -17,7 +17,9 @@ @@ -73,10 +73,10 @@ index 05963f9d2bff9dd438845c09143039fabe818328..0ab1a08e84b0cd360fd4690f8dd683b7 +#if 0 #include "chrome/browser/predictors/resource_prefetch_predictor.h" +#endif + #include "content/public/browser/storage_partition_config.h" #include "net/base/network_anonymization_key.h" #include "url/gurl.h" - -@@ -33,7 +35,28 @@ class NetworkContext; +@@ -34,7 +36,28 @@ class NetworkContext; namespace predictors { diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index 3dd47d2f18ed4..63e816414a6df 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -881,10 +881,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 885d487afbaae02c0401b322dd59273116c91ad0..d8bb1152a1f713e58da0f29324a1eb877a693bfd 100644 +index b392e1ee627c982c4c0b099a6b6ea8a9e698b601..a2e944cb987f739d75428649793e622400ed0213 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3109,8 +3109,9 @@ source_set("browser") { +@@ -3123,8 +3123,9 @@ source_set("browser") { "//ppapi/shared_impl", ] diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index 95718e32852ad..9b6a8dc58fcbe 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -30,7 +30,7 @@ index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9ad // RenderWidgetHost on the primary main frame, and false otherwise. virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 8aa812aa9bb3de4c7bc58192baf45c90402d6ff4..574e31bcfeb16cdb9a6c0121ffbc1abe175bb4ab 100644 +index f13799f1cc440a20d8e3c55ee35f64e6505e5590..e46e5eb74c54bc00ead6e2e4eff99fd500e09262 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -2048,6 +2048,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { @@ -44,10 +44,10 @@ index 8aa812aa9bb3de4c7bc58192baf45c90402d6ff4..574e31bcfeb16cdb9a6c0121ffbc1abe void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496..9f18308873306e841bbc15d9ba36c93ba5603a15 100644 +index 60ed731f7648497d62fbc7b43181a47a82743c4b..e84ef165670048dd2f030a48ff2d9796b5170b52 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5781,6 +5781,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { +@@ -5810,6 +5810,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { return text_input_manager_.get(); } @@ -60,10 +60,10 @@ index 79bc1f7a5c44a0dd255b5ca676d95c9fa9c31496..9f18308873306e841bbc15d9ba36c93b RenderWidgetHostImpl* render_widget_host) { return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index ea5598aafb86078ed34b18a05e6709c3df0c0f24..adfe5bb3bac589c2ad67cbbad01ce39c2b222936 100644 +index 550beed4beda751159a82b94687e05018c0ade54..1e606e88aa5a44b06acf1f421556331346928e08 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h -@@ -1173,6 +1173,7 @@ class CONTENT_EXPORT WebContentsImpl +@@ -1177,6 +1177,7 @@ class CONTENT_EXPORT WebContentsImpl void SendScreenRects() override; void SendActiveState(bool active) override; TextInputManager* GetTextInputManager() override; @@ -72,7 +72,7 @@ index ea5598aafb86078ed34b18a05e6709c3df0c0f24..adfe5bb3bac589c2ad67cbbad01ce39c RenderWidgetHostImpl* render_widget_host) override; bool IsShowingContextMenuOnPage() const override; diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h -index c6f536682a1faa17c2b5b57890d576405ccf5c9f..a6f1ee80ef29aedb919dda7bc84cc5520e43c84f 100644 +index 9f9f4cc755886236da2f10516b119643fca9c933..cf263ec4bf7cc5443b5cca5b2d44d3a66f2a405b 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h @@ -37,6 +37,7 @@ @@ -83,7 +83,7 @@ index c6f536682a1faa17c2b5b57890d576405ccf5c9f..a6f1ee80ef29aedb919dda7bc84cc552 #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h" -@@ -636,6 +637,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { +@@ -642,6 +643,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { // Invoked when the primary main frame changes size. virtual void PrimaryMainFrameWasResized(bool width_changed) {} diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index d92a90e211ccf..87efe1bc232d9 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 1396f371c8ae82a1ce52096c176a135cf02b2d65..b371464c15a5ab821d8dbea96b33475c9ed92803 100644 +index f3d223efcfa8f909e810ec43dd5ef90c9fc5c620..c48f9c00f129d1373ce64d33f83ce7a634433622 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9876,25 +9876,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9905,25 +9905,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/render_widget_host_view_base.patch b/patches/chromium/render_widget_host_view_base.patch index e909ee12fd3ec..20ddb8a97889b 100644 --- a/patches/chromium/render_widget_host_view_base.patch +++ b/patches/chromium/render_widget_host_view_base.patch @@ -6,7 +6,7 @@ Subject: render_widget_host_view_base.patch ... something to do with OSR? and maybe as well? terrifying. diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc -index 5a91c313ed757c04810f03bdc2be0e455913921e..0cf7de41a9958fe9c7b9b60e3edc7402f3a74c67 100644 +index ce00b0540a7ac7f7c7b4c65f1a1343f72ae21c42..cc3b694431f14b166a305a446a48c25d5099b152 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc @@ -654,6 +654,13 @@ void RenderWidgetHostViewBase::OnFrameTokenChangedForView( @@ -24,7 +24,7 @@ index 5a91c313ed757c04810f03bdc2be0e455913921e..0cf7de41a9958fe9c7b9b60e3edc7402 const blink::WebMouseEvent& event, const ui::LatencyInfo& latency) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h -index e1fb0dc61160a402b7748aac7d048e0ffc9c1b32..c2222c345ca01af92b4f42142a6e0c9851129e85 100644 +index 568a87065acb56faf3f91e35b4e9ad2782edbe28..90a8f27e5d240c7d88a314c580b459d1090b1d25 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -30,6 +30,8 @@ diff --git a/patches/chromium/render_widget_host_view_mac.patch b/patches/chromium/render_widget_host_view_mac.patch index d93baba808224..d9584e8426801 100644 --- a/patches/chromium/render_widget_host_view_mac.patch +++ b/patches/chromium/render_widget_host_view_mac.patch @@ -8,7 +8,7 @@ respond to the first mouse click in their window, which is desirable for some kinds of utility windows. Similarly for `disableAutoHideCursor`. diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e7733d9df 100644 +index f70d59fce833ca4daf81d164e5ca9fd39f2520f7..b712b8af0e770aa3acbeb1167b1a20bc1547c98a 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -169,6 +169,15 @@ void ExtractUnderlines(NSAttributedString* string, @@ -27,7 +27,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e // RenderWidgetHostViewCocoa --------------------------------------------------- // Private methods: -@@ -789,6 +798,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { +@@ -780,6 +789,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { } - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { @@ -37,7 +37,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e // Enable "click-through" if mouse clicks are accepted in inactive windows return [self acceptsMouseEventsOption] > kAcceptMouseEventsInActiveWindow; } -@@ -934,6 +946,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { +@@ -925,6 +937,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { // its parent view. BOOL hitSelf = NO; while (view) { @@ -48,7 +48,7 @@ index d09df298697b909e09d057f439d3654110dce4ed..08186f30eaa97caf18ac2fe47655526e if (view == self) hitSelf = YES; if ([view isKindOfClass:[self class]] && ![view isEqual:self] && -@@ -1268,6 +1284,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { +@@ -1259,6 +1275,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { eventType == NSEventTypeKeyDown && !(modifierFlags & NSEventModifierFlagCommand); diff --git a/patches/chromium/resource_file_conflict.patch b/patches/chromium/resource_file_conflict.patch index 10866376bba26..4bcf3eaf5215d 100644 --- a/patches/chromium/resource_file_conflict.patch +++ b/patches/chromium/resource_file_conflict.patch @@ -52,10 +52,10 @@ Some alternatives to this patch: None of these options seems like a substantial maintainability win over this patch to me (@nornagon). diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index 7bad6d2fe242bc71014d3fbd6be4481280297366..bdf6d5865fb0069f4df368613167069d2fb50c86 100644 +index 9e0dca945ef9a9a0209c84ab6cf5e1cda352257e..b38442f018b218944c7b85c9f8bd8b8eb6137b9e 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -1546,7 +1546,7 @@ if (is_chrome_branded && !is_android) { +@@ -1549,7 +1549,7 @@ if (is_chrome_branded && !is_android) { } } @@ -64,7 +64,7 @@ index 7bad6d2fe242bc71014d3fbd6be4481280297366..bdf6d5865fb0069f4df368613167069d chrome_paks("packed_resources") { if (is_mac) { output_dir = "$root_gen_dir/repack" -@@ -1592,6 +1592,12 @@ repack("browser_tests_pak") { +@@ -1595,6 +1595,12 @@ repack("browser_tests_pak") { deps = [ "//chrome/test/data/webui:resources" ] } diff --git a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch index c4ce0436f5d13..29fc4d520e3ec 100644 --- a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch +++ b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch @@ -233,10 +233,10 @@ index 2991489fae8a4eecad97b1ecb2271f096d9a9229..93b7aa620ad1da250ac06e3383ca6897 } diff --git a/content/common/features.cc b/content/common/features.cc -index b55390ed12152c1d5ab01415ac2b64422b59ad8b..51d76c126025f422def2a46ebb5d6c427f5ae3cb 100644 +index 31484e57bd0989af7a2e9584bbd430cdfa713346..fdb5e8f5f395b128c1c5300b94a50f693d6b52e1 100644 --- a/content/common/features.cc +++ b/content/common/features.cc -@@ -262,6 +262,14 @@ BASE_FEATURE(kIOSurfaceCapturer, +@@ -261,6 +261,14 @@ BASE_FEATURE(kIOSurfaceCapturer, base::FEATURE_ENABLED_BY_DEFAULT); #endif @@ -252,7 +252,7 @@ index b55390ed12152c1d5ab01415ac2b64422b59ad8b..51d76c126025f422def2a46ebb5d6c42 // invalidated upon notifications sent by base::SystemMonitor. If disabled, the // cache is considered invalid on every enumeration request. diff --git a/content/common/features.h b/content/common/features.h -index 7a34bc2fafd421a3c63cb11706e1dac84ef02454..09f4a60267ea2ecb426edf3314274d6806a25bec 100644 +index 906c0da9313ac0272f5e5a79ef797de596804cfb..94b63dfe1235b7643e29926edd2c27c447302b35 100644 --- a/content/common/features.h +++ b/content/common/features.h @@ -68,6 +68,9 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kInterestGroupUpdateIfOlderThan); diff --git a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch index bff6322d11a2d..b057e5375c917 100644 --- a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch +++ b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch @@ -6,10 +6,10 @@ Subject: Revert "Remove the AllowAggressiveThrottlingWithWebSocket feature." This reverts commit 615c1810a187840ffeb04096087efff86edb37de. diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc -index 7a885de263f1c84fc676c62caa6c787983e47994..854d57812e99bb4668aa3bbf3f63bdb0219e658d 100644 +index 97325e06385105ec75d354002c6a147d5dff7218..d5b18bbcc9d2c46c6c2ebae1b36f4f1327b27b2c 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc -@@ -104,6 +104,17 @@ enum WebSocketOpCode { +@@ -99,6 +99,17 @@ enum WebSocketOpCode { kOpCodeBinary = 0x2, }; @@ -27,7 +27,7 @@ index 7a885de263f1c84fc676c62caa6c787983e47994..854d57812e99bb4668aa3bbf3f63bdb0 } // namespace WebSocketChannelImpl::MessageDataDeleter::MessageDataDeleter( -@@ -297,7 +308,10 @@ bool WebSocketChannelImpl::Connect(const KURL& url, const String& protocol) { +@@ -293,7 +304,10 @@ bool WebSocketChannelImpl::Connect(const KURL& url, const String& protocol) { // even if the `WebSocketChannel` is closed. feature_handle_for_scheduler_ = scheduler->RegisterFeature( SchedulingPolicy::Feature::kWebSocket, diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index e4b923927d98a..f99653d131c0d 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,10 +6,10 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index d7eb65e86ca60744a6d919b2899208978a761e22..e80404d2d0aed8096731895580cc879b8827b3be 100644 +index 86399ed26f1ba6808d1f7bb0b1d7649df9da8901..b05f83c47f138f7040a175cc46dba99dcf054765 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1306,7 +1306,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { +@@ -1310,7 +1310,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { } bool RenderThreadImpl::IsElasticOverscrollEnabled() { diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index 9326140c56ca3..94de91d98d346 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,7 +22,7 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 1b82a6df5ada9c9c8536225b8bc7a9dd4aa7e8dd..e45de1723b133d529e8eb0c5b019c1b98e994173 100644 +index c7025b0e43bcc9dd1e4b89dac39b5440e1a6ee30..30a3715e3dfa76e68d9a75742b1d085ec32a3fc3 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1754,6 +1754,10 @@ bool RenderProcessHostImpl::Init() { diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index ea87df62c9411..0ab51d0080fc9 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,10 +9,10 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff 100644 +index 591c87ffc5f56b38d0f329da8b983a77af9662ee..b139a13c80eb9d356c78c587f823a4cb8452abe9 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -3853,6 +3853,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3882,6 +3882,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, params.main_frame_name, GetOpener(), primary_main_frame_policy, base::UnguessableToken::Create()); @@ -26,7 +26,7 @@ index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4 std::unique_ptr delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -3863,6 +3870,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3892,6 +3899,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, view_ = CreateWebContentsView(this, std::move(delegate), &render_view_host_delegate_view_); } @@ -35,10 +35,10 @@ index 9d6ad368980202f74d36785623d27354beef3f03..9fb2ac59a8b6a4e6f1ca388cd053c8f4 CHECK(view_.get()); diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h -index 4f3914d6936cbf0119b34da82ab6d5d67bb526f7..49291ee02aa20a34cab44838121e1cff0c380508 100644 +index 4cf7eef3b54bb4d3770786f8eb03d1678516fa54..772b029c67cc74b2922fe7a034edc14bcfda6c31 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h -@@ -116,10 +116,13 @@ class BrowserPluginGuestDelegate; +@@ -119,10 +119,13 @@ class BrowserPluginGuestDelegate; class GuestPageHolder; class RenderFrameHost; class RenderViewHost; @@ -52,7 +52,7 @@ index 4f3914d6936cbf0119b34da82ab6d5d67bb526f7..49291ee02aa20a34cab44838121e1cff class WebUI; struct DropData; struct MHTMLGenerationParams; -@@ -265,6 +268,10 @@ class WebContents : public PageNavigator, public base::SupportsUserData { +@@ -268,6 +271,10 @@ class WebContents : public PageNavigator, public base::SupportsUserData { network::mojom::WebSandboxFlags starting_sandbox_flags = network::mojom::WebSandboxFlags::kNone; diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index 38f2ca15ba7a2..bc8e2416b958d 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 09752e9cda20f0020976015d58f73f320031d11e..b7a1be346c77fcef6c288789bc11f9849d6a126d 100644 +index 37af686964489bd77d84be95d5f3aba9cd0a7a0c..4ffe6251a941cb51b60a5d19fd6307b011a49f39 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8722,6 +8722,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8739,6 +8739,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,10 +37,10 @@ index 09752e9cda20f0020976015d58f73f320031d11e..b7a1be346c77fcef6c288789bc11f984 if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff..55840c1cf5323d1796f1c3b056602411f513a220 100644 +index b139a13c80eb9d356c78c587f823a4cb8452abe9..6f323c5863f4bb869a66d265fac1d372c5243187 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4128,21 +4128,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( +@@ -4157,21 +4157,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"), "WebContentsImpl::PreHandleKeyboardEvent"); @@ -78,7 +78,7 @@ index 9fb2ac59a8b6a4e6f1ca388cd053c8f4d5eac8ff..55840c1cf5323d1796f1c3b056602411 } bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { -@@ -4301,7 +4305,7 @@ void WebContentsImpl::EnterFullscreenMode( +@@ -4330,7 +4334,7 @@ void WebContentsImpl::EnterFullscreenMode( OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode"); DCHECK(CanEnterFullscreenMode(requesting_frame)); DCHECK(requesting_frame->IsActive()); diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index 212b2e7b385c0..bab3a8cbddbc3 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -26,10 +26,10 @@ index 7a2d251ba2d13d0a34df176111e6524a27b87f55..cbbe0fbdd25a0f7859b113fdb3dcd9ce // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 30c79bff1b9665ea0186ec6437cda0804b1168d4..b933264fd21f689de436a832476ebbfef89b927e 100644 +index 4b9fce8429d10cd002017128645ae8a54cd1f088..8ea2df6c0b543dec1309531573d2277b94353923 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -894,6 +894,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -897,6 +897,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } diff --git a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch index f0c2cbf2f2557..9d9284333493b 100644 --- a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch +++ b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch @@ -35,10 +35,10 @@ index cbbe0fbdd25a0f7859b113fdb3dcd9ce57e597d6..1345bb5008e1b4fc3a450f7e353d52ec // from the worker thread. virtual void WillDestroyWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index b933264fd21f689de436a832476ebbfef89b927e..e9a9cf8df3318331f69929437b91b11f9273aea0 100644 +index 8ea2df6c0b543dec1309531573d2277b94353923..3ac2e0783f9a7ab65d89352bf03e68f3b83c64b9 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -906,6 +906,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( +@@ -909,6 +909,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( worker); } diff --git a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch index 664ff5ff075f4..0c4c7e03a27ca 100644 --- a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch +++ b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch @@ -10,10 +10,10 @@ to handle this without patching, but this is fairly clean for now and no longer patching legacy devtools code. diff --git a/front_end/entrypoints/main/MainImpl.ts b/front_end/entrypoints/main/MainImpl.ts -index 469a7440eb5a0306a48e33668453a998ca5fe093..8cf1b93049a16d7bad1c64269b180fdba6d08ef3 100644 +index 117f738fa274ffdb79e1666a5de322fb163d19b8..d8f512c9b4284571bc96285a64f8d0faf1826c52 100644 --- a/front_end/entrypoints/main/MainImpl.ts +++ b/front_end/entrypoints/main/MainImpl.ts -@@ -787,6 +787,8 @@ export class MainImpl { +@@ -760,6 +760,8 @@ export class MainImpl { globalThis.Main = globalThis.Main || {}; // @ts-expect-error Exported for Tests.js globalThis.Main.Main = MainImpl; diff --git a/patches/node/.patches b/patches/node/.patches index 8d0f154143992..9322abd8557ad 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -45,3 +45,4 @@ build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch feat_add_oom_error_callback_in_node_isolatesettings.patch +fix_-wnonnull_warning.patch diff --git a/patches/node/fix_-wnonnull_warning.patch b/patches/node/fix_-wnonnull_warning.patch new file mode 100644 index 0000000000000..a75eaf4b373dd --- /dev/null +++ b/patches/node/fix_-wnonnull_warning.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Charles Kerr +Date: Thu, 6 Mar 2025 19:31:29 -0600 +Subject: fix: -Wnonnull warning +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes this warning: + +> 2025-03-07T01:05:01.8637705Z ../../third_party/electron_node/src/debug_utils.cc(257,12): error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] +> 2025-03-07T01:05:01.8638267Z 257 | return nullptr; +> 2025-03-07T01:05:01.8638481Z | ^~~~~~~ +> 2025-03-07T01:05:01.8638700Z 1 error generated. + +Not sure why this warning was never triggered before; `git blame` +indicates this code hasn't changed in ages: + +> c40a8273ef2 (Michaël Zasso 2024-05-10 09:50:20 +0200 255) #endif // DEBUG +> 8e2d33f1562 (Anna Henningsen 2018-06-07 16:54:29 +0200 256) } +> 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 257) return nullptr; +> 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 258) } + +Presumably this is failing in this Chromium roll due to a +clang version bump. + +We should remove this patch after upstreaming it. + +Upstream PR: https://github.com/nodejs/node/pull/57354 + +diff --git a/src/debug_utils.cc b/src/debug_utils.cc +index c8b3b11ee34d7ac98163aa563fd7f6f1fb0a3b79..8a85e066fd9e6f3d6131eca89d52495f904cd5a6 100644 +--- a/src/debug_utils.cc ++++ b/src/debug_utils.cc +@@ -254,7 +254,7 @@ class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext { + USE(GetLastError()); + #endif // DEBUG + } +- return nullptr; ++ return {}; + } + + SymbolInfo LookupSymbol(void* address) override { diff --git a/patches/v8/.patches b/patches/v8/.patches index 280a34b936037..905bd3558cc0d 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,2 +1,3 @@ chore_allow_customizing_microtask_policy_per_context.patch deps_add_v8_object_setinternalfieldfornodecore.patch +revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch diff --git a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch index 83f3a29e4c694..d812b9be9c90f 100644 --- a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch +++ b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch @@ -46,10 +46,10 @@ index 3e57ae8efe33f326ef0e5d609c311d4be5b8afd6..dc521d39c2280dfc3217e97c1e413b2b V8_INLINE static void* GetAlignedPointerFromInternalField( const BasicTracedReference& object, int index) { diff --git a/src/api/api.cc b/src/api/api.cc -index 412179152521fa45eab085818b9d9a5a0c274362..9eeb209ca92fd372d38f1d6dd0b096cee32d1061 100644 +index 3e6a975f912cf482fbf668142080df7e9aa80455..64044e9cf44d401c249787feafb651688ee0d9f9 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -6385,14 +6385,33 @@ Local v8::Object::SlowGetInternalField(int index) { +@@ -6322,14 +6322,33 @@ Local v8::Object::SlowGetInternalField(int index) { i::Cast(*obj)->GetEmbedderField(index), isolate)); } diff --git a/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch b/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch new file mode 100644 index 0000000000000..3b74403b88001 --- /dev/null +++ b/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Charles Kerr +Date: Thu, 6 Mar 2025 14:31:19 -0600 +Subject: Revert "[api] Delete deprecated AttachCppHeap and DetachCppHeap" + +Restore this API because Node.js needs it. + +This patch can be removed after an upstream fix lands in Node.js, +e.g. in https://github.com/nodejs/node-v8/tree/canary + +diff --git a/include/v8-isolate.h b/include/v8-isolate.h +index 97f1030dd2ca47ca4b58ac64e2e11e615bc46130..24ef6b5e0af63179e557b9896134838e112c59db 100644 +--- a/include/v8-isolate.h ++++ b/include/v8-isolate.h +@@ -1172,6 +1172,28 @@ class V8_EXPORT Isolate { + */ + void SetEmbedderRootsHandler(EmbedderRootsHandler* handler); + ++ /** ++ * Attaches a managed C++ heap as an extension to the JavaScript heap. The ++ * embedder maintains ownership of the CppHeap. At most one C++ heap can be ++ * attached to V8. ++ * ++ * Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see ++ * CppHeap. ++ * ++ * If a CppHeap is set via CreateParams, then this call is a noop. ++ */ ++ V8_DEPRECATED("Set the heap on Isolate creation using CreateParams instead.") ++ void AttachCppHeap(CppHeap*); ++ ++ /** ++ * Detaches a managed C++ heap if one was attached using `AttachCppHeap()`. ++ * ++ * If a CppHeap is set via CreateParams, then this call is a noop. ++ */ ++ V8_DEPRECATED( ++ "The CppHeap gets detached automatically during Isolate tear down.") ++ void DetachCppHeap(); ++ + using ReleaseCppHeapCallback = void (*)(std::unique_ptr); + + /** +@@ -1219,7 +1241,6 @@ class V8_EXPORT Isolate { + class V8_DEPRECATED("AtomicsWaitWakeHandle is unused and will be removed.") + #endif + V8_EXPORT AtomicsWaitWakeHandle { +- + public: + /** + * Stop this `Atomics.wait()` call and call the |AtomicsWaitCallback| +diff --git a/src/api/api.cc b/src/api/api.cc +index 64044e9cf44d401c249787feafb651688ee0d9f9..1677e54b188b6a1699370d8cff37d2acf2933f38 100644 +--- a/src/api/api.cc ++++ b/src/api/api.cc +@@ -9876,6 +9876,16 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) { + i_isolate->heap()->SetEmbedderRootsHandler(handler); + } + ++void Isolate::AttachCppHeap(CppHeap* cpp_heap) { ++ i::Isolate* i_isolate = reinterpret_cast(this); ++ i_isolate->heap()->AttachCppHeap(cpp_heap); ++} ++ ++void Isolate::DetachCppHeap() { ++ i::Isolate* i_isolate = reinterpret_cast(this); ++ i_isolate->heap()->DetachCppHeap(); ++} ++ + CppHeap* Isolate::GetCppHeap() const { + const i::Isolate* i_isolate = reinterpret_cast(this); + return i_isolate->heap()->cpp_heap(); +diff --git a/src/heap/cppgc-js/cpp-heap.cc b/src/heap/cppgc-js/cpp-heap.cc +index e033791ca1cdeba4a304e69b922d4169a22f9caa..706f81f7bbc1b5a7a1b73afe018b0b2c0184d9ef 100644 +--- a/src/heap/cppgc-js/cpp-heap.cc ++++ b/src/heap/cppgc-js/cpp-heap.cc +@@ -513,6 +513,11 @@ CppHeap::CppHeap( + } + + CppHeap::~CppHeap() { ++ if (isolate_) { ++ // TODO(ahaas): Delete this code once `v8::Isolate::DetachCppHeap` has been ++ // deleted. ++ isolate_->heap()->DetachCppHeap(); ++ } + Terminate(); + } + +diff --git a/src/heap/heap.cc b/src/heap/heap.cc +index cedf308a6042cc45241d4ee2731d2ee240ee8d9f..794c6380db4a669e7a83f6cce1db1dbe4fcde972 100644 +--- a/src/heap/heap.cc ++++ b/src/heap/heap.cc +@@ -6065,6 +6065,21 @@ void Heap::AttachCppHeap(v8::CppHeap* cpp_heap) { + cpp_heap_ = cpp_heap; + } + ++void Heap::DetachCppHeap() { ++ // The API function should be a noop in case a CppHeap was passed on Isolate ++ // creation. ++ if (owning_cpp_heap_) { ++ return; ++ } ++ ++ // The CppHeap may have been detached already. ++ if (!cpp_heap_) return; ++ ++ CppHeap::From(cpp_heap_)->StartDetachingIsolate(); ++ CppHeap::From(cpp_heap_)->DetachIsolate(); ++ cpp_heap_ = nullptr; ++} ++ + std::optional Heap::overridden_stack_state() const { + if (!embedder_stack_state_origin_) return {}; + return embedder_stack_state_; +diff --git a/src/heap/heap.h b/src/heap/heap.h +index 200a3bdcd563a54b9fbd6c7ca007fb940b1a9451..0946601a9c25a03a830fb6a8fb5389057bfe5d33 100644 +--- a/src/heap/heap.h ++++ b/src/heap/heap.h +@@ -1108,6 +1108,9 @@ class Heap final { + // Unified heap (C++) support. =============================================== + // =========================================================================== + ++ V8_EXPORT_PRIVATE void AttachCppHeap(v8::CppHeap* cpp_heap); ++ V8_EXPORT_PRIVATE void DetachCppHeap(); ++ + v8::CppHeap* cpp_heap() const { return cpp_heap_; } + + std::optional overridden_stack_state() const; +@@ -1649,8 +1652,6 @@ class Heap final { + private: + class AllocationTrackerForDebugging; + +- void AttachCppHeap(v8::CppHeap* cpp_heap); +- + using ExternalStringTableUpdaterCallback = + Tagged (*)(Heap* heap, FullObjectSlot pointer); + diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 95bd0dc441244..c03eb600e458f 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -152,8 +152,6 @@ uint32_t GetQuotaMask(const std::vector& quota_types) { auto type = base::ToLowerASCII(it); if (type == "temporary") quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY; - else if (type == "syncable") - quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_SYNCABLE; } return quota_mask; } diff --git a/shell/browser/api/electron_api_view.cc b/shell/browser/api/electron_api_view.cc index f36439a98baed..2268674b3ae4f 100644 --- a/shell/browser/api/electron_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -346,7 +346,8 @@ std::vector> View::GetChildren() { void View::SetBackgroundColor(std::optional color) { if (!view_) return; - view_->SetBackground(color ? views::CreateSolidBackground(*color) : nullptr); + view_->SetBackground(color ? views::CreateSolidBackground({*color}) + : nullptr); } void View::SetBorderRadius(int radius) { diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index da53756e7cc57..53a24ac6f5943 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1400,7 +1400,7 @@ bool WebContents::IsFullscreen() const { return owner_window()->IsFullscreen() || is_html_fullscreen(); } -void WebContents::EnterFullscreen(const GURL& url, +void WebContents::EnterFullscreen(const url::Origin& origin, ExclusiveAccessBubbleType bubble_type, const int64_t display_id) {} diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index c4f6ee0a2fb41..58389913caa84 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -684,7 +684,7 @@ class WebContents final : public ExclusiveAccessContext, // ExclusiveAccessContext: Profile* GetProfile() override; bool IsFullscreen() const override; - void EnterFullscreen(const GURL& url, + void EnterFullscreen(const url::Origin& origin, ExclusiveAccessBubbleType bubble_type, const int64_t display_id) override; void ExitFullscreen() override {} diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index ed0033ed001d8..4f19581b97efc 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -178,16 +178,8 @@ void ElectronExtensionLoader::PreAddExtension(const Extension* extension, // The extension might be disabled if a previous reload attempt failed. In // that case, we want to remove that disable reason. ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); - if (extension_prefs->IsExtensionDisabled(extension->id()) && - extension_prefs->HasDisableReason(extension->id(), - disable_reason::DISABLE_RELOAD)) { - extension_prefs->RemoveDisableReason(extension->id(), - disable_reason::DISABLE_RELOAD); - // Only re-enable the extension if there are no other disable reasons. - if (extension_prefs->GetDisableReasons(extension->id()).empty()) { - extension_prefs->SetExtensionEnabled(extension->id()); - } - } + extension_prefs->RemoveDisableReason(extension->id(), + disable_reason::DISABLE_RELOAD); } void ElectronExtensionLoader::PostActivateExtension( diff --git a/shell/browser/extensions/electron_extensions_browser_client.cc b/shell/browser/extensions/electron_extensions_browser_client.cc index 8d2b8727f9916..905414fffb201 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.cc +++ b/shell/browser/extensions/electron_extensions_browser_client.cc @@ -19,7 +19,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/frame_tree_node_id.h" #include "content/public/browser/render_frame_host.h" -#include "content/public/common/user_agent.h" #include "extensions/browser/api/core_extensions_browser_api_provider.h" #include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/component_extension_resource_manager.h" diff --git a/shell/browser/extensions/electron_messaging_delegate.cc b/shell/browser/extensions/electron_messaging_delegate.cc index eff6db2b3b686..0ec714a4825d3 100644 --- a/shell/browser/extensions/electron_messaging_delegate.cc +++ b/shell/browser/extensions/electron_messaging_delegate.cc @@ -65,53 +65,6 @@ content::WebContents* ElectronMessagingDelegate::GetWebContentsByTabId( return contents->web_contents(); } -std::unique_ptr ElectronMessagingDelegate::CreateReceiverForTab( - base::WeakPtr channel_delegate, - const std::string& extension_id, - const PortId& receiver_port_id, - content::WebContents* receiver_contents, - int receiver_frame_id, - const std::string& receiver_document_id) { - // Frame ID -1 is every frame in the tab. - bool include_child_frames = - receiver_frame_id == -1 && receiver_document_id.empty(); - - content::RenderFrameHost* receiver_rfh = nullptr; - if (include_child_frames) { - // The target is the active outermost main frame of the WebContents. - receiver_rfh = receiver_contents->GetPrimaryMainFrame(); - } else if (!receiver_document_id.empty()) { - ExtensionApiFrameIdMap::DocumentId document_id = - ExtensionApiFrameIdMap::DocumentIdFromString(receiver_document_id); - - // Return early for invalid documentIds. - if (!document_id) - return nullptr; - - receiver_rfh = - ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId( - document_id); - - // If both |document_id| and |receiver_frame_id| are provided they - // should find the same RenderFrameHost, if not return early. - if (receiver_frame_id != -1 && - ExtensionApiFrameIdMap::GetRenderFrameHostById( - receiver_contents, receiver_frame_id) != receiver_rfh) { - return nullptr; - } - } else { - DCHECK_GT(receiver_frame_id, -1); - receiver_rfh = ExtensionApiFrameIdMap::GetRenderFrameHostById( - receiver_contents, receiver_frame_id); - } - if (!receiver_rfh) - return nullptr; - - return ExtensionMessagePort::CreateForTab(channel_delegate, receiver_port_id, - extension_id, receiver_rfh, - include_child_frames); -} - std::unique_ptr ElectronMessagingDelegate::CreateReceiverForNativeApp( content::BrowserContext* browser_context, diff --git a/shell/browser/extensions/electron_messaging_delegate.h b/shell/browser/extensions/electron_messaging_delegate.h index 67b6aabe81445..2a447ef8442c0 100644 --- a/shell/browser/extensions/electron_messaging_delegate.h +++ b/shell/browser/extensions/electron_messaging_delegate.h @@ -32,13 +32,6 @@ class ElectronMessagingDelegate : public MessagingDelegate { content::WebContents* GetWebContentsByTabId( content::BrowserContext* browser_context, int tab_id) override; - std::unique_ptr CreateReceiverForTab( - base::WeakPtr channel_delegate, - const std::string& extension_id, - const PortId& receiver_port_id, - content::WebContents* receiver_contents, - int receiver_frame_id, - const std::string& receiver_document_id) override; std::unique_ptr CreateReceiverForNativeApp( content::BrowserContext* browser_context, base::WeakPtr channel_delegate, diff --git a/shell/browser/ui/devtools_manager_delegate.cc b/shell/browser/ui/devtools_manager_delegate.cc index f90dc99e8dd29..bdec61a501c32 100644 --- a/shell/browser/ui/devtools_manager_delegate.cc +++ b/shell/browser/ui/devtools_manager_delegate.cc @@ -20,7 +20,6 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" -#include "content/public/common/user_agent.h" #include "electron/grit/electron_resources.h" #include "net/base/net_errors.h" #include "net/socket/stream_socket.h" diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc index c5d28e6b87932..60d481a9d5caa 100644 --- a/shell/browser/ui/inspectable_web_contents.cc +++ b/shell/browser/ui/inspectable_web_contents.cc @@ -22,6 +22,7 @@ #include "base/uuid.h" #include "base/values.h" #include "chrome/browser/devtools/devtools_contents_resizing_strategy.h" +#include "components/embedder_support/user_agent_utils.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" @@ -35,7 +36,6 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/shared_cors_origin_access_list.h" #include "content/public/browser/storage_partition.h" -#include "content/public/common/user_agent.h" #include "ipc/ipc_channel.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -139,9 +139,10 @@ double GetNextZoomLevel(double level, bool out) { } GURL GetRemoteBaseURL() { - return GURL(absl::StrFormat("%s%s/%s/", kChromeUIDevToolsRemoteFrontendBase, - kChromeUIDevToolsRemoteFrontendPath, - content::GetChromiumGitRevision().c_str())); + return GURL( + absl::StrFormat("%s%s/%s/", kChromeUIDevToolsRemoteFrontendBase, + kChromeUIDevToolsRemoteFrontendPath, + embedder_support::GetChromiumGitRevision().c_str())); } GURL GetDevToolsURL(bool can_dock) { @@ -816,12 +817,6 @@ void InspectableWebContents::DispatchProtocolMessageFromDevToolsFrontend( agent_host_->DispatchProtocolMessage(this, base::as_byte_span(message)); } -void InspectableWebContents::SendJsonRequest(DispatchCallback callback, - const std::string& browser_id, - const std::string& url) { - std::move(callback).Run(nullptr); -} - void InspectableWebContents::GetPreferences(DispatchCallback callback) { const base::Value& prefs = pref_service_->GetValue(kDevToolsPreferences); std::move(callback).Run(&prefs); diff --git a/shell/browser/ui/inspectable_web_contents.h b/shell/browser/ui/inspectable_web_contents.h index 9bdeceabf16f5..012f613895e3a 100644 --- a/shell/browser/ui/inspectable_web_contents.h +++ b/shell/browser/ui/inspectable_web_contents.h @@ -148,9 +148,6 @@ class InspectableWebContents int min, int exclusive_max, int buckets) override {} - void SendJsonRequest(DispatchCallback callback, - const std::string& browser_id, - const std::string& url) override; void RegisterPreference(const std::string& name, const RegisterOptions& options) override {} void GetPreferences(DispatchCallback callback) override; @@ -183,6 +180,7 @@ class InspectableWebContents void RecordDrag(const DragEvent& event) override {} void RecordChange(const ChangeEvent& event) override {} void RecordKeyDown(const KeyDownEvent& event) override {} + void RecordSettingAccess(const SettingAccessEvent& event) override {} void ShowSurvey(DispatchCallback callback, const std::string& trigger) override {} void CanShowSurvey(DispatchCallback callback, diff --git a/shell/browser/ui/views/autofill_popup_view.cc b/shell/browser/ui/views/autofill_popup_view.cc index 27f854db302fc..7e7dd928bf5c0 100644 --- a/shell/browser/ui/views/autofill_popup_view.cc +++ b/shell/browser/ui/views/autofill_popup_view.cc @@ -73,7 +73,9 @@ void AutofillPopupView::Show() { // a weak pointer to hold the reference and don't have to worry about // deletion. auto* widget = new views::Widget; - views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); + views::Widget::InitParams params{ + views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET, + views::Widget::InitParams::TYPE_POPUP}; params.delegate = this; params.parent = parent_widget_->GetNativeView(); params.z_order = ui::ZOrderLevel::kFloatingUIElement; diff --git a/shell/browser/ui/views/menu_bar.cc b/shell/browser/ui/views/menu_bar.cc index e5bceb5ec366b..5e0b639ebc238 100644 --- a/shell/browser/ui/views/menu_bar.cc +++ b/shell/browser/ui/views/menu_bar.cc @@ -35,7 +35,7 @@ MenuBar::MenuBar(NativeWindow* window, RootView* root_view) : background_color_(kDefaultColor), window_(window), root_view_(root_view) { const ui::NativeTheme* theme = root_view_->GetNativeTheme(); #if BUILDFLAG(IS_WIN) - SetBackground(views::CreateThemedSolidBackground(ui::kColorMenuBackground)); + SetBackground(views::CreateSolidBackground(ui::kColorMenuBackground)); #endif RefreshColorCache(theme); UpdateViewColors(); diff --git a/shell/browser/ui/webui/accessibility_ui.cc b/shell/browser/ui/webui/accessibility_ui.cc index ad631bb9fd209..0a730a6b727d4 100644 --- a/shell/browser/ui/webui/accessibility_ui.cc +++ b/shell/browser/ui/webui/accessibility_ui.cc @@ -78,7 +78,7 @@ constexpr std::string_view kLocked = "locked"; constexpr std::string_view kNative = "native"; constexpr std::string_view kPage = "page"; constexpr std::string_view kPDFPrinting = "pdfPrinting"; -constexpr std::string_view kScreenReader = "screenreader"; +constexpr std::string_view kExtendedProperties = "extendedProperties"; constexpr std::string_view kShowOrRefreshTree = "showOrRefreshTree"; constexpr std::string_view kText = "text"; constexpr std::string_view kWeb = "web"; @@ -165,7 +165,7 @@ void HandleAccessibilityRequestCallback( bool native = mode.has_mode(ui::AXMode::kNativeAPIs); bool web = mode.has_mode(ui::AXMode::kWebContents); bool text = mode.has_mode(ui::AXMode::kInlineTextBoxes); - bool screenreader = mode.has_mode(ui::AXMode::kScreenReader); + bool extendedProperties = mode.has_mode(ui::AXMode::kExtendedProperties); bool html = mode.has_mode(ui::AXMode::kHTML); bool pdf_printing = mode.has_mode(ui::AXMode::kPDFPrinting); @@ -174,12 +174,12 @@ void HandleAccessibilityRequestCallback( data.Set(kNative, is_native_enabled ? (native ? kOn : kOff) : kDisabled); data.Set(kWeb, is_native_enabled ? (web ? kOn : kOff) : kDisabled); - // The "text", "screenreader" and "html" flags are only + // The "text", "extendedProperties" and "html" flags are only // meaningful if "web" is enabled. bool is_web_enabled = is_native_enabled && web; data.Set(kText, is_web_enabled ? (text ? kOn : kOff) : kDisabled); - data.Set(kScreenReader, - is_web_enabled ? (screenreader ? kOn : kOff) : kDisabled); + data.Set(kExtendedProperties, + is_web_enabled ? (extendedProperties ? kOn : kOff) : kDisabled); data.Set(kHTML, is_web_enabled ? (html ? kOn : kOff) : kDisabled); // The "pdfPrinting" flag is independent of the others. @@ -246,7 +246,7 @@ void HandleAccessibilityRequestCallback( base::Value::Dict descriptor = BuildTargetDescriptor(rvh); descriptor.Set(kNative, is_native_enabled); - descriptor.Set(kScreenReader, is_web_enabled && screenreader); + descriptor.Set(kExtendedProperties, is_web_enabled && extendedProperties); descriptor.Set(kWeb, is_web_enabled); page_list.Append(std::move(descriptor)); } diff --git a/shell/common/application_info.cc b/shell/common/application_info.cc index 8540ba1f002c8..f7b04a2d04ac2 100644 --- a/shell/common/application_info.cc +++ b/shell/common/application_info.cc @@ -8,7 +8,7 @@ #include "base/no_destructor.h" #include "chrome/browser/browser_process.h" #include "chrome/common/chrome_version.h" -#include "content/public/common/user_agent.h" +#include "components/embedder_support/user_agent_utils.h" #include "electron/electron_version.h" #include "shell/browser/browser.h" #include "third_party/abseil-cpp/absl/strings/str_format.h" @@ -46,7 +46,7 @@ std::string GetApplicationUserAgent() { "%s/%s Chrome/%s " ELECTRON_PRODUCT_NAME "/" ELECTRON_VERSION_STRING, name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING); } - return content::BuildUserAgentFromProduct(user_agent); + return embedder_support::BuildUserAgentFromProduct(user_agent); } bool IsAppRTL() { diff --git a/shell/common/extensions/electron_extensions_client.cc b/shell/common/extensions/electron_extensions_client.cc index a1d8276ea55c4..607f85c9c0ead 100644 --- a/shell/common/extensions/electron_extensions_client.cc +++ b/shell/common/extensions/electron_extensions_client.cc @@ -9,7 +9,6 @@ #include "base/no_destructor.h" #include "components/version_info/version_info.h" -#include "content/public/common/user_agent.h" #include "extensions/common/core_extensions_api_provider.h" #include "extensions/common/extension_urls.h" #include "extensions/common/features/simple_feature.h" diff --git a/shell/renderer/api/electron_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc index 23f93f04abdee..062038e6016da 100644 --- a/shell/renderer/api/electron_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -11,6 +11,7 @@ #include "base/containers/span.h" #include "base/memory/memory_pressure_listener.h" +#include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" #include "components/spellcheck/renderer/spellcheck.h" #include "content/public/renderer/render_frame.h" @@ -410,8 +411,8 @@ class WebFrameRenderer final : public gin::Wrappable, content::RenderFrame** render_frame_ptr) { auto* frame = render_frame(); if (!frame) { - *error_msg = base::ToString("Render frame was torn down before webFrame.", - method_name, " could be executed"); + *error_msg = base::StrCat({"Render frame was torn down before webFrame.", + method_name, " could be executed"}); return false; } *render_frame_ptr = frame; diff --git a/spec/ts-smoke/electron/main.ts b/spec/ts-smoke/electron/main.ts index 62f2726970713..2dbf258aa9113 100644 --- a/spec/ts-smoke/electron/main.ts +++ b/spec/ts-smoke/electron/main.ts @@ -1177,7 +1177,7 @@ session.defaultSession.clearStorageData({ storages: ['shadercache', 'cachestorag // @ts-expect-error Invalid type value session.defaultSession.clearStorageData({ storages: ['wrong_path'] }); -session.defaultSession.clearStorageData({ quotas: ['syncable', 'temporary'] }); +session.defaultSession.clearStorageData({ quotas: ['temporary'] }); // @ts-expect-error Invalid type value session.defaultSession.clearStorageData({ quotas: ['bad_type'] }); From 3e5cabde39369dde07bbe7cc276167f7d82e9129 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:20:55 -0500 Subject: [PATCH 014/339] refactor: use private inheritance from `mojo::MessageReceiver` (#45996) * refactor: make UtilityProcessWrapper inherit privately from mojo::MessageReceiver Co-authored-by: Charles Kerr * refactor: make ParentPort inherit privately from mojo::MessageReceiver Co-authored-by: Charles Kerr * refactor: make MessagePort inherit privately from mojo::MessageReceiver Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_utility_process.h | 2 +- shell/browser/api/message_port.h | 2 +- shell/services/node/parent_port.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/browser/api/electron_api_utility_process.h b/shell/browser/api/electron_api_utility_process.h index c7fb48b0dde7e..6b1be4da25345 100644 --- a/shell/browser/api/electron_api_utility_process.h +++ b/shell/browser/api/electron_api_utility_process.h @@ -43,7 +43,7 @@ class UtilityProcessWrapper final : public gin::Wrappable, public gin_helper::Pinnable, public gin_helper::EventEmitterMixin, - public mojo::MessageReceiver, + private mojo::MessageReceiver, public node::mojom::NodeServiceClient, public content::ServiceProcessHost::Observer { public: diff --git a/shell/browser/api/message_port.h b/shell/browser/api/message_port.h index e46be76cbebb7..ac598bf34a946 100644 --- a/shell/browser/api/message_port.h +++ b/shell/browser/api/message_port.h @@ -29,7 +29,7 @@ namespace electron { // A non-blink version of blink::MessagePort. class MessagePort final : public gin::Wrappable, public gin_helper::CleanedUpAtExit, - public mojo::MessageReceiver { + private mojo::MessageReceiver { public: ~MessagePort() override; static gin::Handle Create(v8::Isolate* isolate); diff --git a/shell/services/node/parent_port.h b/shell/services/node/parent_port.h index 20f6adc5b1344..b39d825c75215 100644 --- a/shell/services/node/parent_port.h +++ b/shell/services/node/parent_port.h @@ -31,7 +31,7 @@ namespace electron { // for the lifetime of a Utility Process which // also means that GC lifecycle is ignored by this class. class ParentPort final : public gin::Wrappable, - public mojo::MessageReceiver { + private mojo::MessageReceiver { public: static ParentPort* GetInstance(); static gin::Handle Create(v8::Isolate* isolate); From 1fc3a606555b8031b84cc9ef19f06a5ea3b40b8c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 13 Mar 2025 04:00:32 -0500 Subject: [PATCH 015/339] fix: `system-context-menu` should only fire in draggable regions (36-x-y) (#46002) fix: `system-context-menu` should only fire in draggable regions (#45893) fix: system-context-menu should only fire in draggable regions Co-authored-by: Shelley Vohr --- patches/chromium/.patches | 2 +- ...y_chromium_handling_of_mouse_events.patch} | 60 ++++++++++++++++++- ...ivate_background_material_on_windows.patch | 2 +- ...x_remove_caption-removing_style_call.patch | 2 +- shell/browser/native_window_views_win.cc | 9 --- .../electron_desktop_window_tree_host_win.cc | 11 ++++ .../electron_desktop_window_tree_host_win.h | 1 + 7 files changed, 72 insertions(+), 15 deletions(-) rename patches/chromium/{chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch => chore_modify_chromium_handling_of_mouse_events.patch} (57%) diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 5a18b7a6a7d0c..5a9fe786850ee 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -83,7 +83,7 @@ feat_filter_out_non-shareable_windows_in_the_current_application_in.patch disable_freezing_flags_after_init_in_node.patch short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch chore_add_electron_deps_to_gitignores.patch -chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch +chore_modify_chromium_handling_of_mouse_events.patch add_electron_deps_to_license_credits_file.patch fix_crash_loading_non-standard_schemes_in_iframes.patch create_browser_v8_snapshot_file_name_fuse.patch diff --git a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch similarity index 57% rename from patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch rename to patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch index 23dd5ed59e3f2..0d84247f270e1 100644 --- a/patches/chromium/chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch +++ b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch @@ -1,11 +1,38 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Fri, 29 Jul 2022 00:29:35 +0900 -Subject: chore: allow chromium to handle synthetic mouse events for touch +Subject: chore: modify chromium handling of mouse events -With WCO, allow chromium to handle synthetic mouse events generated for touch +This patch does the following: + +1. When Windows Control Overlay is enabled, it allows chromium to handle synthetic mouse events generated for touch actions in the non-client caption area. +2. It calls HandleMouseEvent on the delegate earlier in HandleMouseEventInternal, so that Electron can selectively disable +draggable regions to allow events to propagate to the underlying renderer. +diff --git a/ui/events/event.h b/ui/events/event.h +index 39b5a8fdd165efd74b00256552b51b5413107958..bfc4ef4f50efff4a77f2aef64335bb7e34c69f34 100644 +--- a/ui/events/event.h ++++ b/ui/events/event.h +@@ -587,6 +587,9 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { + + const PointerDetails& pointer_details() const { return pointer_details_; } + ++ bool is_system_menu() const { return is_system_menu_; } ++ void set_is_system_menu(bool is_menu) { is_system_menu_ = is_menu; } ++ + // Event: + std::string ToString() const override; + std::unique_ptr Clone() const override; +@@ -619,6 +622,8 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { + + // Structure for holding pointer details for implementing PointerEvents API. + PointerDetails pointer_details_; ++ ++ bool is_system_menu_ = false; + }; + + class ScrollEvent; 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 55c426aee12da4d4d1f62dc7d489133e8d21dc49..40c88bf14d2d4fe841e29b32414361688ae438e5 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -34,7 +61,7 @@ index 4865d79c95c34d8cead96d3bb8063a0e2bd6076b..ebfa09ed15dca98b75a013e3dcbb566c // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..679efe5af4954d9ddca7bc5ccee6b8d6fac966a3 100644 +index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..ad844f4d6d150aee3e00fd6465600bfb248d79d2 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -3146,15 +3146,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, @@ -59,6 +86,33 @@ index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..679efe5af4954d9ddca7bc5ccee6b8d6 return 0; } } +@@ -3177,6 +3181,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, + // handle alt-space, or in the frame itself. + is_right_mouse_pressed_on_caption_ = false; + ReleaseCapture(); ++ + // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() + // expect screen coordinates. + POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); +@@ -3184,7 +3189,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, + w_param = static_cast(SendMessage( + hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); + if (w_param == HTCAPTION || w_param == HTSYSMENU) { +- ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); ++ LONG message_time = GetMessageTime(); ++ CHROME_MSG msg = {hwnd(), ++ message, ++ w_param, ++ l_param, ++ static_cast(message_time), ++ {CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)}}; ++ ui::MouseEvent event(msg); ++ event.set_is_system_menu(true); ++ if (!delegate_->HandleMouseEvent(&event)) ++ ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); + return 0; + } + } else if (message == WM_NCLBUTTONDOWN && diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h index de8fd5657e6885f74a5970bdd49647a6f1616387..4af87792edc7a147468077b834582510550e35e6 100644 --- a/ui/views/win/hwnd_message_handler_delegate.h diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index ae8980e16502f..81656d9ebac69 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,7 +14,7 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index e061dc5e64fde1a9c531dbedae0c137105700ac7..3d0cc571b471bb79d845f8b0a1da168ace33bb6b 100644 +index d68fd93d4cb912b9b27d3a7cdfecc6faf638b91e..22ed43bdf4dbaccc598135807abc8383c52db50e 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -934,13 +934,13 @@ void HWNDMessageHandler::FrameTypeChanged() { diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index 1811e95183f35..336e619b4b231 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,7 +18,7 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 679efe5af4954d9ddca7bc5ccee6b8d6fac966a3..e061dc5e64fde1a9c531dbedae0c137105700ac7 100644 +index ad844f4d6d150aee3e00fd6465600bfb248d79d2..d68fd93d4cb912b9b27d3a7cdfecc6faf638b91e 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -1788,7 +1788,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 91bd2413ce6e5..a08d347c3b193 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -288,15 +288,6 @@ bool NativeWindowViews::PreHandleMSG(UINT message, return false; } - case WM_RBUTTONUP: { - if (!has_frame()) { - bool prevent_default = false; - NotifyWindowSystemContextMenu(GET_X_LPARAM(l_param), - GET_Y_LPARAM(l_param), &prevent_default); - return prevent_default; - } - return false; - } case WM_GETMINMAXINFO: { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index ee17c885fc30b..b2c5f07ccf501 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -108,6 +108,17 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEventForCaption( return native_window_view_->IsWindowControlsOverlayEnabled(); } +bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { + if (event->is_system_menu() && !native_window_view_->has_frame()) { + bool prevent_default = false; + native_window_view_->NotifyWindowSystemContextMenu(event->x(), event->y(), + &prevent_default); + return prevent_default; + } + + return views::DesktopWindowTreeHostWin::HandleMouseEvent(event); +} + void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated( ui::NativeTheme* observed_theme) { HWND hWnd = GetAcceleratedWidget(); diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h index d654431b66ef2..cc51d42e83534 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -40,6 +40,7 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, bool GetClientAreaInsets(gfx::Insets* insets, HMONITOR monitor) const override; bool HandleMouseEventForCaption(UINT message) const override; + bool HandleMouseEvent(ui::MouseEvent* event) override; // ui::NativeThemeObserver: void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; From 515f5847e13b535a04881ab7f80b02e3b9ddd4f2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:05:55 +0100 Subject: [PATCH 016/339] refactor: remove usage of V8's `{Attach|Detach}CppHeap()` (#45979) * refactor: remove usage of V8's {Attach|Detach}CppHeap() Co-authored-by: Shelley Vohr * chore: remove revert patch Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/node/.patches | 1 + ...ch_cppgc_heap_on_v8_isolate_creation.patch | 320 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch diff --git a/patches/node/.patches b/patches/node/.patches index 9322abd8557ad..d0f24de31659b 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -46,3 +46,4 @@ fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch feat_add_oom_error_callback_in_node_isolatesettings.patch fix_-wnonnull_warning.patch +refactor_attach_cppgc_heap_on_v8_isolate_creation.patch diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch new file mode 100644 index 0000000000000..15b57e6905062 --- /dev/null +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -0,0 +1,320 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Fri, 7 Mar 2025 11:18:41 -0600 +Subject: refactor: attach cppgc heap on v8::Isolate creation + +Refs https://issues.chromium.org/issues/42203693 + +v8/Node Commits by V8 Team: + +* https://github.com/v8/node/pull/208 +* https://github.com/v8/node/pull/209 +* https://github.com/v8/node/pull/210 +* https://github.com/v8/node/pull/211 +* https://github.com/v8/node/pull/212 +* https://github.com/v8/node/pull/213 + +This can be removed when Node.js upgrades to a version of V8 containing CLs +from the above issue. + +diff --git a/src/api/environment.cc b/src/api/environment.cc +index e72bee385865c7d34e9eea6b90c6d911d592f8af..d3d1040b7a1a6b9c4a1fa2399e9235ec3b0b2990 100644 +--- a/src/api/environment.cc ++++ b/src/api/environment.cc +@@ -315,6 +315,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, + MultiIsolatePlatform* platform, + const SnapshotData* snapshot_data, + const IsolateSettings& settings) { ++ if (params->cpp_heap == nullptr) { ++ params->cpp_heap = ++ v8::CppHeap::Create(platform, v8::CppHeapCreateParams{{}}).release(); ++ } + Isolate* isolate = Isolate::Allocate(); + if (isolate == nullptr) return nullptr; + +@@ -358,9 +362,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const EmbedderSnapshotData* snapshot_data, +- const IsolateSettings& settings) { ++ const IsolateSettings& settings, ++ std::unique_ptr cpp_heap) { + Isolate::CreateParams params; + if (allocator != nullptr) params.array_buffer_allocator = allocator; ++ if (cpp_heap) ++ params.cpp_heap = cpp_heap.release(); + return NewIsolate(¶ms, + event_loop, + platform, +diff --git a/src/env.cc b/src/env.cc +index 1c1062a3996f2bb7de9e91f7f4385c8f8d20f490..b0156ee26c29ebe5b79c97834f3bfe8b56df50c6 100644 +--- a/src/env.cc ++++ b/src/env.cc +@@ -575,14 +575,6 @@ IsolateData::IsolateData(Isolate* isolate, + // We do not care about overflow since we just want this to be different + // from the cppgc id. + uint16_t non_cppgc_id = cppgc_id + 1; +- if (cpp_heap == nullptr) { +- cpp_heap_ = CppHeap::Create(platform, v8::CppHeapCreateParams{{}}); +- // TODO(joyeecheung): pass it into v8::Isolate::CreateParams and let V8 +- // own it when we can keep the isolate registered/task runner discoverable +- // during isolate disposal. +- isolate->AttachCppHeap(cpp_heap_.get()); +- } +- + { + // GC could still be run after the IsolateData is destroyed, so we store + // the ids in a static map to ensure pointers to them are still valid +@@ -605,14 +597,6 @@ IsolateData::IsolateData(Isolate* isolate, + } + } + +-IsolateData::~IsolateData() { +- if (cpp_heap_ != nullptr) { +- // The CppHeap must be detached before being terminated. +- isolate_->DetachCppHeap(); +- cpp_heap_->Terminate(); +- } +-} +- + // Deprecated API, embedders should use v8::Object::Wrap() directly instead. + void SetCppgcReference(Isolate* isolate, + Local object, +diff --git a/src/env.h b/src/env.h +index 1f8dc8f88d40ca95ba13d6517b2b5ed83184e1ce..ec3a813b3b864a0eda2e4aa0748b1447001fca9a 100644 +--- a/src/env.h ++++ b/src/env.h +@@ -155,7 +155,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { + ArrayBufferAllocator* node_allocator = nullptr, + const EmbedderSnapshotData* embedder_snapshot_data = nullptr, + std::shared_ptr options = nullptr); +- ~IsolateData(); + + SET_MEMORY_INFO_NAME(IsolateData) + SET_SELF_SIZE(IsolateData) +@@ -258,7 +257,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { + const SnapshotData* snapshot_data_; + std::optional snapshot_config_; + +- std::unique_ptr cpp_heap_; + std::shared_ptr options_; + worker::Worker* worker_context_ = nullptr; + PerIsolateWrapperData* wrapper_data_; +diff --git a/src/node.cc b/src/node.cc +index b2b10ffb572f010992f221de752618fd56b5d50e..0ed78ab6b52906e980eebf1f625a1c7cbfc8097b 100644 +--- a/src/node.cc ++++ b/src/node.cc +@@ -1222,10 +1222,6 @@ InitializeOncePerProcessInternal(const std::vector& args, + result->platform_ = per_process::v8_platform.Platform(); + } + +- if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { +- V8::Initialize(); +- } +- + if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) { + v8::PageAllocator* allocator = nullptr; + if (result->platform_ != nullptr) { +@@ -1234,6 +1230,10 @@ InitializeOncePerProcessInternal(const std::vector& args, + cppgc::InitializeProcess(allocator); + } + ++ if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { ++ V8::Initialize(); ++ } ++ + #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.h b/src/node.h +index 98ad0ea649eaef43d1f5231f7bc4044e100e08d7..c295cce8f5c7965cce4d2e4c0614dbe076986a4c 100644 +--- a/src/node.h ++++ b/src/node.h +@@ -589,7 +589,8 @@ NODE_EXTERN v8::Isolate* NewIsolate( + struct uv_loop_s* event_loop, + MultiIsolatePlatform* platform, + const EmbedderSnapshotData* snapshot_data = nullptr, +- const IsolateSettings& settings = {}); ++ const IsolateSettings& settings = {}, ++ std::unique_ptr cpp_heap = {}); + NODE_EXTERN v8::Isolate* NewIsolate( + std::shared_ptr allocator, + struct uv_loop_s* event_loop, +diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc +index 4119ac1b002681d39711eac810ca2fcc2702ffc7..790347056cde949ffe6cf8498a7eca0c4864c997 100644 +--- a/src/node_main_instance.cc ++++ b/src/node_main_instance.cc +@@ -44,6 +44,8 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, + isolate_params_(std::make_unique()), + snapshot_data_(snapshot_data) { + isolate_params_->array_buffer_allocator = array_buffer_allocator_.get(); ++ isolate_params_->cpp_heap = ++ v8::CppHeap::Create(platform_, v8::CppHeapCreateParams{{}}).release(); + + isolate_ = + NewIsolate(isolate_params_.get(), event_loop, platform, snapshot_data); +@@ -81,9 +83,9 @@ NodeMainInstance::~NodeMainInstance() { + // This should only be done on a main instance that owns its isolate. + // IsolateData must be freed before UnregisterIsolate() is called. + isolate_data_.reset(); +- platform_->UnregisterIsolate(isolate_); + } + isolate_->Dispose(); ++ platform_->UnregisterIsolate(isolate_); + } + + ExitCode NodeMainInstance::Run() { +diff --git a/src/node_worker.cc b/src/node_worker.cc +index 1fc3774948dae3c0aae7d2aef563e18ecd4243a3..a610ee24ff18bddc3849aec3a43c2037b9ab5d53 100644 +--- a/src/node_worker.cc ++++ b/src/node_worker.cc +@@ -230,13 +230,8 @@ class WorkerThreadData { + *static_cast(data) = true; + }, &platform_finished); + +- // The order of these calls is important; if the Isolate is first disposed +- // and then unregistered, there is a race condition window in which no +- // new Isolate at the same address can successfully be registered with +- // the platform. +- // (Refs: https://github.com/nodejs/node/issues/30846) +- w_->platform_->UnregisterIsolate(isolate); + isolate->Dispose(); ++ w_->platform_->UnregisterIsolate(isolate); + + // Wait until the platform has cleaned up all relevant resources. + while (!platform_finished) { +diff --git a/src/util.cc b/src/util.cc +index 3e9dfb4392fb3e3deaab5506771f01be65bc5dda..416e0479ddf740c6d3e2d4ea9466ac060b038294 100644 +--- a/src/util.cc ++++ b/src/util.cc +@@ -726,8 +726,8 @@ RAIIIsolateWithoutEntering::RAIIIsolateWithoutEntering(const SnapshotData* data) + } + + RAIIIsolateWithoutEntering::~RAIIIsolateWithoutEntering() { +- per_process::v8_platform.Platform()->UnregisterIsolate(isolate_); + isolate_->Dispose(); ++ per_process::v8_platform.Platform()->UnregisterIsolate(isolate_); + } + + RAIIIsolate::RAIIIsolate(const SnapshotData* data) +diff --git a/test/cctest/node_test_fixture.h b/test/cctest/node_test_fixture.h +index 3414c0be8ad777f0b9836323150071b688831a38..82013ffe7667d53248bd616efb79b294e4ae47dd 100644 +--- a/test/cctest/node_test_fixture.h ++++ b/test/cctest/node_test_fixture.h +@@ -123,8 +123,8 @@ class NodeTestFixture : public NodeZeroIsolateTestFixture { + void TearDown() override { + platform->DrainTasks(isolate_); + isolate_->Exit(); +- platform->UnregisterIsolate(isolate_); + isolate_->Dispose(); ++ platform->UnregisterIsolate(isolate_); + isolate_ = nullptr; + NodeZeroIsolateTestFixture::TearDown(); + } +diff --git a/test/cctest/test_cppgc.cc b/test/cctest/test_cppgc.cc +index edd413ae9b956b2e59e8166785adef6a8ff06d51..d1c1549efcb0320bc0f7d354db2101acc0930005 100644 +--- a/test/cctest/test_cppgc.cc ++++ b/test/cctest/test_cppgc.cc +@@ -46,18 +46,15 @@ int CppGCed::kDestructCount = 0; + int CppGCed::kTraceCount = 0; + + TEST_F(NodeZeroIsolateTestFixture, ExistingCppHeapTest) { +- v8::Isolate* isolate = +- node::NewIsolate(allocator.get(), ¤t_loop, platform.get()); + + // Create and attach the CppHeap before we set up the IsolateData so that + // it recognizes the existing heap. + std::unique_ptr cpp_heap = + v8::CppHeap::Create(platform.get(), v8::CppHeapCreateParams{{}}); + +- // TODO(joyeecheung): pass it into v8::Isolate::CreateParams and let V8 +- // own it when we can keep the isolate registered/task runner discoverable +- // during isolate disposal. +- isolate->AttachCppHeap(cpp_heap.get()); ++ v8::Isolate* isolate = ++ node::NewIsolate(allocator.get(), ¤t_loop, platform.get(), ++ nullptr, {}, std::move(cpp_heap)); + + // Try creating Context + IsolateData + Environment. + { +@@ -102,13 +99,11 @@ TEST_F(NodeZeroIsolateTestFixture, ExistingCppHeapTest) { + platform->DrainTasks(isolate); + + // Cleanup. +- isolate->DetachCppHeap(); +- cpp_heap->Terminate(); + platform->DrainTasks(isolate); + } + +- platform->UnregisterIsolate(isolate); + isolate->Dispose(); ++ platform->UnregisterIsolate(isolate); + + // Check that all the objects are created and destroyed properly. + EXPECT_EQ(CppGCed::kConstructCount, 100); +diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc +index 14e82cc80ff73084fb43b2ef07febfd2667a0abc..b6a92f1685d1083c8f0c0b3ed110509f6d76b59f 100644 +--- a/test/cctest/test_environment.cc ++++ b/test/cctest/test_environment.cc +@@ -623,6 +623,9 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { + // Allocate and initialize Isolate. + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = allocator.get(); ++ create_params.cpp_heap = ++ v8::CppHeap::Create(platform.get(), v8::CppHeapCreateParams{{}}) ++ .release(); + v8::Isolate* isolate = v8::Isolate::Allocate(); + CHECK_NOT_NULL(isolate); + platform->RegisterIsolate(isolate, ¤t_loop); +@@ -672,8 +675,8 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { + } + + // Cleanup. +- platform->UnregisterIsolate(isolate); + isolate->Dispose(); ++ platform->UnregisterIsolate(isolate); + } + #endif // _WIN32 + +diff --git a/test/cctest/test_platform.cc b/test/cctest/test_platform.cc +index c2d7893813000601502d050f21ad5c740c6a3b8f..3042f63201bd0df3ad316e09f0f54e210cea8831 100644 +--- a/test/cctest/test_platform.cc ++++ b/test/cctest/test_platform.cc +@@ -101,8 +101,8 @@ TEST_F(NodeZeroIsolateTestFixture, IsolatePlatformDelegateTest) { + + // Graceful shutdown + delegate->Shutdown(); +- platform->UnregisterIsolate(isolate); + isolate->Dispose(); ++ platform->UnregisterIsolate(isolate); + } + + TEST_F(PlatformTest, TracingControllerNullptr) { +diff --git a/test/fuzzers/fuzz_env.cc b/test/fuzzers/fuzz_env.cc +index bace3051f8cecd5050d4707f2431973752a22188..5ca295848a974c70ff1a9094eb288ef7e658d8e5 100644 +--- a/test/fuzzers/fuzz_env.cc ++++ b/test/fuzzers/fuzz_env.cc +@@ -65,8 +65,8 @@ public: + void Teardown() { + platform->DrainTasks(isolate_); + isolate_->Exit(); +- platform->UnregisterIsolate(isolate_); + isolate_->Dispose(); ++ platform->UnregisterIsolate(isolate_); + isolate_ = nullptr; + } + }; +diff --git a/test/fuzzers/fuzz_strings.cc b/test/fuzzers/fuzz_strings.cc +index 8f5e1a473e3148e0bcdcc3c2fd582685665ce461..936876cdae20d29618d3789a5ab46a1b3101a79d 100644 +--- a/test/fuzzers/fuzz_strings.cc ++++ b/test/fuzzers/fuzz_strings.cc +@@ -72,8 +72,8 @@ public: + void Teardown() { + platform->DrainTasks(isolate_); + isolate_->Exit(); +- platform->UnregisterIsolate(isolate_); + isolate_->Dispose(); ++ platform->UnregisterIsolate(isolate_); + isolate_ = nullptr; + } + }; From 0ea8decb0ae37dd6204e7dd8de3bdaef6c5ac027 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:41:22 -0500 Subject: [PATCH 017/339] refactor: make a variadic `gin_helper::internal::InvokeFactory()` (#46027) refactor: make a variadic gin_helper::internal::InvokeFactory() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/common/gin_helper/constructor.h | 157 ++++++-------------------- 1 file changed, 34 insertions(+), 123 deletions(-) diff --git a/shell/common/gin_helper/constructor.h b/shell/common/gin_helper/constructor.h index 728ee0f2ebc11..7ae5e096c15b7 100644 --- a/shell/common/gin_helper/constructor.h +++ b/shell/common/gin_helper/constructor.h @@ -5,6 +5,8 @@ #ifndef ELECTRON_SHELL_COMMON_GIN_HELPER_CONSTRUCTOR_H_ #define ELECTRON_SHELL_COMMON_GIN_HELPER_CONSTRUCTOR_H_ +#include + #include "shell/common/gin_helper/function_template.h" #include "shell/common/gin_helper/wrappable_base.h" @@ -12,131 +14,40 @@ namespace gin_helper { namespace internal { -// This set of templates invokes a base::RepeatingCallback by converting the -// Arguments into native types. It relies on the function_template.h to provide -// helper templates. -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& callback) { - return callback.Run(); -} - -template -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& callback) { - typename CallbackParamTraits::LocalType a1; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1)) - return nullptr; - return callback.Run(a1); -} - -template -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& callback) { - typename CallbackParamTraits::LocalType a1; - typename CallbackParamTraits::LocalType a2; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a2)) - return nullptr; - return callback.Run(a1, a2); -} - -template -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& callback) { - typename CallbackParamTraits::LocalType a1; - typename CallbackParamTraits::LocalType a2; - typename CallbackParamTraits::LocalType a3; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a2) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a3)) - return nullptr; - return callback.Run(a1, a2, a3); -} - -template -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& callback) { - typename CallbackParamTraits::LocalType a1; - typename CallbackParamTraits::LocalType a2; - typename CallbackParamTraits::LocalType a3; - typename CallbackParamTraits::LocalType a4; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a2) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a3) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a4)) - return nullptr; - return callback.Run(a1, a2, a3, a4); -} +// Convert a `gin::Argument`'s arguments into a tuple of native types +// by iteratively calling gin_helper::GetNextArgument(). +template +class GinArgumentsToTuple { + public: + [[nodiscard]] static std::pair> GetArgs( + gin::Arguments* args) { + bool ok = true; + InvokerOptions opts{.holder_is_first_argument = true}; + auto tup = std::make_tuple(GetNextArg(args, opts, ok)...); + return {ok, std::move(tup)}; + } -template -inline WrappableBase* InvokeFactory( - gin::Arguments* args, - const base::RepeatingCallback& - callback) { - typename CallbackParamTraits::LocalType a1; - typename CallbackParamTraits::LocalType a2; - typename CallbackParamTraits::LocalType a3; - typename CallbackParamTraits::LocalType a4; - typename CallbackParamTraits::LocalType a5; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a2) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a3) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a4) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a5)) - return nullptr; - return callback.Run(a1, a2, a3, a4, a5); -} + private: + template + static T GetNextArg(gin::Arguments* args, InvokerOptions& opts, bool& ok) { + auto val = T{}; + ok = ok && gin_helper::GetNextArgument(args, opts, 0, &val); + opts.holder_is_first_argument = false; + return val; + } +}; -template -inline WrappableBase* InvokeFactory( +// Invoke a callback with arguments extracted from `args`. +template +WrappableBase* InvokeFactory( gin::Arguments* args, - const base::RepeatingCallback& - callback) { - typename CallbackParamTraits::LocalType a1; - typename CallbackParamTraits::LocalType a2; - typename CallbackParamTraits::LocalType a3; - typename CallbackParamTraits::LocalType a4; - typename CallbackParamTraits::LocalType a5; - typename CallbackParamTraits::LocalType a6; - if (!gin_helper::GetNextArgument(args, {.holder_is_first_argument = true}, 0, - &a1) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a2) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a3) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a4) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a5) || - !gin_helper::GetNextArgument(args, {.holder_is_first_argument = false}, 0, - &a6)) - return nullptr; - return callback.Run(a1, a2, a3, a4, a5, a6); + const base::RepeatingCallback& callback) { + auto [ok, tup] = GinArgumentsToTuple::GetArgs(args); + if (!ok) + return {}; + return std::apply( + [&callback](Types... args) { return callback.Run(std::move(args)...); }, + std::move(tup)); } template @@ -151,7 +62,7 @@ void InvokeNew(const base::RepeatingCallback& factory, WrappableBase* object; { // Don't continue if the constructor throws an exception. - v8::TryCatch try_catch(isolate); + v8::TryCatch try_catch{isolate}; object = internal::InvokeFactory(args, factory); if (try_catch.HasCaught()) { try_catch.ReThrow(); From 8275f7e402acae49119192f1d0435edd49afcb4b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:57:58 +0100 Subject: [PATCH 018/339] fix: package import existence verification (#46023) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- ...fix_allow_passing_fileexists_fn_to_legacymainresolve.patch | 4 ++-- ...op_using_deprecated_fields_of_fastapicallbackoptions.patch | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch index b497527571d41..79eff671572bd 100644 --- a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch +++ b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch @@ -53,7 +53,7 @@ index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..03082f0ec4f91382933eec48e77331cd const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? packageConfig.main || './' : ''; diff --git a/src/node_file.cc b/src/node_file.cc -index 1d22e19f16d5ad82466b0724971b2e4a685682f7..9619d10710ffbbdc73fa7d59d1b797c8d0b3a956 100644 +index 1d22e19f16d5ad82466b0724971b2e4a685682f7..3d7e303741a73134e140152bed637fe5ae8bc1db 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3220,13 +3220,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { @@ -73,7 +73,7 @@ index 1d22e19f16d5ad82466b0724971b2e4a685682f7..9619d10710ffbbdc73fa7d59d1b797c8 + env->isolate(), file_path.c_str(), v8::NewStringType::kNormal) + .ToLocalChecked()}; + MaybeLocal maybe_is_file = is_file_function->Call(env->context(), v8::Undefined(env->isolate()), 1, argv); -+ if (maybe_is_file.IsEmpty()) { ++ if (!maybe_is_file.IsEmpty()) { + bool is_file = maybe_is_file.ToLocalChecked()->BooleanValue(env->isolate()); + return is_file ? BindingData::FilePathIsFileReturnType::kIsFile + : BindingData::FilePathIsFileReturnType::kIsNotFile; diff --git a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch index f1e2a47021553..8c61c3b3d788c 100644 --- a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch +++ b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch @@ -40,7 +40,7 @@ index 0f0cde7be431dcb80c5314b1a9da49886c436d1c..f6d2bd439cad8b9f91c9d9a6cdb302e6 } HistogramBase* histogram; diff --git a/src/node_file.cc b/src/node_file.cc -index 9619d10710ffbbdc73fa7d59d1b797c8d0b3a956..f2a5c0939b60c582dbbecc07add1e903a9183b95 100644 +index 3d7e303741a73134e140152bed637fe5ae8bc1db..5e744bc34b9dc364e8f20adfd37ee41d76451170 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1061,13 +1061,8 @@ static int32_t FastInternalModuleStat( From 5c73799f520f1628eb481424fae152cce0e1ce01 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:58:19 +0100 Subject: [PATCH 019/339] fix: don't crash Web Workers on unhandled rejections (#46021) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/renderer/web_worker_observer.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index 44c2dc9edd0eb..a3a1caf8ded6d 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -85,6 +85,9 @@ void WebWorkerObserver::WorkerScriptReadyForEvaluation( } } + // We do not want to crash Web Workers on unhandled rejections. + env->options()->unhandled_rejections = "warn-with-error-code"; + // Add Electron extended APIs. electron_bindings_->BindTo(env->isolate(), env->process_object()); From a3af41674ff7e055d8771b77ea0e29462ac51b48 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:58:47 +0100 Subject: [PATCH 020/339] build: roll sysroots again (#46026) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- script/sysroots.json | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/script/sysroots.json b/script/sysroots.json index 7948eb68c101d..28eff97b0b588 100644 --- a/script/sysroots.json +++ b/script/sysroots.json @@ -1,14 +1,14 @@ { "bullseye_amd64": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "0be326b106f0df7b8547ec8d3b58ccb95435c8414a45cda675c4805821d4d860", + "Key": "20250129T203412Z-2", + "Sha256Sum": "f89d7f27fdff95336d20c899c795f70f16ba488205f7f493b0d118cbf89dea59", "SysrootDir": "debian_bullseye_amd64-sysroot", "Tarball": "debian_bullseye_amd64_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_arm64": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "1225cd518c1609e54033bb6a1c687875d4f4c21b2dbd5d5d81c4b5927d0fc0f1", + "Key": "20250129T203412Z-2", + "Sha256Sum": "68164118871fddfe9212aa22364fe9c015023ab68a76af6910a319a800246d58", "SysrootDir": "debian_bullseye_arm64-sysroot", "Tarball": "debian_bullseye_arm64_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" @@ -21,31 +21,38 @@ "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_armhf": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "ed5a71ce5fc6d1691817c3b203139328ee88395c9e54ff726f67c84ee1561e65", + "Key": "20250129T203412Z-2", + "Sha256Sum": "409047dc2431e4fa4499facc5ca13a74e1941f7b85e414b6e1b4298bc6caca1a", "SysrootDir": "debian_bullseye_armhf-sysroot", "Tarball": "debian_bullseye_armhf_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_i386": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "57f800042b0c4bd00a8755da165823cc70decc0481b78d751924db7470af6b5e", + "Key": "20250129T203412Z-2", + "Sha256Sum": "1ae04410396ac2a46c60f112c7eb8e47d39cb73579f99d75ba81fb1b08ef1c08", "SysrootDir": "debian_bullseye_i386-sysroot", "Tarball": "debian_bullseye_i386_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_mips64el": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "187b8644a949d0124cb00fa099a8a5842e9a88bb48d8d1c682604ebf546796b7", + "Key": "20250129T203412Z-2", + "Sha256Sum": "3b6d3383614bbed33a6580ab7c15a55b92cd2d400617bd956c0ab3e5cd3873d2", "SysrootDir": "debian_bullseye_mips64el-sysroot", "Tarball": "debian_bullseye_mips64el_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" }, "bullseye_mipsel": { - "Key": "20230611T210420Z-2", - "Sha256Sum": "f08771dc7a813e7f0fd540b49a1b611416979630b0009e9ecc51f999a7543081", + "Key": "20250129T203412Z-2", + "Sha256Sum": "9922c66285fa037aaddb4afd4bb13eaaa65fdd889b35d94c61fb4e4076c228f0", "SysrootDir": "debian_bullseye_mipsel-sysroot", "Tarball": "debian_bullseye_mipsel_sysroot.tar.xz", "URL": "https://dev-cdn.electronjs.org/linux-sysroots" + }, + "bullseye_ppc64el": { + "Key": "20250129T203412Z-2", + "Sha256Sum": "370e28ad70f2edca39fad2f16dd23e315b67728d61c327cda42d32300b06812c", + "SysrootDir": "debian_bullseye_ppc64el-sysroot", + "Tarball": "debian_bullseye_ppc64el_sysroot.tar.xz", + "URL": "https://dev-cdn.electronjs.org/linux-sysroots" } } From a1ec212049d5593f38c3f89cc0986f30e489c211 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:54:21 -0500 Subject: [PATCH 021/339] fix: emit `context-menu` event in Windows draggable regions (#46032) fix: emit context-menu event in Windows draggable regions Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window_views_win.cc | 20 +++++++++++++++---- .../electron_desktop_window_tree_host_win.cc | 7 +++++++ spec/api-web-contents-spec.ts | 7 ++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index a08d347c3b193..04016b304c88c 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -10,6 +10,7 @@ #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" #include "content/public/browser/browser_accessibility_state.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/browser.h" #include "shell/browser/native_window_views.h" #include "shell/browser/ui/views/root_view.h" @@ -288,6 +289,11 @@ bool NativeWindowViews::PreHandleMSG(UINT message, return false; } + case WM_RBUTTONUP: { + if (!has_frame()) + electron::api::WebContents::SetDisableDraggableRegions(false); + return false; + } case WM_GETMINMAXINFO: { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); @@ -411,10 +417,16 @@ bool NativeWindowViews::PreHandleMSG(UINT message, return false; } case WM_CONTEXTMENU: { - bool prevent_default = false; - NotifyWindowSystemContextMenu(GET_X_LPARAM(l_param), - GET_Y_LPARAM(l_param), &prevent_default); - return prevent_default; + // We don't want to trigger system-context-menu here if we have a + // frameless window as it'll already be emitted in + // ElectronDesktopWindowTreeHostWin::HandleMouseEvent. + if (has_frame()) { + bool prevent_default = false; + NotifyWindowSystemContextMenu(GET_X_LPARAM(l_param), + GET_Y_LPARAM(l_param), &prevent_default); + return prevent_default; + } + return false; } case WM_SYSCOMMAND: { // Mask is needed to account for double clicking title bar to maximize diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index b2c5f07ccf501..ac4ffc26e0ddf 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -6,6 +6,7 @@ #include "base/win/windows_version.h" #include "electron/buildflags/buildflags.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/native_window_views.h" #include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/win/dark_mode.h" @@ -113,6 +114,12 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { bool prevent_default = false; native_window_view_->NotifyWindowSystemContextMenu(event->x(), event->y(), &prevent_default); + // If the user prevents default behavior, emit contextmenu event to + // allow bringing up the custom menu. + if (prevent_default) { + electron::api::WebContents::SetDisableDraggableRegions(true); + views::DesktopWindowTreeHostWin::HandleMouseEvent(event); + } return prevent_default; } diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index aa2c7d5297453..52883991d32c0 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -2896,8 +2896,13 @@ describe('webContents module', () => { expect(contextMenuEmitCount).to.equal(1); }); - ifit(process.platform !== 'win32')('emits when right-clicked in page in a draggable region', async () => { + it('emits when right-clicked in page in a draggable region', async () => { const w = new BrowserWindow({ show: false }); + + if (process.platform === 'win32') { + w.on('system-context-menu', (event) => { event.preventDefault(); }); + } + await w.loadFile(path.join(fixturesPath, 'pages', 'draggable-page.html')); const promise = once(w.webContents, 'context-menu') as Promise<[any, Electron.ContextMenuParams]>; From c87fd357fc4614e08c51bcb819669f629c62e15c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:01:31 -0500 Subject: [PATCH 022/339] fix: prevent title change for within page navigation (#46036) * fix: prevent title change for on page navigation Co-authored-by: Michaela Laurencin * add back and forward testing Co-authored-by: Michaela Laurencin * update Chromium comment Co-authored-by: Michaela Laurencin * remove errant script tag Co-authored-by: Michaela Laurencin --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michaela Laurencin --- .../browser/api/electron_api_web_contents.cc | 20 ++++++++++-------- spec/api-web-contents-spec.ts | 21 +++++++++++++++++++ .../navigation-history-anchor-in-page.html | 5 +++++ 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 spec/fixtures/pages/navigation-history-anchor-in-page.html diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 53a24ac6f5943..c01e3c5fda2aa 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2057,6 +2057,17 @@ void WebContents::DidFinishNavigation( if (is_main_frame) { Emit("did-navigate", url, http_response_code, http_status_text); } + + content::NavigationEntry* entry = navigation_handle->GetNavigationEntry(); + + // This check is needed due to an issue in Chromium + // Upstream is open to patching: + // https://bugs.chromium.org/p/chromium/issues/detail?id=1178663 + // If a history entry has been made and the forward/back call has been + // made, proceed with setting the new title + if (entry && + (entry->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK)) + WebContents::TitleWasSet(entry); } if (is_guest()) Emit("load-commit", url, is_main_frame); @@ -2077,15 +2088,6 @@ void WebContents::DidFinishNavigation( frame_process_id, frame_routing_id); } } - content::NavigationEntry* entry = navigation_handle->GetNavigationEntry(); - - // This check is needed due to an issue in Chromium - // Check the Chromium issue to keep updated: - // https://bugs.chromium.org/p/chromium/issues/detail?id=1178663 - // If a history entry has been made and the forward/back call has been made, - // proceed with setting the new title - if (entry && (entry->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK)) - WebContents::TitleWasSet(entry); } void WebContents::TitleWasSet(content::NavigationEntry* entry) { diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index 52883991d32c0..53a419d724d94 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -631,6 +631,17 @@ describe('webContents module', () => { w.webContents.navigationHistory.goBack(); expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(0); }); + + it('should have the same window title if navigating back within the page', async () => { + const title = 'Test'; + w.webContents.on('did-finish-load', () => { + w.setTitle(title); + w.loadURL(`file://${fixturesPath}/pages/navigation-history-anchor-in-page.html#next`); + }); + await w.loadURL(`file://${fixturesPath}/pages/navigation-history-anchor-in-page.html`); + w.webContents.navigationHistory.goBack(); + expect(w.getTitle()).to.equal(title); + }); }); describe('navigationHistory.canGoForward and navigationHistory.goForward API', () => { @@ -653,6 +664,16 @@ describe('webContents module', () => { w.webContents.navigationHistory.goForward(); expect(w.webContents.navigationHistory.getActiveIndex()).to.equal(1); }); + + it('should have the same window title if navigating forward within the page', async () => { + const title = 'Test'; + w.webContents.on('did-finish-load', () => { + w.setTitle(title); + w.loadURL(`file://${fixturesPath}/pages/navigation-history-anchor-in-page.html#next`); + }); + await w.loadURL(`file://${fixturesPath}/pages/navigation-history-anchor-in-page.html`); + expect(w.getTitle()).to.equal(title); + }); }); describe('navigationHistory.canGoToOffset(index) and navigationHistory.goToOffset(index) API', () => { diff --git a/spec/fixtures/pages/navigation-history-anchor-in-page.html b/spec/fixtures/pages/navigation-history-anchor-in-page.html new file mode 100644 index 0000000000000..03406b81f2a39 --- /dev/null +++ b/spec/fixtures/pages/navigation-history-anchor-in-page.html @@ -0,0 +1,5 @@ + + + This is content. + + From 585075d776e5ba3b12e2e4d7b8500704d42a53bc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 20:31:08 +0100 Subject: [PATCH 023/339] fix: take Snapped status into account when showing a window (#46041) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/chromium/.patches | 1 + ...s_into_account_when_showing_a_window.patch | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 5a9fe786850ee..2966787e13c29 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -142,3 +142,4 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch fix_linter_error.patch +fix_take_snapped_status_into_account_when_showing_a_window.patch diff --git a/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch b/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch new file mode 100644 index 0000000000000..0ee9e6d3c8b1d --- /dev/null +++ b/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Thu, 13 Mar 2025 10:47:00 +0100 +Subject: fix: take Snapped status into account when showing a window + +Adjusts HWNDMessageHandler::Show to correctly restore windows that were +in a snapped state prior to being hidden or maximized. From Windows +documentation at +https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowarranged: + +> A snapped window (see Snap your windows) is considered to be arranged. +> You should treat arranged as a window state similar to maximized. Arranged, +> maximized, and minimized are mutually exclusive states. + +The logic already took into account a window being maximized and +correctly restored it, but if the window was snapped prior to this CL it +would be removed from its snapped state when re-shown. This fixes that. + +Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/6330848. + +diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc +index 22ed43bdf4dbaccc598135807abc8383c52db50e..c5457e3e58b53ca04697b22a885da654c6c0655f 100644 +--- a/ui/views/win/hwnd_message_handler.cc ++++ b/ui/views/win/hwnd_message_handler.cc +@@ -676,7 +676,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, + SetWindowPlacement(hwnd(), &placement); + native_show_state = SW_SHOWMAXIMIZED; + } else { +- const bool is_maximized = IsMaximized(); ++ const bool is_maximized_or_arranged = ++ IsMaximized() || IsWindowArranged(hwnd()); + + // Use SW_SHOW/SW_SHOWNA instead of SW_SHOWNORMAL/SW_SHOWNOACTIVATE so that + // the window is not restored to its original position if it is maximized. +@@ -686,7 +687,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, + // position, some do not. See crbug.com/1296710 + switch (show_state) { + case ui::mojom::WindowShowState::kInactive: +- native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE; ++ native_show_state = ++ is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE; + break; + case ui::mojom::WindowShowState::kMaximized: + native_show_state = SW_SHOWMAXIMIZED; +@@ -697,9 +699,11 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, + case ui::mojom::WindowShowState::kNormal: + if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) || + (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { +- native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE; ++ native_show_state = ++ is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE; + } else { +- native_show_state = is_maximized ? SW_SHOW : SW_SHOWNORMAL; ++ native_show_state = ++ is_maximized_or_arranged ? SW_SHOW : SW_SHOWNORMAL; + } + break; + case ui::mojom::WindowShowState::kFullscreen: From 4b72738b41a1a91656012a4a47130e6b3e95d070 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:06:04 -0500 Subject: [PATCH 024/339] refactor: remove unused system header includes (#46043) * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: remove unused #include Co-authored-by: Charles Kerr * chore: iwyu Co-authored-by: Charles Kerr * chore: iwyu Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/app/electron_content_client.cc | 1 - shell/app/electron_main_linux.cc | 1 - shell/app/electron_main_win.cc | 1 - shell/browser/api/electron_api_service_worker_main.cc | 1 - shell/browser/api/electron_api_service_worker_main.h | 2 -- shell/browser/api/electron_api_web_contents.h | 2 -- shell/browser/api/gpu_info_enumerator.h | 1 - shell/browser/electron_download_manager_delegate.cc | 1 - shell/browser/electron_web_contents_utility_handler_impl.h | 2 -- .../extensions/api/extension_action/extension_action_api.cc | 1 - .../extensions/api/extension_action/extension_action_api.h | 2 -- shell/browser/extensions/api/scripting/scripting_api.h | 2 +- .../file_system_access/file_system_access_permission_context.h | 1 + shell/browser/javascript_environment.cc | 1 - shell/browser/native_window.h | 1 + shell/browser/net/asar/asar_file_validator.cc | 1 - shell/browser/net/system_network_context_manager.cc | 1 - shell/browser/osr/osr_render_widget_host_view.cc | 1 - shell/browser/relauncher.cc | 2 -- shell/browser/ui/devtools_ui_bundle_data_source.cc | 1 - shell/browser/ui/devtools_ui_theme_data_source.cc | 1 - shell/browser/zoom_level_delegate.cc | 1 - shell/common/process_util.h | 1 - shell/renderer/api/electron_api_context_bridge.cc | 1 - shell/renderer/electron_api_service_impl.cc | 2 -- shell/renderer/electron_render_frame_observer.cc | 2 -- shell/renderer/electron_sandboxed_renderer_client.cc | 1 - .../renderer/extensions/electron_extensions_renderer_client.cc | 2 -- shell/renderer/extensions/electron_extensions_renderer_client.h | 2 -- 29 files changed, 3 insertions(+), 36 deletions(-) diff --git a/shell/app/electron_content_client.cc b/shell/app/electron_content_client.cc index 2748d4df5bedf..b77a0e4d494a0 100644 --- a/shell/app/electron_content_client.cc +++ b/shell/app/electron_content_client.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include "base/command_line.h" diff --git a/shell/app/electron_main_linux.cc b/shell/app/electron_main_linux.cc index d12cd55f7e579..f4f452ee975f5 100644 --- a/shell/app/electron_main_linux.cc +++ b/shell/app/electron_main_linux.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include -#include #include "base/at_exit.h" #include "base/command_line.h" diff --git a/shell/app/electron_main_win.cc b/shell/app/electron_main_win.cc index 4f89cb5a4cf09..da0143a9e63f5 100644 --- a/shell/app/electron_main_win.cc +++ b/shell/app/electron_main_win.cc @@ -14,7 +14,6 @@ #include #include #include -#include #include "base/at_exit.h" #include "base/debug/alias.h" diff --git a/shell/browser/api/electron_api_service_worker_main.cc b/shell/browser/api/electron_api_service_worker_main.cc index 2c397a9e86eeb..4d06cc083a127 100644 --- a/shell/browser/api/electron_api_service_worker_main.cc +++ b/shell/browser/api/electron_api_service_worker_main.cc @@ -7,7 +7,6 @@ #include #include #include -#include #include "base/logging.h" #include "base/no_destructor.h" diff --git a/shell/browser/api/electron_api_service_worker_main.h b/shell/browser/api/electron_api_service_worker_main.h index 995f7ad5428bd..7166c6fac090a 100644 --- a/shell/browser/api/electron_api_service_worker_main.h +++ b/shell/browser/api/electron_api_service_worker_main.h @@ -5,9 +5,7 @@ #ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_ #define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_ -#include #include -#include #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 58389913caa84..d8e88d7fec435 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include "base/functional/callback_forward.h" diff --git a/shell/browser/api/gpu_info_enumerator.h b/shell/browser/api/gpu_info_enumerator.h index 7286cb120337d..c6fb3d5820989 100644 --- a/shell/browser/api/gpu_info_enumerator.h +++ b/shell/browser/api/gpu_info_enumerator.h @@ -5,7 +5,6 @@ #ifndef ELECTRON_SHELL_BROWSER_API_GPU_INFO_ENUMERATOR_H_ #define ELECTRON_SHELL_BROWSER_API_GPU_INFO_ENUMERATOR_H_ -#include #include #include diff --git a/shell/browser/electron_download_manager_delegate.cc b/shell/browser/electron_download_manager_delegate.cc index 2e68b70532474..560bf867790a3 100644 --- a/shell/browser/electron_download_manager_delegate.cc +++ b/shell/browser/electron_download_manager_delegate.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include "base/files/file_util.h" diff --git a/shell/browser/electron_web_contents_utility_handler_impl.h b/shell/browser/electron_web_contents_utility_handler_impl.h index 517c28eaee6e6..78c3ac5936373 100644 --- a/shell/browser/electron_web_contents_utility_handler_impl.h +++ b/shell/browser/electron_web_contents_utility_handler_impl.h @@ -5,8 +5,6 @@ #ifndef ELECTRON_SHELL_BROWSER_ELECTRON_WEB_CONTENTS_UTILITY_HANDLER_IMPL_H_ #define ELECTRON_SHELL_BROWSER_ELECTRON_WEB_CONTENTS_UTILITY_HANDLER_IMPL_H_ -#include - #include "base/memory/weak_ptr.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/web_contents_observer.h" diff --git a/shell/browser/extensions/api/extension_action/extension_action_api.cc b/shell/browser/extensions/api/extension_action/extension_action_api.cc index 0d32060921469..537b0f9d1a7d1 100644 --- a/shell/browser/extensions/api/extension_action/extension_action_api.cc +++ b/shell/browser/extensions/api/extension_action/extension_action_api.cc @@ -6,7 +6,6 @@ #include -#include #include #include "base/no_destructor.h" diff --git a/shell/browser/extensions/api/extension_action/extension_action_api.h b/shell/browser/extensions/api/extension_action/extension_action_api.h index cb8c2634abec8..61f9ecbbabaae 100644 --- a/shell/browser/extensions/api/extension_action/extension_action_api.h +++ b/shell/browser/extensions/api/extension_action/extension_action_api.h @@ -5,8 +5,6 @@ #ifndef SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_ #define SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_ -#include - #include "base/memory/raw_ptr.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_action.h" diff --git a/shell/browser/extensions/api/scripting/scripting_api.h b/shell/browser/extensions/api/scripting/scripting_api.h index fe701a55a712d..7b1f5b452e723 100644 --- a/shell/browser/extensions/api/scripting/scripting_api.h +++ b/shell/browser/extensions/api/scripting/scripting_api.h @@ -7,8 +7,8 @@ #include #include +#include #include -#include #include #include "chrome/common/extensions/api/scripting.h" diff --git a/shell/browser/file_system_access/file_system_access_permission_context.h b/shell/browser/file_system_access/file_system_access_permission_context.h index db60e43210a96..18d55ad477100 100644 --- a/shell/browser/file_system_access/file_system_access_permission_context.h +++ b/shell/browser/file_system_access/file_system_access_permission_context.h @@ -7,6 +7,7 @@ #include "shell/browser/file_system_access/file_system_access_permission_context.h" +#include #include #include #include diff --git a/shell/browser/javascript_environment.cc b/shell/browser/javascript_environment.cc index 1632c31d665c1..e051083ba4538 100644 --- a/shell/browser/javascript_environment.cc +++ b/shell/browser/javascript_environment.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include "base/allocator/partition_alloc_features.h" diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index f02aa559b4449..79d50103d805f 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/shell/browser/net/asar/asar_file_validator.cc b/shell/browser/net/asar/asar_file_validator.cc index f0f1b9981ce8b..0c9ddf44ee21e 100644 --- a/shell/browser/net/asar/asar_file_validator.cc +++ b/shell/browser/net/asar/asar_file_validator.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/shell/browser/net/system_network_context_manager.cc b/shell/browser/net/system_network_context_manager.cc index ef75931d15162..73cea7fb0b461 100644 --- a/shell/browser/net/system_network_context_manager.cc +++ b/shell/browser/net/system_network_context_manager.cc @@ -7,7 +7,6 @@ #include #include #include -#include #include "base/command_line.h" #include "base/memory/raw_ptr.h" diff --git a/shell/browser/osr/osr_render_widget_host_view.cc b/shell/browser/osr/osr_render_widget_host_view.cc index a7bffe3f84f02..483fd94bbd2dd 100644 --- a/shell/browser/osr/osr_render_widget_host_view.cc +++ b/shell/browser/osr/osr_render_widget_host_view.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include "base/functional/callback_helpers.h" #include "base/location.h" diff --git a/shell/browser/relauncher.cc b/shell/browser/relauncher.cc index f2200d817ad30..a7b668254ce01 100644 --- a/shell/browser/relauncher.cc +++ b/shell/browser/relauncher.cc @@ -4,8 +4,6 @@ #include "shell/browser/relauncher.h" -#include - #if BUILDFLAG(IS_WIN) #include #endif diff --git a/shell/browser/ui/devtools_ui_bundle_data_source.cc b/shell/browser/ui/devtools_ui_bundle_data_source.cc index 9f649f98f13f3..2a73bfeb6e5f4 100644 --- a/shell/browser/ui/devtools_ui_bundle_data_source.cc +++ b/shell/browser/ui/devtools_ui_bundle_data_source.cc @@ -4,7 +4,6 @@ #include "shell/browser/ui/devtools_ui_bundle_data_source.h" -#include #include #include diff --git a/shell/browser/ui/devtools_ui_theme_data_source.cc b/shell/browser/ui/devtools_ui_theme_data_source.cc index f1c78c69ffc8d..6715fdf3868eb 100644 --- a/shell/browser/ui/devtools_ui_theme_data_source.cc +++ b/shell/browser/ui/devtools_ui_theme_data_source.cc @@ -4,7 +4,6 @@ #include "shell/browser/ui/devtools_ui_theme_data_source.h" -#include #include #include #include diff --git a/shell/browser/zoom_level_delegate.cc b/shell/browser/zoom_level_delegate.cc index 5b3838bd942db..7dd24f9b51e04 100644 --- a/shell/browser/zoom_level_delegate.cc +++ b/shell/browser/zoom_level_delegate.cc @@ -4,7 +4,6 @@ #include "shell/browser/zoom_level_delegate.h" -#include #include #include diff --git a/shell/common/process_util.h b/shell/common/process_util.h index c9eb759dd6f35..49ec597342037 100644 --- a/shell/common/process_util.h +++ b/shell/common/process_util.h @@ -6,7 +6,6 @@ #define ELECTRON_SHELL_COMMON_PROCESS_UTIL_H_ #include -#include namespace electron { diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 016d377d130b3..772591b521a8b 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/shell/renderer/electron_api_service_impl.cc b/shell/renderer/electron_api_service_impl.cc index bc41997852248..739404631c8e6 100644 --- a/shell/renderer/electron_api_service_impl.cc +++ b/shell/renderer/electron_api_service_impl.cc @@ -4,9 +4,7 @@ #include "electron/shell/renderer/electron_api_service_impl.h" -#include #include -#include #include #include diff --git a/shell/renderer/electron_render_frame_observer.cc b/shell/renderer/electron_render_frame_observer.cc index bf0045ae2d9e1..b735b73b32e7e 100644 --- a/shell/renderer/electron_render_frame_observer.cc +++ b/shell/renderer/electron_render_frame_observer.cc @@ -4,8 +4,6 @@ #include "shell/renderer/electron_render_frame_observer.h" -#include - #include "base/memory/ref_counted_memory.h" #include "base/trace_event/trace_event.h" #include "content/public/renderer/render_frame.h" diff --git a/shell/renderer/electron_sandboxed_renderer_client.cc b/shell/renderer/electron_sandboxed_renderer_client.cc index d98e2b32759f6..2c985c9a59c03 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.cc +++ b/shell/renderer/electron_sandboxed_renderer_client.cc @@ -5,7 +5,6 @@ #include "shell/renderer/electron_sandboxed_renderer_client.h" #include -#include #include #include "base/base_paths.h" diff --git a/shell/renderer/extensions/electron_extensions_renderer_client.cc b/shell/renderer/extensions/electron_extensions_renderer_client.cc index 29ecc149a39a6..cab4a6beb1435 100644 --- a/shell/renderer/extensions/electron_extensions_renderer_client.cc +++ b/shell/renderer/extensions/electron_extensions_renderer_client.cc @@ -4,8 +4,6 @@ #include "shell/renderer/extensions/electron_extensions_renderer_client.h" -#include - #include "content/public/renderer/render_thread.h" #include "extensions/common/constants.h" #include "extensions/common/manifest_handlers/background_info.h" diff --git a/shell/renderer/extensions/electron_extensions_renderer_client.h b/shell/renderer/extensions/electron_extensions_renderer_client.h index 7bd4aedef32bf..2fb3d8f7bba24 100644 --- a/shell/renderer/extensions/electron_extensions_renderer_client.h +++ b/shell/renderer/extensions/electron_extensions_renderer_client.h @@ -5,8 +5,6 @@ #ifndef ELECTRON_SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_RENDERER_CLIENT_H_ #define ELECTRON_SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_RENDERER_CLIENT_H_ -#include - #include "extensions/renderer/extensions_renderer_client.h" namespace content { From c5310fff804bbc6fc95c170b459c743cb9aedc35 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 09:06:12 -0500 Subject: [PATCH 025/339] refactor: replace base::StringPrintf() calls with absl::StrFormat() (#46050) The former is now a pass-through for the latter and is slated for removal Xref: https://issues.chromium.org/issues/40241565 https://chromium-review.googlesource.com/c/chromium/src/+/4907781 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_session.cc | 15 +++++++-------- shell/browser/ui/devtools_ui_theme_data_source.cc | 10 +++++----- shell/renderer/api/electron_api_context_bridge.cc | 10 +++++----- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index c03eb600e458f..22fc75e3bf6cb 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -1067,9 +1067,9 @@ std::string Session::RegisterPreloadScript( }); if (it != preload_scripts.end()) { - thrower.ThrowError(base::StringPrintf( - "Cannot register preload script with existing ID '%s'", - new_preload_script.id.c_str())); + thrower.ThrowError( + absl::StrFormat("Cannot register preload script with existing ID '%s'", + new_preload_script.id)); return ""; } @@ -1080,8 +1080,8 @@ std::string Session::RegisterPreloadScript( << new_preload_script.file_path; } else { thrower.ThrowError( - base::StringPrintf("Preload script must have absolute path: %s", - new_preload_script.file_path.value().c_str())); + absl::StrFormat("Preload script must have absolute path: %s", + new_preload_script.file_path.value())); return ""; } } @@ -1110,9 +1110,8 @@ void Session::UnregisterPreloadScript(gin_helper::ErrorThrower thrower, } // If the script is not found, throw an error - thrower.ThrowError(base::StringPrintf( - "Cannot unregister preload script with non-existing ID '%s'", - script_id.c_str())); + thrower.ThrowError(absl::StrFormat( + "Cannot unregister preload script with non-existing ID '%s'", script_id)); } std::vector Session::GetPreloadScripts() const { diff --git a/shell/browser/ui/devtools_ui_theme_data_source.cc b/shell/browser/ui/devtools_ui_theme_data_source.cc index 6715fdf3868eb..68dcc7b5a355c 100644 --- a/shell/browser/ui/devtools_ui_theme_data_source.cc +++ b/shell/browser/ui/devtools_ui_theme_data_source.cc @@ -22,6 +22,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "net/base/url_util.h" +#include "third_party/abseil-cpp/absl/strings/str_format.h" #include "ui/base/webui/web_ui_util.h" #include "ui/color/color_provider.h" #include "ui/color/color_provider_utils.h" @@ -157,17 +158,16 @@ void ThemeDataSource::SendColorsCss( for (ui::ColorId id = start; id < end; ++id) { const SkColor color = color_provider.GetColor(id); std::string css_id_to_color_mapping = - base::StringPrintf("%s:%s;", color_css_name.Run(id).c_str(), - ui::ConvertSkColorToCSSColor(color).c_str()); + absl::StrFormat("%s:%s;", color_css_name.Run(id), + ui::ConvertSkColorToCSSColor(color)); base::StrAppend(&css_string, {css_id_to_color_mapping}); if (generate_rgb_vars) { // Also generate a r,g,b string for each color so apps can construct // colors with their own opacities in css. const std::string css_rgb_color_str = color_utils::SkColorToRgbString(color); - const std::string css_id_to_rgb_color_mapping = - base::StringPrintf("%s-rgb:%s;", color_css_name.Run(id).c_str(), - css_rgb_color_str.c_str()); + const std::string css_id_to_rgb_color_mapping = absl::StrFormat( + "%s-rgb:%s;", color_css_name.Run(id), css_rgb_color_str); base::StrAppend(&css_string, {css_id_to_rgb_color_mapping}); } } diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 772591b521a8b..119b869c9f250 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -24,6 +24,7 @@ #include "shell/common/node_includes.h" #include "shell/common/world_ids.h" #include "shell/renderer/preload_realm_context.h" +#include "third_party/abseil-cpp/absl/strings/str_format.h" #include "third_party/blink/public/web/web_blob.h" #include "third_party/blink/public/web/web_element.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -944,7 +945,7 @@ v8::Local ExecuteInWorld(v8::Isolate* isolate, if (!maybe_target_context.ToLocal(&target_context)) { isolate->ThrowException(v8::Exception::Error(gin::StringToV8( isolate, - base::StringPrintf("Failed to get context for world %d", world_id)))); + absl::StrFormat("Failed to get context for world %d", world_id)))); return v8::Undefined(isolate); } @@ -956,8 +957,7 @@ v8::Local ExecuteInWorld(v8::Isolate* isolate, v8::MaybeLocal maybe_compiled_script; { v8::TryCatch try_catch(isolate); - std::string return_func_code = - base::StringPrintf("(%s)", function_str.c_str()); + std::string return_func_code = absl::StrFormat("(%s)", function_str); maybe_compiled_script = v8::Script::Compile( target_context, gin::StringToV8(isolate, return_func_code)); if (try_catch.HasCaught()) { @@ -1021,7 +1021,7 @@ v8::Local ExecuteInWorld(v8::Isolate* isolate, v8::Local arg; if (!args_array->Get(source_context, i).ToLocal(&arg)) { gin_helper::ErrorThrower(isolate).ThrowError( - base::StringPrintf("Failed to get argument at index %d", i)); + absl::StrFormat("Failed to get argument at index %d", i)); return v8::Undefined(isolate); } @@ -1031,7 +1031,7 @@ v8::Local ExecuteInWorld(v8::Isolate* isolate, &object_cache); if (proxied_arg.IsEmpty()) { gin_helper::ErrorThrower(isolate).ThrowError( - base::StringPrintf("Failed to proxy argument at index %d", i)); + absl::StrFormat("Failed to proxy argument at index %d", i)); return v8::Undefined(isolate); } proxied_args.push_back(proxied_arg.ToLocalChecked()); From 463e0d0ab75196ee3f229a9da15463096778c694 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 09:50:54 -0500 Subject: [PATCH 026/339] fix: `ElectronBrowserContext` `raw_ptr` bug + remove dead code (#46056) refactor: remove unused ElectronBrowserContext::extension_system() Last use removed on Jul 21, 2020 by 2fb14f5 in PR #24575 This fixes a raw_ptr warning by letting us remove the raw_ptr field `ElectronBrowserContext::extension_system_`. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_context.cc | 11 +++-------- shell/browser/electron_browser_context.h | 22 ---------------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 4ad77d32b217a..eff399c8f8518 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -365,10 +365,10 @@ ElectronBrowserContext::ElectronBrowserContext( BrowserContextDependencyManager::GetInstance() ->CreateBrowserContextServices(this); - extension_system_ = static_cast( + auto* extension_system = static_cast( extensions::ExtensionSystem::Get(this)); - extension_system_->InitForRegularProfile(true /* extensions_enabled */); - extension_system_->FinishInitialization(); + extension_system->InitForRegularProfile(true /* extensions_enabled */); + extension_system->FinishInitialization(); } #endif } @@ -377,11 +377,6 @@ ElectronBrowserContext::~ElectronBrowserContext() { DCHECK_CURRENTLY_ON(BrowserThread::UI); NotifyWillBeDestroyed(); -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - // the DestroyBrowserContextServices() call below frees this. - extension_system_ = nullptr; -#endif - // Notify any keyed services of browser context destruction. BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices( this); diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 3088d98624805..29c2c119655b6 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -13,12 +13,10 @@ #include #include -#include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/media_stream_request.h" #include "content/public/browser/resource_context.h" -#include "electron/buildflags/buildflags.h" #include "electron/shell/browser/media/media_device_id_salt.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/ssl_config.mojom.h" @@ -43,12 +41,6 @@ namespace storage { class SpecialStoragePolicy; } -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -namespace extensions { -class ElectronExtensionSystem; -} -#endif - namespace electron { class ElectronDownloadManagerDelegate; @@ -154,15 +146,6 @@ class ElectronBrowserContext : public content::BrowserContext { return weak_factory_.GetWeakPtr(); } -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - extensions::ElectronExtensionSystem* extension_system() { - // Guard usages of extension_system() with !IsOffTheRecord() - // There is no extension system for in-memory sessions - DCHECK(!IsOffTheRecord()); - return extension_system_; - } -#endif - ProtocolRegistry* protocol_registry() const { return protocol_registry_.get(); } @@ -237,11 +220,6 @@ class ElectronBrowserContext : public content::BrowserContext { bool use_cache_ = true; int max_cache_size_ = 0; -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - // Owned by the KeyedService system. - raw_ptr extension_system_; -#endif - // Shared URLLoaderFactory. scoped_refptr url_loader_factory_; From 3faedae91cc3c62d24c45e6e3a1a288ad184e25c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 13:46:58 -0500 Subject: [PATCH 027/339] perf: avoid redundant map lookup in ElectronBrowserContext::From() (#46062) perf: avoid redundant map lookup in ElectronBrowserContext::FromPath() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_context.cc | 31 ++++++++--------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index eff399c8f8518..fd89254e23b37 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -833,34 +833,23 @@ ElectronBrowserContext* ElectronBrowserContext::From( const std::string& partition, bool in_memory, base::Value::Dict options) { - PartitionKey key(partition, in_memory); - ElectronBrowserContext* browser_context = browser_context_map()[key].get(); - if (browser_context) { - return browser_context; + auto& context = browser_context_map()[PartitionKey(partition, in_memory)]; + if (!context) { + context.reset(new ElectronBrowserContext{std::cref(partition), in_memory, + std::move(options)}); } - - auto* new_context = new ElectronBrowserContext(std::cref(partition), - in_memory, std::move(options)); - browser_context_map()[key] = - std::unique_ptr(new_context); - return new_context; + return context.get(); } ElectronBrowserContext* ElectronBrowserContext::FromPath( const base::FilePath& path, base::Value::Dict options) { - PartitionKey key(path); - - ElectronBrowserContext* browser_context = browser_context_map()[key].get(); - if (browser_context) { - return browser_context; + auto& context = browser_context_map()[PartitionKey(path)]; + if (!context) { + context.reset( + new ElectronBrowserContext{std::cref(path), false, std::move(options)}); } - - auto* new_context = - new ElectronBrowserContext(std::cref(path), false, std::move(options)); - browser_context_map()[key] = - std::unique_ptr(new_context); - return new_context; + return context.get(); } } // namespace electron From 56fced3d0d33667a60a7dc4611a459df86fa3f45 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 11:16:20 +0100 Subject: [PATCH 028/339] build: fix compound bash conditional in patchup (#46058) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/checkout/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index d43907f07c19f..446f06a7bc7be 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -99,7 +99,7 @@ runs: fi ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv - if [ "${{ inputs.is-release }}" != "true" && -n "${{ env.PATCH_UP_APP_CREDS }}" ]; then + if [[ "${{ inputs.is-release }}" != "true" && -n "${{ env.PATCH_UP_APP_CREDS }}" ]]; then # Re-export all the patches to check if there were changes. python3 src/electron/script/export_all_patches.py src/electron/patches/config.json cd src/electron @@ -128,6 +128,8 @@ runs: cat ../../patches/update-patches.patch exit 1 fi + else + echo "No changes to patches detected" fi fi From 9716b6623ec03ea1c52b428d1c127be330e0ab70 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 23:28:18 -0500 Subject: [PATCH 029/339] fix: warning in file picker UI (#46077) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../file_system_access_permission_context.cc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/shell/browser/file_system_access/file_system_access_permission_context.cc b/shell/browser/file_system_access/file_system_access_permission_context.cc index 9f4b43645332b..865fa06f19bac 100644 --- a/shell/browser/file_system_access/file_system_access_permission_context.cc +++ b/shell/browser/file_system_access/file_system_access_permission_context.cc @@ -825,13 +825,7 @@ std::u16string FileSystemAccessPermissionContext::GetPickerTitle( ? IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_WRITABLE_DIRECTORY_TITLE : IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_READABLE_DIRECTORY_TITLE); break; - case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag:: - kSaveFilePickerOptions: - title = l10n_util::GetStringUTF16( - IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_SAVE_FILE_TITLE); - break; - case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag:: - kOpenFilePickerOptions: + default: break; } return title; From bc9389df9a9a039fb1a20fe2d9382ec99e68897d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 19:43:52 +0100 Subject: [PATCH 030/339] refactor: use `GetDefaultStoragePartition()` instead of `GetStoragePartition(nullptr)` (#46083) refactor: use GetDefaultStoragePartition() Use GetDefaultStorageParition() instead of GetStoragePartition(nullptr) - It improves code uniformity, since we use get-default everywhere else - It's more readable - It's marginally faster, since GetStoragePartition() has more steps Added in https://github.com/electron/electron/pull/28907/commits/49b0a1bf4a6c032d1788aac56c1e5eaa0f61e3d3 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_session.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 22fc75e3bf6cb..ffc710b4a0266 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -701,14 +701,13 @@ v8::Local Session::ClearStorageData(gin::Arguments* args) { ClearStorageDataOptions options; args->GetNext(&options); - auto* storage_partition = browser_context()->GetStoragePartition(nullptr); if (options.storage_types & StoragePartition::REMOVE_DATA_MASK_COOKIES) { // Reset media device id salt when cookies are cleared. // https://w3c.github.io/mediacapture-main/#dom-mediadeviceinfo-deviceid MediaDeviceIDSalt::Reset(browser_context()->prefs()); } - storage_partition->ClearData( + browser_context()->GetDefaultStoragePartition()->ClearData( options.storage_types, options.quota_types, options.storage_key, base::Time(), base::Time::Max(), base::BindOnce(gin_helper::Promise::ResolvePromise, @@ -717,8 +716,7 @@ v8::Local Session::ClearStorageData(gin::Arguments* args) { } void Session::FlushStorageData() { - auto* storage_partition = browser_context()->GetStoragePartition(nullptr); - storage_partition->Flush(); + browser_context()->GetDefaultStoragePartition()->Flush(); } v8::Local Session::SetProxy(gin::Arguments* args) { From 7ea2daf45620dcc16fe163632da7f394b2b7ad19 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 16:50:09 -0500 Subject: [PATCH 031/339] refactor: add `ElectronBrowserContext::GetDefaultBrowserContext()` (#46086) * refactor: add ElectronBrowserContext::DestroyAllContexts() Simpler semantics than previous implementation; also hides the "default context must be destroyed last" implementation detail. Co-authored-by: Charles Kerr * refactor: add ElectronBrowserContext::GetDefaultBrowserContext() clearer semantics than everyone calling From("", false) Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_global_shortcut.cc | 2 +- shell/browser/api/electron_api_session.cc | 2 +- shell/browser/electron_browser_context.cc | 14 ++++++++++++++ shell/browser/electron_browser_context.h | 6 ++++++ shell/browser/electron_browser_main_parts.cc | 6 +----- .../electron_extensions_browser_client.cc | 2 +- shell/browser/net/electron_url_loader_factory.cc | 3 +-- shell/browser/ui/devtools_manager_delegate.cc | 2 +- 8 files changed, 26 insertions(+), 11 deletions(-) diff --git a/shell/browser/api/electron_api_global_shortcut.cc b/shell/browser/api/electron_api_global_shortcut.cc index b0aedf148bb17..d78314e50ec75 100644 --- a/shell/browser/api/electron_api_global_shortcut.cc +++ b/shell/browser/api/electron_api_global_shortcut.cc @@ -119,7 +119,7 @@ bool GlobalShortcut::Register(const ui::Accelerator& accelerator, } if (instance->IsRegistrationHandledExternally()) { - auto* context = ElectronBrowserContext::From("", false); + auto* context = ElectronBrowserContext::GetDefaultBrowserContext(); PrefService* prefs = context->prefs(); // Need a unique profile id. Set one if not generated yet, otherwise re-use diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index ffc710b4a0266..06664761261a6 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -1679,7 +1679,7 @@ gin::Handle Session::FromPartition(v8::Isolate* isolate, ElectronBrowserContext* browser_context; if (partition.empty()) { browser_context = - ElectronBrowserContext::From("", false, std::move(options)); + ElectronBrowserContext::GetDefaultBrowserContext(std::move(options)); } else if (partition.starts_with(kPersistPrefix)) { std::string name = partition.substr(8); browser_context = diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index fd89254e23b37..908d23ed5ad2c 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -318,6 +318,14 @@ ElectronBrowserContext::browser_context_map() { return *browser_context_map; } +// static +void ElectronBrowserContext::DestroyAllContexts() { + auto& map = browser_context_map(); + // Avoid UAF by destroying the default context last. See ba629e3 for info. + const auto extracted = map.extract(PartitionKey{"", false}); + map.clear(); +} + ElectronBrowserContext::ElectronBrowserContext( const PartitionOrPath partition_location, bool in_memory, @@ -841,6 +849,12 @@ ElectronBrowserContext* ElectronBrowserContext::From( return context.get(); } +// static +ElectronBrowserContext* ElectronBrowserContext::GetDefaultBrowserContext( + base::Value::Dict options) { + return ElectronBrowserContext::From("", false, std::move(options)); +} + ElectronBrowserContext* ElectronBrowserContext::FromPath( const base::FilePath& path, base::Value::Dict options) { diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 29c2c119655b6..3ae8b2e2f13f6 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -84,6 +84,10 @@ class ElectronBrowserContext : public content::BrowserContext { using BrowserContextMap = std::map>; + // Get or create the default BrowserContext. + static ElectronBrowserContext* GetDefaultBrowserContext( + base::Value::Dict options = {}); + // Get or create the BrowserContext according to its |partition| and // |in_memory|. The |options| will be passed to constructor when there is no // existing BrowserContext. @@ -99,6 +103,8 @@ class ElectronBrowserContext : public content::BrowserContext { static BrowserContextMap& browser_context_map(); + static void DestroyAllContexts(); + void SetUserAgent(const std::string& user_agent); std::string GetUserAgent() const; bool can_use_http_cache() const { return use_cache_; } diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index dfdec38f16096..5e5a8b6ae7652 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -598,11 +598,7 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() { node_bindings_->set_uv_env(nullptr); node_env_.reset(); - auto default_context_key = ElectronBrowserContext::PartitionKey("", false); - std::unique_ptr default_context = std::move( - ElectronBrowserContext::browser_context_map()[default_context_key]); - ElectronBrowserContext::browser_context_map().clear(); - default_context.reset(); + ElectronBrowserContext::DestroyAllContexts(); fake_browser_process_->PostMainMessageLoopRun(); content::DevToolsAgentHost::StopRemoteDebuggingPipeHandler(); diff --git a/shell/browser/extensions/electron_extensions_browser_client.cc b/shell/browser/extensions/electron_extensions_browser_client.cc index 905414fffb201..f74f1a7e61a39 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.cc +++ b/shell/browser/extensions/electron_extensions_browser_client.cc @@ -112,7 +112,7 @@ BrowserContext* ElectronExtensionsBrowserClient::GetOriginalContext( BrowserContext* context) { DCHECK(context); if (context->IsOffTheRecord()) { - return ElectronBrowserContext::From("", false); + return ElectronBrowserContext::GetDefaultBrowserContext(); } else { return context; } diff --git a/shell/browser/net/electron_url_loader_factory.cc b/shell/browser/net/electron_url_loader_factory.cc index d6925d893f2ec..bc8bd61be36fb 100644 --- a/shell/browser/net/electron_url_loader_factory.cc +++ b/shell/browser/net/electron_url_loader_factory.cc @@ -544,8 +544,7 @@ void ElectronURLLoaderFactory::StartLoadingHttp( request->method != net::HttpRequestHeaders::kHeadMethod) dict.Get("uploadData", &upload_data); - ElectronBrowserContext* browser_context = - ElectronBrowserContext::From("", false); + auto* browser_context = ElectronBrowserContext::GetDefaultBrowserContext(); v8::Local value; if (dict.Get("session", &value)) { if (value->IsNull()) { diff --git a/shell/browser/ui/devtools_manager_delegate.cc b/shell/browser/ui/devtools_manager_delegate.cc index bdec61a501c32..2825db7e25268 100644 --- a/shell/browser/ui/devtools_manager_delegate.cc +++ b/shell/browser/ui/devtools_manager_delegate.cc @@ -138,7 +138,7 @@ bool DevToolsManagerDelegate::HasBundledFrontendResources() { } content::BrowserContext* DevToolsManagerDelegate::GetDefaultBrowserContext() { - return ElectronBrowserContext::From("", false); + return ElectronBrowserContext::GetDefaultBrowserContext(); } } // namespace electron From 0ba2441963698c78afafded06d14aa54b71a1ab9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:24:38 +0100 Subject: [PATCH 032/339] docs: fix types of `app.dock` (#46103) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- docs/api/app.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index e7e635e21bd55..988edf22c7ca1 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1559,8 +1559,8 @@ command line arguments that Chromium uses. ### `app.dock` _macOS_ _Readonly_ -A [`Dock`](./dock.md) `| undefined` object that allows you to perform actions on your app icon in the user's -dock on macOS. +A `Dock | undefined` property ([`Dock`](./dock.md) on macOS, `undefined` on all other +platforms) that allows you to perform actions on your app icon in the user's dock. ### `app.isPackaged` _Readonly_ From 859cff24ec32b5ff616e5ceba772f4ececc7c12b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:07:52 -0400 Subject: [PATCH 033/339] build: move set chromium cookie before build tools step (#46095) build: move set cookie before build tools Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: alice --- .github/actions/checkout/action.yml | 4 ++-- .github/workflows/pipeline-segment-node-nan-test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 446f06a7bc7be..b1f003e802b40 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -18,10 +18,10 @@ runs: echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Install Build Tools - uses: ./src/electron/.github/actions/install-build-tools - name: Set Chromium Git Cookie uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools - name: Get Depot Tools shell: bash run: | diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index ce99158af8d03..981ffe39828d9 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -51,6 +51,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -58,8 +60,6 @@ jobs: e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie - name: Get Depot Tools timeout-minutes: 5 run: | From 29ead1bc2561da9a896f917f011cdb7e867b7145 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 16:46:13 -0400 Subject: [PATCH 034/339] test: fix `app.dock` for corrected type (#46115) test: fix app.dock for corrected type Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- spec/api-app-spec.ts | 40 ++++++++++++++++----------------- spec/api-browser-window-spec.ts | 4 ++-- spec/ts-smoke/electron/main.ts | 11 ++++----- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index 1c8e9505c504f..810c94e5382e2 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -1668,19 +1668,19 @@ describe('app module', () => { ifdescribe(process.platform === 'darwin')('dock APIs', () => { after(async () => { - await app.dock.show(); + await app.dock?.show(); }); describe('dock.setMenu', () => { it('can be retrieved via dock.getMenu', () => { - expect(app.dock.getMenu()).to.equal(null); + expect(app.dock?.getMenu()).to.equal(null); const menu = new Menu(); - app.dock.setMenu(menu); - expect(app.dock.getMenu()).to.equal(menu); + app.dock?.setMenu(menu); + expect(app.dock?.getMenu()).to.equal(menu); }); it('keeps references to the menu', () => { - app.dock.setMenu(new Menu()); + app.dock?.setMenu(new Menu()); const v8Util = process._linkedBinding('electron_common_v8_util'); v8Util.requestGarbageCollectionForTesting(); }); @@ -1690,56 +1690,56 @@ describe('app module', () => { it('throws a descriptive error for a bad icon path', () => { const badPath = path.resolve('I', 'Do', 'Not', 'Exist'); expect(() => { - app.dock.setIcon(badPath); + app.dock?.setIcon(badPath); }).to.throw(/Failed to load image from path (.+)/); }); }); describe('dock.bounce', () => { it('should return -1 for unknown bounce type', () => { - expect(app.dock.bounce('bad type' as any)).to.equal(-1); + expect(app.dock?.bounce('bad type' as any)).to.equal(-1); }); it('should return a positive number for informational type', () => { const appHasFocus = !!BrowserWindow.getFocusedWindow(); if (!appHasFocus) { - expect(app.dock.bounce('informational')).to.be.at.least(0); + expect(app.dock?.bounce('informational')).to.be.at.least(0); } }); it('should return a positive number for critical type', () => { const appHasFocus = !!BrowserWindow.getFocusedWindow(); if (!appHasFocus) { - expect(app.dock.bounce('critical')).to.be.at.least(0); + expect(app.dock?.bounce('critical')).to.be.at.least(0); } }); }); describe('dock.cancelBounce', () => { it('should not throw', () => { - app.dock.cancelBounce(app.dock.bounce('critical')); + app.dock?.cancelBounce(app.dock?.bounce('critical')); }); }); describe('dock.setBadge', () => { after(() => { - app.dock.setBadge(''); + app.dock?.setBadge(''); }); it('should not throw', () => { - app.dock.setBadge('1'); + app.dock?.setBadge('1'); }); it('should be retrievable via getBadge', () => { - app.dock.setBadge('test'); - expect(app.dock.getBadge()).to.equal('test'); + app.dock?.setBadge('test'); + expect(app.dock?.getBadge()).to.equal('test'); }); }); describe('dock.hide', () => { it('should not throw', () => { - app.dock.hide(); - expect(app.dock.isVisible()).to.equal(false); + app.dock?.hide(); + expect(app.dock?.isVisible()).to.equal(false); }); }); @@ -1748,17 +1748,17 @@ describe('app module', () => { // See https://github.com/electron/electron/pull/25269 for more. describe('dock.show', () => { it('should not throw', () => { - return app.dock.show().then(() => { - expect(app.dock.isVisible()).to.equal(true); + return app.dock?.show().then(() => { + expect(app.dock?.isVisible()).to.equal(true); }); }); it('returns a Promise', () => { - expect(app.dock.show()).to.be.a('promise'); + expect(app.dock?.show()).to.be.a('promise'); }); it('eventually fulfills', async () => { - await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined); + await expect(app.dock?.show()).to.eventually.be.fulfilled.equal(undefined); }); }); }); diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 66a2188b502a5..a1d90f628e21a 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -2499,12 +2499,12 @@ describe('BrowserWindow module', () => { it('sets the progress', () => { expect(() => { if (process.platform === 'darwin') { - app.dock.setIcon(path.join(fixtures, 'assets', 'logo.png')); + app.dock?.setIcon(path.join(fixtures, 'assets', 'logo.png')); } w.setProgressBar(0.5); if (process.platform === 'darwin') { - app.dock.setIcon(null as any); + app.dock?.setIcon(null as any); } w.setProgressBar(-1); }).to.not.throw(); diff --git a/spec/ts-smoke/electron/main.ts b/spec/ts-smoke/electron/main.ts index 2dbf258aa9113..64fd6232540a2 100644 --- a/spec/ts-smoke/electron/main.ts +++ b/spec/ts-smoke/electron/main.ts @@ -230,11 +230,12 @@ const dockMenu = Menu.buildFromTemplate([ ] } ]); -app.dock.setMenu(dockMenu); -app.dock.setBadge('foo'); -const dockid = app.dock.bounce('informational'); -app.dock.cancelBounce(dockid); -app.dock.setIcon('/path/to/icon.png'); + +app.dock?.setMenu(dockMenu); +app.dock?.setBadge('foo'); +const dockid = app.dock?.bounce('informational'); +app.dock?.cancelBounce(dockid); +app.dock?.setIcon('/path/to/icon.png'); app.setBadgeCount(app.getBadgeCount() + 1); From 1424da9131ce3bb546db1d30eaf72d1b1b97db74 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 18:50:29 -0500 Subject: [PATCH 035/339] docs: use optional chaining for `app.dock` (#46140) docs: use optional chaining for app.dock Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- docs/api/dock.md | 2 +- docs/fiddles/features/macos-dock-menu/main.js | 4 +--- docs/tutorial/macos-dock.md | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/api/dock.md b/docs/api/dock.md index ea6ad93db7857..4224694728da9 100644 --- a/docs/api/dock.md +++ b/docs/api/dock.md @@ -9,7 +9,7 @@ The following example shows how to bounce your icon on the dock. ```js const { app } = require('electron') -app.dock.bounce() +app.dock?.bounce() ``` ### Instance Methods diff --git a/docs/fiddles/features/macos-dock-menu/main.js b/docs/fiddles/features/macos-dock-menu/main.js index 5b8b154fe4a8f..4b9503471bca2 100644 --- a/docs/fiddles/features/macos-dock-menu/main.js +++ b/docs/fiddles/features/macos-dock-menu/main.js @@ -24,9 +24,7 @@ const dockMenu = Menu.buildFromTemplate([ ]) app.whenReady().then(() => { - if (process.platform === 'darwin') { - app.dock.setMenu(dockMenu) - } + app.dock?.setMenu(dockMenu) }).then(createWindow) app.on('window-all-closed', () => { diff --git a/docs/tutorial/macos-dock.md b/docs/tutorial/macos-dock.md index 6df03e3fd1cf1..97b5e5db5b014 100644 --- a/docs/tutorial/macos-dock.md +++ b/docs/tutorial/macos-dock.md @@ -50,9 +50,7 @@ const dockMenu = Menu.buildFromTemplate([ ]) app.whenReady().then(() => { - if (process.platform === 'darwin') { - app.dock.setMenu(dockMenu) - } + app.dock?.setMenu(dockMenu) }).then(createWindow) app.on('window-all-closed', () => { From b0d207e3842b648619403b6a41c842dab5dad3c8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:55:33 +0100 Subject: [PATCH 036/339] fix: `webContents.print()` crash on Linux (#46146) fix: webContents.print() crash on Linux Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/feature_list.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index f063fbef926bd..a4be79d37e984 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -27,6 +27,10 @@ #include "pdf/pdf_features.h" #endif +#if BUILDFLAG(IS_LINUX) +#include "printing/printing_features.h" +#endif + namespace electron { void InitializeFeatureList() { @@ -56,6 +60,14 @@ void InitializeFeatureList() { std::string(",") + features::kMacWebContentsOcclusion.name; #endif +#if BUILDFLAG(IS_LINUX) + disable_features += + // EnableOopPrintDrivers is still a bit half-baked on Linux and + // causes crashes when trying to show dialogs. + // TODO(codebytere): figure out how to re-enable this with our patches. + std::string(",") + printing::features::kEnableOopPrintDrivers.name; +#endif + #if BUILDFLAG(ENABLE_PDF_VIEWER) // Enable window.showSaveFilePicker api for saving pdf files. // Refs https://issues.chromium.org/issues/373852607 From ef22986d1d83ebf44a2912aef8b68c61b08c1353 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:56:43 +0100 Subject: [PATCH 037/339] test: disable `parallel/test-worker-resource-limits` (#46137) test: disable parallel/test-worker-resource-limits Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- script/node-disabled-tests.json | 1 + 1 file changed, 1 insertion(+) diff --git a/script/node-disabled-tests.json b/script/node-disabled-tests.json index 21465e3701814..97852d1ca1021 100644 --- a/script/node-disabled-tests.json +++ b/script/node-disabled-tests.json @@ -153,6 +153,7 @@ "parallel/test-webcrypto-wrap-unwrap", "parallel/test-worker-no-atomics", "parallel/test-worker-no-sab", + "parallel/test-worker-resource-limits", "parallel/test-zlib-unused-weak", "report/test-report-fatalerror-oomerror-compact", "report/test-report-fatalerror-oomerror-directory", From d23aaadcd513d2ecd75ee4cfa1d9807913d469c5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:56:55 +0100 Subject: [PATCH 038/339] build: fail for out of date patches on forks (#46126) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/checkout/action.yml | 2 +- script/push-patch.js | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index b1f003e802b40..2ca2dafe3874d 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -99,7 +99,7 @@ runs: fi ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv - if [[ "${{ inputs.is-release }}" != "true" && -n "${{ env.PATCH_UP_APP_CREDS }}" ]]; then + if [[ "${{ inputs.is-release }}" != "true" ]]; then # Re-export all the patches to check if there were changes. python3 src/electron/script/export_all_patches.py src/electron/patches/config.json cd src/electron diff --git a/script/push-patch.js b/script/push-patch.js index b32f46c3ebafc..7265a10f59618 100644 --- a/script/push-patch.js +++ b/script/push-patch.js @@ -2,23 +2,31 @@ const { appCredentialsFromString, getTokenForRepo } = require('@electron/github- const cp = require('node:child_process'); +const { PATCH_UP_APP_CREDS } = process.env; + async function main () { + if (!PATCH_UP_APP_CREDS) { + throw new Error('PATCH_UP_APP_CREDS environment variable not set'); + } + const token = await getTokenForRepo( { name: 'electron', owner: 'electron' }, - appCredentialsFromString(process.env.PATCH_UP_APP_CREDS) + appCredentialsFromString(PATCH_UP_APP_CREDS) ); + const remoteURL = `https://x-access-token:${token}@github.com/electron/electron.git`; + // NEVER LOG THE OUTPUT OF THIS COMMAND // GIT LEAKS THE ACCESS CREDENTIALS IN CONSOLE LOGS const { status } = cp.spawnSync('git', ['push', '--set-upstream', remoteURL], { stdio: 'ignore' }); + if (status !== 0) { - console.error('Failed to push to target branch'); - process.exit(1); + throw new Error('Failed to push to target branch'); } } From b3526da28e114dac79c4344261f7d58f39e3ffa3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:19:47 -0500 Subject: [PATCH 039/339] refactor: use base::NumberToString() (#46153) base::NumberToString() is slightly more efficient than absl::StrFormat("%u"). Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/serial/serial_chooser_controller.cc | 6 +++--- shell/common/gin_converters/serial_port_info_converter.h | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shell/browser/serial/serial_chooser_controller.cc b/shell/browser/serial/serial_chooser_controller.cc index a871867806554..9ab960a3b9785 100644 --- a/shell/browser/serial/serial_chooser_controller.cc +++ b/shell/browser/serial/serial_chooser_controller.cc @@ -9,6 +9,7 @@ #include "base/containers/contains.h" #include "base/functional/bind.h" +#include "base/strings/string_number_conversions.h" #include "content/public/browser/web_contents.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" @@ -23,7 +24,6 @@ #include "shell/common/gin_converters/content_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/promise.h" -#include "third_party/abseil-cpp/absl/strings/str_format.h" #include "ui/base/l10n/l10n_util.h" namespace gin { @@ -40,10 +40,10 @@ struct Converter { dict.Set("displayName", *port->display_name); } if (port->has_vendor_id) { - dict.Set("vendorId", absl::StrFormat("%u", port->vendor_id)); + dict.Set("vendorId", base::NumberToString(port->vendor_id)); } if (port->has_product_id) { - dict.Set("productId", absl::StrFormat("%u", port->product_id)); + dict.Set("productId", base::NumberToString(port->product_id)); } if (port->serial_number && !port->serial_number->empty()) { dict.Set("serialNumber", *port->serial_number); diff --git a/shell/common/gin_converters/serial_port_info_converter.h b/shell/common/gin_converters/serial_port_info_converter.h index 4bd4587b19c60..b3254e8a300f0 100644 --- a/shell/common/gin_converters/serial_port_info_converter.h +++ b/shell/common/gin_converters/serial_port_info_converter.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_SERIAL_PORT_INFO_CONVERTER_H_ #define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_SERIAL_PORT_INFO_CONVERTER_H_ +#include "base/strings/string_number_conversions.h" #include "gin/converter.h" #include "shell/common/gin_helper/dictionary.h" #include "third_party/blink/public/mojom/serial/serial.mojom.h" @@ -22,9 +23,9 @@ struct Converter { if (port->display_name && !port->display_name->empty()) dict.Set("displayName", *port->display_name); if (port->has_vendor_id) - dict.Set("vendorId", absl::StrFormat("%u", port->vendor_id)); + dict.Set("vendorId", base::NumberToString(port->vendor_id)); if (port->has_product_id) - dict.Set("productId", absl::StrFormat("%u", port->product_id)); + dict.Set("productId", base::NumberToString(port->product_id)); if (port->serial_number && !port->serial_number->empty()) dict.Set("serialNumber", *port->serial_number); #if BUILDFLAG(IS_MAC) From cee2c2ceeb604fd2f5c352d39ca6b00e606495c4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 15:42:12 -0500 Subject: [PATCH 040/339] refactor: Add `ElectronBrowserContext::BrowserContexts()` (#46158) * refactor: add ElectronBrowserContext::BrowserContexts() Co-authored-by: Charles Kerr * refactor: use ElectronBrowserContext::BrowserContexts() in ElectronBrowserMainParts::PostMainMessageLoopRun() Co-authored-by: Charles Kerr * refactor: use ElectronBrowserContext::BrowserContexts() in ElectronExtensionsBrowserClient::IsValidContext() Co-authored-by: Charles Kerr * refactor: use ElectronBrowserContext::BrowserContexts() in ElectronExtensionsBrowserClient::BroadcastEventToRenderers() Co-authored-by: Charles Kerr * refactor: move PartitionKey, BrowserContextMap private Co-authored-by: Charles Kerr * refactor: add ElectronBrowserContext::IsValidContext() decouple ElectronExtensionsBrowserClient from the internals of ElectronBrowserContext Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_context.cc | 48 +++++++++++++++---- shell/browser/electron_browser_context.h | 25 +--------- shell/browser/electron_browser_main_parts.cc | 6 +-- .../electron_extensions_browser_client.cc | 18 ++----- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 908d23ed5ad2c..24eba93bd7b5d 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -12,6 +12,7 @@ #include "base/barrier_closure.h" #include "base/base_paths.h" #include "base/command_line.h" +#include "base/containers/to_vector.h" #include "base/files/file_path.h" #include "base/no_destructor.h" #include "base/path_service.h" @@ -308,19 +309,50 @@ bool DoesDeviceMatch(const base::Value& device, return false; } +// partition_id => browser_context +struct PartitionKey { + PartitionKey(const std::string_view partition, bool in_memory) + : type_{Type::Partition}, location_{partition}, in_memory_{in_memory} {} + + explicit PartitionKey(const base::FilePath& file_path) + : type_{Type::Path}, + location_{file_path.AsUTF8Unsafe()}, + in_memory_{false} {} + + friend auto operator<=>(const PartitionKey&, const PartitionKey&) = default; + + private: + enum class Type { Partition, Path }; + + Type type_; + std::string location_; + bool in_memory_; +}; + +[[nodiscard]] auto& ContextMap() { + static base::NoDestructor< + std::map>> + map; + return *map; +} + } // namespace // static -ElectronBrowserContext::BrowserContextMap& -ElectronBrowserContext::browser_context_map() { - static base::NoDestructor - browser_context_map; - return *browser_context_map; +std::vector ElectronBrowserContext::BrowserContexts() { + return base::ToVector(ContextMap(), + [](auto& iter) { return iter.second.get(); }); +} + +bool ElectronBrowserContext::IsValidContext(const void* context) { + return std::ranges::any_of(ContextMap(), [context](const auto& iter) { + return iter.second.get() == context; + }); } // static void ElectronBrowserContext::DestroyAllContexts() { - auto& map = browser_context_map(); + auto& map = ContextMap(); // Avoid UAF by destroying the default context last. See ba629e3 for info. const auto extracted = map.extract(PartitionKey{"", false}); map.clear(); @@ -841,7 +873,7 @@ ElectronBrowserContext* ElectronBrowserContext::From( const std::string& partition, bool in_memory, base::Value::Dict options) { - auto& context = browser_context_map()[PartitionKey(partition, in_memory)]; + auto& context = ContextMap()[PartitionKey(partition, in_memory)]; if (!context) { context.reset(new ElectronBrowserContext{std::cref(partition), in_memory, std::move(options)}); @@ -858,7 +890,7 @@ ElectronBrowserContext* ElectronBrowserContext::GetDefaultBrowserContext( ElectronBrowserContext* ElectronBrowserContext::FromPath( const base::FilePath& path, base::Value::Dict options) { - auto& context = browser_context_map()[PartitionKey(path)]; + auto& context = ContextMap()[PartitionKey(path)]; if (!context) { context.reset( new ElectronBrowserContext{std::cref(path), false, std::move(options)}); diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 3ae8b2e2f13f6..2985861825117 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -61,28 +61,9 @@ class ElectronBrowserContext : public content::BrowserContext { ElectronBrowserContext(const ElectronBrowserContext&) = delete; ElectronBrowserContext& operator=(const ElectronBrowserContext&) = delete; - // partition_id => browser_context - struct PartitionKey { - PartitionKey(const std::string_view partition, bool in_memory) - : type_{Type::Partition}, location_{partition}, in_memory_{in_memory} {} + [[nodiscard]] static std::vector BrowserContexts(); - explicit PartitionKey(const base::FilePath& file_path) - : type_{Type::Path}, - location_{file_path.AsUTF8Unsafe()}, - in_memory_{false} {} - - friend auto operator<=>(const PartitionKey&, const PartitionKey&) = default; - - private: - enum class Type { Partition, Path }; - - Type type_; - std::string location_; - bool in_memory_; - }; - - using BrowserContextMap = - std::map>; + [[nodiscard]] static bool IsValidContext(const void* context); // Get or create the default BrowserContext. static ElectronBrowserContext* GetDefaultBrowserContext( @@ -101,8 +82,6 @@ class ElectronBrowserContext : public content::BrowserContext { static ElectronBrowserContext* FromPath(const base::FilePath& path, base::Value::Dict options = {}); - static BrowserContextMap& browser_context_map(); - static void DestroyAllContexts(); void SetUserAgent(const std::string& user_agent); diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc index 5e5a8b6ae7652..1603230fbb623 100644 --- a/shell/browser/electron_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -554,11 +554,9 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() { // Shutdown the DownloadManager before destroying Node to prevent // DownloadItem callbacks from crashing. - for (auto& iter : ElectronBrowserContext::browser_context_map()) { - auto* download_manager = iter.second.get()->GetDownloadManager(); - if (download_manager) { + for (auto* browser_context : ElectronBrowserContext::BrowserContexts()) { + if (auto* download_manager = browser_context->GetDownloadManager()) download_manager->Shutdown(); - } } // Shutdown utility process created with Electron API before diff --git a/shell/browser/extensions/electron_extensions_browser_client.cc b/shell/browser/extensions/electron_extensions_browser_client.cc index f74f1a7e61a39..1ada759c73d9a 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.cc +++ b/shell/browser/extensions/electron_extensions_browser_client.cc @@ -84,12 +84,7 @@ bool ElectronExtensionsBrowserClient::AreExtensionsDisabled( } bool ElectronExtensionsBrowserClient::IsValidContext(void* context) { - auto& context_map = ElectronBrowserContext::browser_context_map(); - for (auto const& entry : context_map) { - if (entry.second && entry.second.get() == context) - return true; - } - return false; + return ElectronBrowserContext::IsValidContext(context); } bool ElectronExtensionsBrowserClient::IsSameContext(BrowserContext* first, @@ -340,13 +335,10 @@ void ElectronExtensionsBrowserClient::BroadcastEventToRenderers( return; } - for (auto const& [key, browser_context] : - ElectronBrowserContext::browser_context_map()) { - if (browser_context) { - extensions::EventRouter::Get(browser_context.get()) - ->BroadcastEvent(std::make_unique( - histogram_value, event_name, args.Clone())); - } + for (auto* browser_context : ElectronBrowserContext::BrowserContexts()) { + extensions::EventRouter::Get(browser_context) + ->BroadcastEvent(std::make_unique( + histogram_value, event_name, args.Clone())); } } From 1ba56c8696bc63d6f78ba35290346c67eae57bd9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:52:23 -0500 Subject: [PATCH 041/339] fix: APNS token ids are lowercase ASCII (#46149) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/push-notifications.md | 3 +++ .../api/electron_api_push_notifications_mac.mm | 11 +++-------- shell/browser/mac/electron_application_delegate.mm | 4 +++- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/api/push-notifications.md b/docs/api/push-notifications.md index 01c0e830e5b87..099689d927300 100644 --- a/docs/api/push-notifications.md +++ b/docs/api/push-notifications.md @@ -46,4 +46,7 @@ See: https://developer.apple.com/documentation/appkit/nsapplication/1428476-regi ### `pushNotifications.unregisterForAPNSNotifications()` _macOS_ Unregisters the app from notifications received from APNS. + +Apps unregistered through this method can always reregister. + See: https://developer.apple.com/documentation/appkit/nsapplication/1428747-unregisterforremotenotifications?language=objc diff --git a/shell/browser/api/electron_api_push_notifications_mac.mm b/shell/browser/api/electron_api_push_notifications_mac.mm index b9f3de17441a7..ede147f28b4ef 100644 --- a/shell/browser/api/electron_api_push_notifications_mac.mm +++ b/shell/browser/api/electron_api_push_notifications_mac.mm @@ -19,10 +19,7 @@ gin_helper::Promise promise(isolate); v8::Local handle = promise.GetHandle(); - [[AtomApplication sharedApplication] - registerForRemoteNotificationTypes:NSRemoteNotificationTypeBadge | - NSRemoteNotificationTypeAlert | - NSRemoteNotificationTypeSound]; + [[AtomApplication sharedApplication] registerForRemoteNotifications]; PushNotifications::apns_promise_set_.emplace_back(std::move(promise)); return handle; @@ -30,8 +27,7 @@ void PushNotifications::ResolveAPNSPromiseSetWithToken( const std::string& token_string) { - std::vector> promises = - std::move(PushNotifications::apns_promise_set_); + auto promises = std::move(PushNotifications::apns_promise_set_); for (auto& promise : promises) { promise.Resolve(token_string); } @@ -39,8 +35,7 @@ void PushNotifications::RejectAPNSPromiseSetWithError( const std::string& error_message) { - std::vector> promises = - std::move(PushNotifications::apns_promise_set_); + auto promises = std::move(PushNotifications::apns_promise_set_); for (auto& promise : promises) { promise.RejectWithErrorMessage(error_message); } diff --git a/shell/browser/mac/electron_application_delegate.mm b/shell/browser/mac/electron_application_delegate.mm index 007cff291e1be..72774bcd67831 100644 --- a/shell/browser/mac/electron_application_delegate.mm +++ b/shell/browser/mac/electron_application_delegate.mm @@ -180,8 +180,10 @@ - (void)application:(NSApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { // Resolve outstanding APNS promises created during registration attempts if (auto* push_notifications = electron::api::PushNotifications::Get()) { + std::string encoded = + base::HexEncode(electron::util::as_byte_span(deviceToken)); push_notifications->ResolveAPNSPromiseSetWithToken( - base::HexEncode(electron::util::as_byte_span(deviceToken))); + base::ToLowerASCII(encoded)); } } From b7a28f31d8a1f80c9bba091c50b9b6a8840a6a74 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 23:02:16 -0500 Subject: [PATCH 042/339] refactor: make URLPipeLoader private (#46166) Move the URLPipeLoader class into an anonymous namespace in electron_url_loader_factory.cc. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- filenames.gni | 2 - .../net/electron_url_loader_factory.cc | 129 +++++++++++++++++- shell/browser/net/url_pipe_loader.cc | 104 -------------- shell/browser/net/url_pipe_loader.h | 90 ------------ 4 files changed, 128 insertions(+), 197 deletions(-) delete mode 100644 shell/browser/net/url_pipe_loader.cc delete mode 100644 shell/browser/net/url_pipe_loader.h diff --git a/filenames.gni b/filenames.gni index 7bd470eec7420..0d58b9e147ffd 100644 --- a/filenames.gni +++ b/filenames.gni @@ -453,8 +453,6 @@ filenames = { "shell/browser/net/system_network_context_manager.h", "shell/browser/net/url_loader_network_observer.cc", "shell/browser/net/url_loader_network_observer.h", - "shell/browser/net/url_pipe_loader.cc", - "shell/browser/net/url_pipe_loader.h", "shell/browser/net/web_request_api_interface.h", "shell/browser/network_hints_handler_impl.cc", "shell/browser/network_hints_handler_impl.h", diff --git a/shell/browser/net/electron_url_loader_factory.cc b/shell/browser/net/electron_url_loader_factory.cc index bc8bd61be36fb..1f06048ed3133 100644 --- a/shell/browser/net/electron_url_loader_factory.cc +++ b/shell/browser/net/electron_url_loader_factory.cc @@ -8,25 +8,36 @@ #include #include #include +#include #include "base/containers/fixed_flat_map.h" #include "base/strings/string_number_conversions.h" +#include "base/task/sequenced_task_runner.h" #include "base/uuid.h" +#include "base/values.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe_producer.h" #include "mojo/public/cpp/system/string_data_source.h" #include "net/base/filename_util.h" #include "net/http/http_request_headers.h" #include "net/http/http_status_code.h" #include "net/url_request/redirect_util.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" #include "services/network/public/cpp/url_loader_completion_status.h" +#include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" #include "shell/browser/api/electron_api_session.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/net/asar/asar_url_loader.h" #include "shell/browser/net/node_stream_loader.h" -#include "shell/browser/net/url_pipe_loader.h" #include "shell/common/electron_constants.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/gurl_converter.h" @@ -179,6 +190,122 @@ void OnWrite(std::unique_ptr write_data, MojoResult result) { write_data->client->OnComplete(status); } +// Read data from URL and pipe it to NetworkService. +// +// Different from creating a new loader for the URL directly, protocol handlers +// using this loader can work around CORS restrictions. +// +// This class manages its own lifetime and should delete itself when the +// connection is lost or finished. +class URLPipeLoader : public network::mojom::URLLoader, + public network::SimpleURLLoaderStreamConsumer { + public: + URLPipeLoader(scoped_refptr factory, + std::unique_ptr request, + mojo::PendingReceiver loader, + mojo::PendingRemote client, + const net::NetworkTrafficAnnotationTag& annotation, + base::Value::Dict upload_data) + : url_loader_(this, std::move(loader)), client_(std::move(client)) { + url_loader_.set_disconnect_handler( + base::BindOnce(&URLPipeLoader::NotifyComplete, base::Unretained(this), + net::ERR_FAILED)); + + // PostTask since it might destruct. + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, + base::BindOnce(&URLPipeLoader::Start, weak_factory_.GetWeakPtr(), + factory, std::move(request), annotation, + std::move(upload_data))); + } + + // disable copy + URLPipeLoader(const URLPipeLoader&) = delete; + URLPipeLoader& operator=(const URLPipeLoader&) = delete; + + private: + ~URLPipeLoader() override = default; + + void Start(scoped_refptr factory, + std::unique_ptr request, + const net::NetworkTrafficAnnotationTag& annotation, + base::Value::Dict upload_data) { + loader_ = network::SimpleURLLoader::Create(std::move(request), annotation); + loader_->SetOnResponseStartedCallback(base::BindOnce( + &URLPipeLoader::OnResponseStarted, weak_factory_.GetWeakPtr())); + + // TODO(zcbenz): The old protocol API only supports string as upload data, + // we should seek to support more types in future. + std::string* content_type = upload_data.FindString("contentType"); + std::string* data = upload_data.FindString("data"); + if (content_type && data) + loader_->AttachStringForUpload(*data, *content_type); + + loader_->DownloadAsStream(factory.get(), this); + } + + void NotifyComplete(int result) { + client_->OnComplete(network::URLLoaderCompletionStatus(result)); + delete this; + } + + void OnResponseStarted(const GURL& final_url, + const network::mojom::URLResponseHead& response_head) { + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + MojoResult rv = mojo::CreateDataPipe(nullptr, producer, consumer); + if (rv != MOJO_RESULT_OK) { + NotifyComplete(net::ERR_INSUFFICIENT_RESOURCES); + return; + } + + producer_ = std::make_unique(std::move(producer)); + + client_->OnReceiveResponse(response_head.Clone(), std::move(consumer), + std::nullopt); + } + + void OnWrite(base::OnceClosure resume, MojoResult result) { + if (result == MOJO_RESULT_OK) + std::move(resume).Run(); + else + NotifyComplete(net::ERR_FAILED); + } + + // SimpleURLLoaderStreamConsumer: + void OnDataReceived(std::string_view string_view, + base::OnceClosure resume) override { + producer_->Write( + std::make_unique( + string_view, mojo::StringDataSource::AsyncWritingMode:: + STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION), + base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(), + std::move(resume))); + } + + void OnComplete(bool success) override { + NotifyComplete(loader_->NetError()); + } + void OnRetry(base::OnceClosure start_retry) override { NOTREACHED(); } + + // URLLoader: + void FollowRedirect( + const std::vector& removed_headers, + const net::HttpRequestHeaders& modified_headers, + const net::HttpRequestHeaders& modified_cors_exempt_headers, + const std::optional& new_url) override {} + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override {} + + mojo::Receiver url_loader_; + mojo::Remote client_; + + std::unique_ptr producer_; + std::unique_ptr loader_; + + base::WeakPtrFactory weak_factory_{this}; +}; + } // namespace ElectronURLLoaderFactory::RedirectedRequest::RedirectedRequest( diff --git a/shell/browser/net/url_pipe_loader.cc b/shell/browser/net/url_pipe_loader.cc deleted file mode 100644 index ab62d37c85e20..0000000000000 --- a/shell/browser/net/url_pipe_loader.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/browser/net/url_pipe_loader.h" - -#include -#include - -#include "base/task/sequenced_task_runner.h" -#include "mojo/public/cpp/bindings/pending_remote.h" -#include "mojo/public/cpp/system/string_data_source.h" -#include "services/network/public/cpp/shared_url_loader_factory.h" -#include "services/network/public/mojom/url_response_head.mojom.h" - -namespace electron { - -URLPipeLoader::URLPipeLoader( - scoped_refptr factory, - std::unique_ptr request, - mojo::PendingReceiver loader, - mojo::PendingRemote client, - const net::NetworkTrafficAnnotationTag& annotation, - base::Value::Dict upload_data) - : url_loader_(this, std::move(loader)), client_(std::move(client)) { - url_loader_.set_disconnect_handler(base::BindOnce( - &URLPipeLoader::NotifyComplete, base::Unretained(this), net::ERR_FAILED)); - - // PostTask since it might destruct. - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, - base::BindOnce(&URLPipeLoader::Start, weak_factory_.GetWeakPtr(), factory, - std::move(request), annotation, std::move(upload_data))); -} - -URLPipeLoader::~URLPipeLoader() = default; - -void URLPipeLoader::Start( - scoped_refptr factory, - std::unique_ptr request, - const net::NetworkTrafficAnnotationTag& annotation, - base::Value::Dict upload_data) { - loader_ = network::SimpleURLLoader::Create(std::move(request), annotation); - loader_->SetOnResponseStartedCallback(base::BindOnce( - &URLPipeLoader::OnResponseStarted, weak_factory_.GetWeakPtr())); - - // TODO(zcbenz): The old protocol API only supports string as upload data, - // we should seek to support more types in future. - std::string* content_type = upload_data.FindString("contentType"); - std::string* data = upload_data.FindString("data"); - if (content_type && data) - loader_->AttachStringForUpload(*data, *content_type); - - loader_->DownloadAsStream(factory.get(), this); -} - -void URLPipeLoader::NotifyComplete(int result) { - client_->OnComplete(network::URLLoaderCompletionStatus(result)); - delete this; -} - -void URLPipeLoader::OnResponseStarted( - const GURL& final_url, - const network::mojom::URLResponseHead& response_head) { - mojo::ScopedDataPipeProducerHandle producer; - mojo::ScopedDataPipeConsumerHandle consumer; - MojoResult rv = mojo::CreateDataPipe(nullptr, producer, consumer); - if (rv != MOJO_RESULT_OK) { - NotifyComplete(net::ERR_INSUFFICIENT_RESOURCES); - return; - } - - producer_ = std::make_unique(std::move(producer)); - - client_->OnReceiveResponse(response_head.Clone(), std::move(consumer), - std::nullopt); -} - -void URLPipeLoader::OnWrite(base::OnceClosure resume, MojoResult result) { - if (result == MOJO_RESULT_OK) - std::move(resume).Run(); - else - NotifyComplete(net::ERR_FAILED); -} - -void URLPipeLoader::OnDataReceived(std::string_view string_view, - base::OnceClosure resume) { - producer_->Write( - std::make_unique( - string_view, mojo::StringDataSource::AsyncWritingMode:: - STRING_MAY_BE_INVALIDATED_BEFORE_COMPLETION), - base::BindOnce(&URLPipeLoader::OnWrite, weak_factory_.GetWeakPtr(), - std::move(resume))); -} - -void URLPipeLoader::OnRetry(base::OnceClosure start_retry) { - NOTREACHED(); -} - -void URLPipeLoader::OnComplete(bool success) { - NotifyComplete(loader_->NetError()); -} - -} // namespace electron diff --git a/shell/browser/net/url_pipe_loader.h b/shell/browser/net/url_pipe_loader.h deleted file mode 100644 index e2f5fe2da4b7e..0000000000000 --- a/shell/browser/net/url_pipe_loader.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ELECTRON_SHELL_BROWSER_NET_URL_PIPE_LOADER_H_ -#define ELECTRON_SHELL_BROWSER_NET_URL_PIPE_LOADER_H_ - -#include -#include -#include -#include - -#include "base/values.h" -#include "mojo/public/cpp/bindings/receiver.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "services/network/public/cpp/resource_request.h" -#include "services/network/public/cpp/simple_url_loader.h" -#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" -#include "services/network/public/mojom/url_loader.mojom.h" - -namespace mojo { -class DataPipeProducer; -} - -namespace network { -class SharedURLLoaderFactory; -} - -namespace electron { - -// Read data from URL and pipe it to NetworkService. -// -// Different from creating a new loader for the URL directly, protocol handlers -// using this loader can work around CORS restrictions. -// -// This class manages its own lifetime and should delete itself when the -// connection is lost or finished. -class URLPipeLoader : public network::mojom::URLLoader, - public network::SimpleURLLoaderStreamConsumer { - public: - URLPipeLoader(scoped_refptr factory, - std::unique_ptr request, - mojo::PendingReceiver loader, - mojo::PendingRemote client, - const net::NetworkTrafficAnnotationTag& annotation, - base::Value::Dict upload_data); - - // disable copy - URLPipeLoader(const URLPipeLoader&) = delete; - URLPipeLoader& operator=(const URLPipeLoader&) = delete; - - private: - ~URLPipeLoader() override; - - void Start(scoped_refptr factory, - std::unique_ptr request, - const net::NetworkTrafficAnnotationTag& annotation, - base::Value::Dict upload_data); - void NotifyComplete(int result); - void OnResponseStarted(const GURL& final_url, - const network::mojom::URLResponseHead& response_head); - void OnWrite(base::OnceClosure resume, MojoResult result); - - // SimpleURLLoaderStreamConsumer: - void OnDataReceived(std::string_view string_view, - base::OnceClosure resume) override; - void OnComplete(bool success) override; - void OnRetry(base::OnceClosure start_retry) override; - - // URLLoader: - void FollowRedirect( - const std::vector& removed_headers, - const net::HttpRequestHeaders& modified_headers, - const net::HttpRequestHeaders& modified_cors_exempt_headers, - const std::optional& new_url) override {} - void SetPriority(net::RequestPriority priority, - int32_t intra_priority_value) override {} - - mojo::Receiver url_loader_; - mojo::Remote client_; - - std::unique_ptr producer_; - std::unique_ptr loader_; - - base::WeakPtrFactory weak_factory_{this}; -}; - -} // namespace electron - -#endif // ELECTRON_SHELL_BROWSER_NET_URL_PIPE_LOADER_H_ From 3772e267c3594682d0668ff28b3ed1a4d044a270 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:21:08 -0400 Subject: [PATCH 043/339] chore: bump chromium to 136.0.7067.0 (36-x-y) (#46017) * chore: bump chromium in DEPS to 136.0.7066.1 * chore: bump chromium in DEPS to 136.0.7067.0 * chore: bump chromium in DEPS to 136.0.7067.3 * chore: bump chromium in DEPS to 136.0.7069.1 * chore: bump chromium in DEPS to 136.0.7071.0 * chore: bump chromium in DEPS to 136.0.7073.1 * chore: bump chromium in DEPS to 136.0.7075.1 * chore: bump chromium in DEPS to 136.0.7076.1 * chore: bump chromium in DEPS to 136.0.7077.1 * chore: bump chromium to 136.0.7064.0 (main) (#45985) * chore: bump chromium in DEPS to 136.0.7063.0 * chore: bump chromium in DEPS to 136.0.7064.0 * 6169919: Instantiate Linux (or CrOS) system fonts using Fontations Refs https://chromium-review.googlesource.com/c/chromium/src/+/6169919 * 6341209: Remove file-wide unsafe buffer suppression from content/ [1 of N]. Refs https://chromium-review.googlesource.com/c/chromium/src/+/6341209 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: David Sanders (cherry picked from commit 962d8b325aa2ace6a72f0606ddc2e1ac65ee0907) * chore: bump chromium to 136.0.7067.0 (main) (#46018) * chore: bump chromium in DEPS to 136.0.7066.0 * chore: bump chromium in DEPS to 136.0.7067.0 * 6325710: [LNA] Add Local Network Access permission type Refs https://chromium-review.googlesource.com/c/chromium/src/+/6325710 * 6342514: Create frame mojo endpoints in renderer during window.open() Refs https://chromium-review.googlesource.com/c/chromium/src/+/6342514 * 6344040: Create widget mojo endpoints in renderer process for window.open() Refs https://chromium-review.googlesource.com/c/chromium/src/+/6344040 * chore: update patches * 6349218: Move ExtensionService::install_directory() to ExtensionRegistrar Refs https://chromium-review.googlesource.com/c/chromium/src/+/6349218 * 6349395: Move ExtensionService::extensions_enabled() to ExtensionRegistrar Refs https://chromium-review.googlesource.com/c/chromium/src/+/6349395 * 6331510: Migrate views::Background class to ui::ColorVariant | https://chromium-review.googlesource.com/c/chromium/src/+/6331510 * build: reorder set-cookie step https://github.com/electron/electron/pull/46091 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: David Sanders Co-authored-by: alice (cherry picked from commit b13f05e2dc355a4d2bcfb0823ccf3b89e39350b3) * chore: update patch --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- ...ack_ssl_error_zero_return_explicitly.patch | 6 ++-- .../add_didinstallconditionalfeatures.patch | 4 +-- ...lectron_deps_to_license_credits_file.patch | 2 +- patches/chromium/blink_local_frame.patch | 6 ++-- .../build_add_electron_tracing_category.patch | 2 +- ..._depend_on_packed_resource_integrity.patch | 12 +++---- patches/chromium/build_gn.patch | 2 +- patches/chromium/can_create_window.patch | 30 ++++++++-------- ...fy_chromium_handling_of_mouse_events.patch | 8 ++--- .../chromium/chore_partial_revert_of.patch | 2 +- ...screationoverridden_with_full_params.patch | 6 ++-- patches/chromium/disable_hidden.patch | 6 ++-- .../chromium/enable_reset_aspect_ratio.patch | 4 +-- ...xpose_setuseragent_on_networkcontext.patch | 6 ++-- ...dd_set_theme_source_to_allow_apps_to.patch | 4 +-- ...t_allow_code_cache_in_custom_schemes.patch | 12 +++---- ...screen_rendering_with_viz_compositor.patch | 10 +++--- ..._raw_response_headers_from_urlloader.patch | 24 ++++++------- ...allback_for_sync_and_async_clipboard.patch | 36 +++++++++---------- ...ivate_background_material_on_windows.patch | 6 ++-- .../fix_aspect_ratio_with_max_size.patch | 4 +-- ...ding_non-standard_schemes_in_iframes.patch | 4 +-- ...board_hides_on_input_blur_in_webview.patch | 4 +-- ...x_remove_caption-removing_style_call.patch | 4 +-- ...original_resize_performance_on_macos.patch | 2 +- ...from_localframe_requestexecutescript.patch | 10 +++--- ...t_menu_item_when_opened_via_keyboard.patch | 4 +-- ...s_into_account_when_showing_a_window.patch | 8 ++--- patches/chromium/frame_host_manager.patch | 2 +- .../chromium/gritsettings_resource_ids.patch | 4 +-- ...key_appusermodel_toastactivatorclsid.patch | 2 +- ..._avoid_private_macos_api_usage.patch.patch | 24 ++++++------- ...emote_certificate_verification_logic.patch | 8 ++--- .../chromium/notification_provenance.patch | 2 +- ...eated_to_allow_for_browser_initiated.patch | 2 +- patches/chromium/printing.patch | 4 +-- ...r_changes_to_the_webcontentsobserver.patch | 4 +-- ..._expose_file_system_access_blocklist.patch | 2 +- ...efactor_unfilter_unresponsive_events.patch | 4 +-- patches/chromium/scroll_bounce_flag.patch | 2 +- .../support_mixed_sandbox_with_zygote.patch | 2 +- patches/chromium/web_contents.patch | 2 +- patches/chromium/webview_fullscreen.patch | 8 ++--- .../worker_context_will_destroy.patch | 8 ++--- ...feat_add_hook_to_notify_script_ready.patch | 8 ++--- ...i_to_allow_electron_to_set_dock_side.patch | 4 +-- ...8_object_setinternalfieldfornodecore.patch | 4 +-- ...ated_attachcppheap_and_detachcppheap.patch | 16 ++++----- .../extensions/electron_extension_loader.cc | 7 +++- shell/browser/native_window_views.cc | 2 +- shell/browser/ui/views/menu_bar.cc | 6 ++-- .../gin_converters/content_converter.cc | 2 ++ spec/chromium-spec.ts | 2 +- 54 files changed, 185 insertions(+), 176 deletions(-) diff --git a/DEPS b/DEPS index 0b5fee7db3bd0..0316ae3e71b79 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7062.0', + '136.0.7067.0', 'node_version': 'v22.14.0', 'nan_version': diff --git a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch index e88ef421922c4..9b355b6f09620 100644 --- a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch +++ b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch @@ -20,10 +20,10 @@ index 2cdcbc346175eeee69402ecee7f169e61c655199..f7226fe711e4214b216ea2c5173a0212 case ssl_open_record_error: diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index c0c65798b61650aec08b971b417b21aa4b2305c6..6212d3ae2b11f35a576fcd02afe7f55965206e08 100644 +index 10b062abf8304df32652c57f377d57209bb47ed1..4eefe928daaf959d0cb1f0820e01ee05754bb4d5 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -1204,7 +1204,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { +@@ -1206,7 +1206,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { } if (ret_code == 0) { @@ -32,7 +32,7 @@ index c0c65798b61650aec08b971b417b21aa4b2305c6..6212d3ae2b11f35a576fcd02afe7f559 return SSL_ERROR_ZERO_RETURN; } // An EOF was observed which violates the protocol, and the underlying -@@ -2571,13 +2571,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { +@@ -2573,13 +2573,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } diff --git a/patches/chromium/add_didinstallconditionalfeatures.patch b/patches/chromium/add_didinstallconditionalfeatures.patch index 5258899fc7dda..34880274e34a3 100644 --- a/patches/chromium/add_didinstallconditionalfeatures.patch +++ b/patches/chromium/add_didinstallconditionalfeatures.patch @@ -23,10 +23,10 @@ index 44da0544b778d6ff4c14b6f4e8463cb8260d2f0d..8ae8939af4141a684b7a6d50a43e1abb int32_t world_id) {} virtual void DidClearWindowObject() {} diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 3afbd7c4e88d97f1a5c744e82eba2da19cd9e53f..143c42b98965c879557213670a9fbbe460faec5b 100644 +index 0fb92081a1bbfb14c0ddd74dfe91f94bb2be1d2a..886f677564084d8b6af15b03403cfa56aeddd3c9 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -4794,6 +4794,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, +@@ -4802,6 +4802,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, observer.DidCreateScriptContext(context, world_id); } diff --git a/patches/chromium/add_electron_deps_to_license_credits_file.patch b/patches/chromium/add_electron_deps_to_license_credits_file.patch index 62ae21576df52..cce3480fa518f 100644 --- a/patches/chromium/add_electron_deps_to_license_credits_file.patch +++ b/patches/chromium/add_electron_deps_to_license_credits_file.patch @@ -7,7 +7,7 @@ Ensure that licenses for the dependencies introduced by Electron are included in `LICENSES.chromium.html` diff --git a/tools/licenses/licenses.py b/tools/licenses/licenses.py -index 1368fae3346175c0fdd652d74f882eca26dd7aa2..7f2ca2366ea188bfb0a6e846a519b9cf790ec56a 100755 +index 12ad657f5df3ff4af2bdbd8b9fb7959131db2970..f2b03596dd5ff96236272b6348336515df85eef0 100755 --- a/tools/licenses/licenses.py +++ b/tools/licenses/licenses.py @@ -336,6 +336,31 @@ SPECIAL_CASES = { diff --git a/patches/chromium/blink_local_frame.patch b/patches/chromium/blink_local_frame.patch index 82d56cbcf5ea7..edfeb9615f758 100644 --- a/patches/chromium/blink_local_frame.patch +++ b/patches/chromium/blink_local_frame.patch @@ -49,10 +49,10 @@ index 9dc450bc20744463c8898bc822a558be38486493..576421cb9600625ad8b9eda25cb99954 // its owning reference back to our owning LocalFrame. client_->Detached(type); diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index dbb0843658e356f3c47dcaef470789c9e047aeee..feb30ced77cfc7d0a1faa637cd8bdec399155504 100644 +index ba6aac34b4838efa86c01926759871a31a3b2257..99ae1320be10ac19d204edfe1c1cf4f52c62eb76 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -746,10 +746,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -748,10 +748,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { } DCHECK(!view_ || !view_->IsAttached()); @@ -63,7 +63,7 @@ index dbb0843658e356f3c47dcaef470789c9e047aeee..feb30ced77cfc7d0a1faa637cd8bdec3 if (!Client()) return false; -@@ -803,6 +799,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -805,6 +801,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { DCHECK(!view_->IsAttached()); Client()->WillBeDetached(); diff --git a/patches/chromium/build_add_electron_tracing_category.patch b/patches/chromium/build_add_electron_tracing_category.patch index a1897b9d44249..9b087aa5845bb 100644 --- a/patches/chromium/build_add_electron_tracing_category.patch +++ b/patches/chromium/build_add_electron_tracing_category.patch @@ -8,7 +8,7 @@ categories in use are known / declared. This patch is required for us to introduce a new Electron category for Electron-specific tracing. diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h -index c428a5f8d79e826077ab05fb6c56ae8e0e4ff609..c5fd8782fc1343f04f9e2c2c0414245d20696193 100644 +index 45c518185da68419e0f482acba359f02c2152f88..a0e35f118f0b1f767b41676d651a575e8b5fddb4 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h @@ -91,6 +91,7 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index d366c327411ce..3592273e6afc1 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -33,10 +33,10 @@ index b38442f018b218944c7b85c9f8bd8b8eb6137b9e..dd15f6cf5dc40f2d54134c833d35508f "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index d668c23e5eb34602fab4f9002c341a9d38f13995..55e5e98c332fdc99b7ce824665d71ceb826d647f 100644 +index 0ad542897fa8e45003a7945d9393f84844f993d3..0fb8770c97c2a3c2ffebb558cd81081821747b2a 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4564,7 +4564,7 @@ static_library("browser") { +@@ -4567,7 +4567,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index d668c23e5eb34602fab4f9002c341a9d38f13995..55e5e98c332fdc99b7ce824665d71ceb # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index ceefd25c55645ddc0bc04c7509962edec1148506..bfb8ee6710ac4fa9e35cd92d109fc8b1647c552d 100644 +index 1cc7b3905eae8d1c3025ae3454482ea8418a5217..bf71a7514f337cc6447e27a8ebd3a2c299840121 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7024,9 +7024,12 @@ test("unit_tests") { +@@ -7031,9 +7031,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index ceefd25c55645ddc0bc04c7509962edec1148506..bfb8ee6710ac4fa9e35cd92d109fc8b1 "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -7990,6 +7993,10 @@ test("unit_tests") { +@@ -7996,6 +7999,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index ceefd25c55645ddc0bc04c7509962edec1148506..bfb8ee6710ac4fa9e35cd92d109fc8b1 sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8045,7 +8052,6 @@ test("unit_tests") { +@@ -8051,7 +8058,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/build_gn.patch b/patches/chromium/build_gn.patch index bd1b1df06c7e8..a8999358f9f38 100644 --- a/patches/chromium/build_gn.patch +++ b/patches/chromium/build_gn.patch @@ -7,7 +7,7 @@ These are variables we add to the root BUILDCONFIG so that they're available everywhere, without having to import("//electron/.../flags.gni"). diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn -index 9e64769566a136b41cab4ab5e31798ae33d2ebd1..0303488f688c1e18d0cdbafc0c481cccbbcc96d0 100644 +index bdf8b13631a3cdf698078f70c435e9316cf0bfc3..53df468e94bdd2cdf049604e29e55863efdd3a8d 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -123,6 +123,9 @@ if (current_os == "") { diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index 6b09d4fa7c36a..b5cb0276c27fd 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 6708ec005561fb174ed9557ad92a85f5c33666ee..37af686964489bd77d84be95d5f3aba9cd0a7a0c 100644 +index 447c0ae162f45eb157008e62cbc756d6f23e4920..27db5dbfd05106788feda9daab9d1aaaf1ae920f 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9629,6 +9629,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9635,6 +9635,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,7 +21,7 @@ index 6708ec005561fb174ed9557ad92a85f5c33666ee..37af686964489bd77d84be95d5f3aba9 &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index c28aabb66b8ff483a140a6c18b4cd7fbc82c649d..60ed731f7648497d62fbc7b43181a47a82743c4b 100644 +index c53ab4b96a16b7326dfde14452b20d2170aecf04..e500fe77f55836b3e5832536f98cf9581984b8c1 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -5061,6 +5061,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -51,13 +51,13 @@ index c28aabb66b8ff483a140a6c18b4cd7fbc82c649d..60ed731f7648497d62fbc7b43181a47a new_contents_impl, opener, params.target_url, params.referrer.To(), params.disposition, diff --git a/content/common/frame.mojom b/content/common/frame.mojom -index 91dcf6c9c4a2d840fb50cb329fe3ef1bba9103c3..cbc887a3034605a93468e73a310e9ca6838b32d8 100644 +index 8f8f79733c956fed2469e51993bad29689c11d8a..17e953f46d479f431fa06d28857901cb844ff4ea 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom -@@ -621,6 +621,10 @@ struct CreateNewWindowParams { - // The navigation initiator's user activation and ad status. - blink.mojom.NavigationInitiatorActivationAndAdStatus - initiator_activation_and_ad_status; +@@ -642,6 +642,10 @@ struct CreateNewWindowParams { + pending_associated_remote widget; + pending_associated_receiver frame_widget_host; + pending_associated_remote frame_widget; + + // Extra fields added by Electron. + string raw_features; @@ -66,10 +66,10 @@ index 91dcf6c9c4a2d840fb50cb329fe3ef1bba9103c3..cbc887a3034605a93468e73a310e9ca6 // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index 54014f1d8374b4286b3f4358cca1b6964ce370f8..50c5073b4281239a485ea8b0e08d687d7e9a3cf3 100644 +index f5b028759f86f382230867e6abf72a82051c02c3..f1110385258f057be3b456198b46e4d2c54ca718 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc -@@ -805,6 +805,8 @@ bool ContentBrowserClient::CanCreateWindow( +@@ -815,6 +815,8 @@ bool ContentBrowserClient::CanCreateWindow( const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -79,7 +79,7 @@ index 54014f1d8374b4286b3f4358cca1b6964ce370f8..50c5073b4281239a485ea8b0e08d687d bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index f3750ed03dff3ca9885b189692dde2c919ab3eb3..faf574d7778e24d6fc9e3f539b39c9cb1c149bbc 100644 +index 1c7e6dc3b867c0e598f8517591ffb9aa8007bfea..7459b083156d1f6bc01198690c4c9ec02d88d862 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -198,6 +198,7 @@ class NetworkService; @@ -90,7 +90,7 @@ index f3750ed03dff3ca9885b189692dde2c919ab3eb3..faf574d7778e24d6fc9e3f539b39c9cb } // namespace network namespace sandbox { -@@ -1356,6 +1357,8 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -1368,6 +1369,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -148,10 +148,10 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 // typically happens when popups are created. virtual void WebContentsCreated(WebContents* source_contents, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 602bea4189f0d23c19cbc7afd56583927cbb7e0c..3afbd7c4e88d97f1a5c744e82eba2da19cd9e53f 100644 +index 0e2524de1159d7e2628c66d188002c6a417bfa52..0fb92081a1bbfb14c0ddd74dfe91f94bb2be1d2a 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -6916,6 +6916,10 @@ WebView* RenderFrameImpl::CreateNewWindow( +@@ -6924,6 +6924,10 @@ WebView* RenderFrameImpl::CreateNewWindow( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); @@ -210,7 +210,7 @@ index 82e9d3dfb5f7da76d89fe15ae61d379fa46e177d..fd035512099a54dff6cc951a2226c23a } // namespace blink diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc -index b44f0db98f89befc33430bcf275bffcfae9e7171..66f92d1e975a74720af12662447ea214ac210248 100644 +index 70d107d7c99056e790d75755855b804ed1961a90..a0ed0e7cd27532dcf2c327874ae4573c70290ecd 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc @@ -2270,6 +2270,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, diff --git a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch index 0d84247f270e1..16f983f707470 100644 --- a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch +++ b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch @@ -61,10 +61,10 @@ index 4865d79c95c34d8cead96d3bb8063a0e2bd6076b..ebfa09ed15dca98b75a013e3dcbb566c // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..ad844f4d6d150aee3e00fd6465600bfb248d79d2 100644 +index 3a60e310d1c4048f0e37e085c97b8dfc093aefda..8fe48c9bef144218e34434d563883b15733d03bc 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3146,15 +3146,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3156,15 +3156,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } // We must let Windows handle the caption buttons if it's drawing them, or // they won't work. @@ -86,7 +86,7 @@ index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..ad844f4d6d150aee3e00fd6465600bfb return 0; } } -@@ -3177,6 +3181,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3187,6 +3191,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, // handle alt-space, or in the frame itself. is_right_mouse_pressed_on_caption_ = false; ReleaseCapture(); @@ -94,7 +94,7 @@ index 7f29d902ae0a2f980a56e77e6e25935dab3c0685..ad844f4d6d150aee3e00fd6465600bfb // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() // expect screen coordinates. POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); -@@ -3184,7 +3189,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3194,7 +3199,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, w_param = static_cast(SendMessage( hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); if (w_param == HTCAPTION || w_param == HTSYSMENU) { diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index aeec7988bdc9e..801c594f350aa 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,7 +14,7 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index a16c2368a4e37756642544c45d3e4b1fe7d64b26..f3d223efcfa8f909e810ec43dd5ef90c9fc5c620 100644 +index 6010d9f9fc9bfeffb3e5a64de7352b52a202cbf7..4325cdfd256ae7a1008e073d42da995b82df5bba 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -4980,7 +4980,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index ff7e01427518c..c390777f3f0d4 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -141,10 +141,10 @@ index ca72b324bf7c3b81ac94b53f0ff454d2df177950..d60ef3075d126e2bbd50c8469f2bf67c // The profile used for the presentation. raw_ptr otr_profile_; diff --git a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc -index 83a0a538fef0da1d3674293d20dac7b8a252273f..155c8a0af46e20f68f8b028c056092b404062861 100644 +index 1085003ab18d471d5c018ac68041924d458fcec7..4aac70febec9f0abadd1ecb96d9066e6c270efca 100644 --- a/chrome/browser/ui/views/hats/hats_next_web_dialog.cc +++ b/chrome/browser/ui/views/hats/hats_next_web_dialog.cc -@@ -99,8 +99,7 @@ class HatsNextWebDialog::HatsWebView : public views::WebView { +@@ -100,8 +100,7 @@ class HatsNextWebDialog::HatsWebView : public views::WebView { content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -218,7 +218,7 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index e84ef165670048dd2f030a48ff2d9796b5170b52..591c87ffc5f56b38d0f329da8b983a77af9662ee 100644 +index 952fd20f71560acd89c74f08e9d8cdbf34fb5a1a..59b05937ceb1b81b69d913f587150021b9031106 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -4943,8 +4943,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index 00a29bfd44cf4..f62bee289327c 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -6,7 +6,7 @@ Subject: disable_hidden.patch Electron uses this to disable background throttling for hidden windows. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index f4475e34d3d6cf78b1d5b5492e398d1551c2bd90..f13799f1cc440a20d8e3c55ee35f64e6505e5590 100644 +index 22ffafd8f5e0f49d5c9df7ecd3f61dad3dfd2814..a409c64768968c81be3b6c7e7646f8df26e2afe6 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -832,6 +832,10 @@ void RenderWidgetHostImpl::WasHidden() { @@ -21,10 +21,10 @@ index f4475e34d3d6cf78b1d5b5492e398d1551c2bd90..f13799f1cc440a20d8e3c55ee35f64e6 // Prompts should remain open and functional across tab switches. if (!delegate_ || !delegate_->IsWaitingForPointerLockPrompt(this)) { diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h -index 99fe44aab8599bffe256e4683ec36441c06925b8..72c5afe5d028c77acb76757698c15a013379860d 100644 +index c201cff9e5c3b286389a5eb74e1a9ebd86edfef9..949f6a7867758e35c24897add9a4d47a641357b2 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h -@@ -1017,6 +1017,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl +@@ -1012,6 +1012,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Requests a commit and forced redraw in the renderer compositor. void ForceRedrawForTesting(); diff --git a/patches/chromium/enable_reset_aspect_ratio.patch b/patches/chromium/enable_reset_aspect_ratio.patch index 07d3c9dadbc65..0a0ec7c802f06 100644 --- a/patches/chromium/enable_reset_aspect_ratio.patch +++ b/patches/chromium/enable_reset_aspect_ratio.patch @@ -19,10 +19,10 @@ index 24f63e82a1a170b392bdc8e868729ddd5f9238fa..55c426aee12da4d4d1f62dc7d489133e excluded_margin); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index a10d781e667416e74f53583081867d879f9f1eae..d1146216b6969562876a318586420636331f63d1 100644 +index 8d5002fab43ccfcaccdb044fc9b2a95748e71b75..adade58a533c373087d8c51a5744e8f118ba6e9d 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -990,8 +990,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, +@@ -997,8 +997,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, void HWNDMessageHandler::SetAspectRatio(float aspect_ratio, const gfx::Size& excluded_margin) { diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index dfb7eb432725b..0b079b34898dc 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,7 +33,7 @@ index 0ab8187b0db8ae6db46d81738f653a2bc4c566f6..de3d55e85c22317f7f9375eb94d0d5d4 } // namespace net diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 771fee7092bb25e4f7546941a04647d060e966c2..356d7e8fd5e8a82a8d506d1172844706bc76c99f 100644 +index b1df0a09a9bfd226ffe7b37144e6599b099e619e..f8c7bc591552fea25b4a8e0edd7a3823b00e6b06 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -1814,6 +1814,13 @@ void NetworkContext::SetNetworkConditions( @@ -63,10 +63,10 @@ index 930e0bd987c48d111b2c8d71147c09e4418bda6c..9373a53c5cac879c689fcea77f1dbbb3 void SetEnableReferrers(bool enable_referrers) override; #if BUILDFLAG(IS_CT_SUPPORTED) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 773049b72e7784d396bb9cd1ebd29c3a523e7454..eccbcaf01b06d3c7613556d8f7617502c823e074 100644 +index cf4d2dcf86b6536c37d46875f74517e478b34928..b19976926137cae56094ec6e292a5014c2fd546b 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -1261,6 +1261,9 @@ interface NetworkContext { +@@ -1267,6 +1267,9 @@ interface NetworkContext { SetNetworkConditions(mojo_base.mojom.UnguessableToken throttling_profile_id, NetworkConditions? conditions); diff --git a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch index 59bc1acc40fc2..f6f5a58425702 100644 --- a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch +++ b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch @@ -62,10 +62,10 @@ index 2e657f24bb625c7a7af14686553aebdc06ad8eda..6f2384338ac4a48a78bc8aac8b4bb9d3 SEQUENCE_CHECKER(sequence_checker_); }; diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index b5ed5d33890bf4f98237c92bbe642063d27d0092..82c1705ae0dbc7ac33eb90e7978d48c7de7f1c8f 100644 +index ff43747a45b5f508f45afb1e6304bda22ae46fbc..955c3734591f608f32b40927e53db1bf453ce907 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc -@@ -688,6 +688,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { +@@ -695,6 +695,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { if (InForcedColorsMode() && !IsForcedDarkMode()) { return false; } diff --git a/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch b/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch index 48f7a56290f80..2135811c98dc5 100644 --- a/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch +++ b/patches/chromium/feat_allow_code_cache_in_custom_schemes.patch @@ -9,18 +9,18 @@ embedders to make custom schemes allow V8 code cache. Chromium CL: https://chromium-review.googlesource.com/c/chromium/src/+/5019665 diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc -index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e546c9f067 100644 +index cad3cf44df0c65067d18490ee7694fd0f82153af..0f9b64a81e0c4114bd885b24ff65c458810ceb99 100644 --- a/content/browser/code_cache/generated_code_cache.cc +++ b/content/browser/code_cache/generated_code_cache.cc -@@ -12,6 +12,7 @@ - #include +@@ -8,6 +8,7 @@ #include + #include "base/compiler_specific.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" -@@ -36,6 +37,7 @@ +@@ -32,6 +33,7 @@ #include "net/http/http_cache.h" #include "third_party/blink/public/common/scheme_registry.h" #include "url/gurl.h" @@ -28,7 +28,7 @@ index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e5 using storage::BigIOBuffer; -@@ -48,7 +50,7 @@ constexpr char kSeparator[] = " \n"; +@@ -44,7 +46,7 @@ constexpr char kSeparator[] = " \n"; // We always expect to receive valid URLs that can be used as keys to the code // cache. The relevant checks (for ex: resource_url is valid, origin_lock is @@ -37,7 +37,7 @@ index bba85bb47e489b73c2341bbeb9aa04099d712889..4bd69c3c5511a4f492f78f203284c8e5 // // This function doesn't enforce anything in the production code. It is here // to make the assumptions explicit and to catch any errors when DCHECKs are -@@ -58,33 +60,55 @@ void CheckValidKeys(const GURL& resource_url, +@@ -54,33 +56,55 @@ void CheckValidKeys(const GURL& resource_url, GeneratedCodeCache::CodeCacheType cache_type) { // If the resource url is invalid don't cache the code. DCHECK(resource_url.is_valid()); diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch index 6ff014b7abe3a..08f61ae649db4 100644 --- a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -522,7 +522,7 @@ index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109cc waiting_on_draw_ack_ = true; diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -index e7ba8edf2b6e2228bc142bb164c9cd5a147ce5ad..a278a3eca9eedf53ca3878fc8c41d42de3a1b708 100644 +index 8c660af0aaa1c031815082838dea1497b725e55b..852f4d9ac45aaa51f5edf0b3a5e3492eec52d743 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc @@ -112,7 +112,8 @@ RootCompositorFrameSinkImpl::Create( @@ -564,10 +564,10 @@ index 399fba1a3d4e601dc2cdd5f1f4def8b7fd7a3011..8bcbe0d26c80323155d536c0d3a177a1 gpu::SyncPointManager* GetSyncPointManager() override; gpu::Scheduler* GetGpuScheduler() override; diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc -index d0ea177f54b8db8837840e22f5cdb6b64a0c9605..94e4a27fefff406e1d09e7867c0e84e43e6027cf 100644 +index 6e35e3a2e1cc10d62a487111d1e185bf900d9cfa..fe0dbc425746ec97372cade1365a5654b22881f3 100644 --- a/content/browser/compositor/viz_process_transport_factory.cc +++ b/content/browser/compositor/viz_process_transport_factory.cc -@@ -441,8 +441,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( +@@ -436,8 +436,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( mojo::AssociatedRemote display_private; root_params->display_private = display_private.BindNewEndpointAndPassReceiver(); @@ -585,10 +585,10 @@ index d0ea177f54b8db8837840e22f5cdb6b64a0c9605..94e4a27fefff406e1d09e7867c0e84e4 compositor_data.display_client->GetBoundRemote(resize_task_runner_); mojo::AssociatedRemote diff --git a/services/viz/privileged/mojom/compositing/display_private.mojom b/services/viz/privileged/mojom/compositing/display_private.mojom -index 7d19b6be8bb0e0269c381cf6efdf79eaeff1e935..b8ec06ade095df99c024396a601dbf1abb97cc00 100644 +index 3046e60995bceb1c9931038be3d309577a830a60..4476f48510f807d03f05a4afa2a648dac8720010 100644 --- a/services/viz/privileged/mojom/compositing/display_private.mojom +++ b/services/viz/privileged/mojom/compositing/display_private.mojom -@@ -117,7 +117,6 @@ interface DisplayClient { +@@ -125,7 +125,6 @@ interface DisplayClient { // Creates a LayeredWindowUpdater implementation to draw into a layered // window. diff --git a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch index 9d378513a70ec..24a9a04e113ac 100644 --- a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch +++ b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch @@ -37,7 +37,7 @@ index b60fd23a39eb423450b57275526ac4ba36058225..deafb4cd5a55853eb11a1371ca8331ee allow_cookies_from_browser == other.allow_cookies_from_browser && include_request_cookies_with_response == diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h -index 6430943ce0a4817d2806f701ef39b4263648ce6e..4b8eddb96f739e86cb35f7bfe2d9742de8dcecda 100644 +index b87bb47d4d51f734ee3dc1e38158726795fcd19e..dd862ff8c43ecbc08467bf2b49ed5044d92ad3f3 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h @@ -77,6 +77,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { @@ -49,7 +49,7 @@ index 6430943ce0a4817d2806f701ef39b4263648ce6e..4b8eddb96f739e86cb35f7bfe2d9742d mojo::PendingRemote trust_token_observer; mojo::PendingRemote diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc -index 33f4def7593dac366034b5a8e83b5079a726f4b7..cd83b5d95099af724d1d04642cad313905c5d3a1 100644 +index ae26e1cff7235ac77d4610f936cf83b01a9b429d..d45189ba3280cd6802240fc5cee25388f77bdb39 100644 --- a/services/network/public/cpp/url_request_mojom_traits.cc +++ b/services/network/public/cpp/url_request_mojom_traits.cc @@ -49,6 +49,7 @@ bool StructTraits>(); out->trust_token_observer = data.TakeTrustTokenObserver< diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h -index cdf5283b5b3e58f77f37e9501d2b867493fa6cf7..86e41493fcffb0d9fc9b4306714b910e2f2963e0 100644 +index fc85f9def245d854d210b14f8190119fbd38404b..1392090ef9a1c8c0ef1a958ebeae75f24a11a6ea 100644 --- a/services/network/public/cpp/url_request_mojom_traits.h +++ b/services/network/public/cpp/url_request_mojom_traits.h @@ -71,6 +71,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) @@ -76,7 +76,7 @@ index cdf5283b5b3e58f77f37e9501d2b867493fa6cf7..86e41493fcffb0d9fc9b4306714b910e cookie_observer( const network::ResourceRequest::TrustedParams& trusted_params) { diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom -index 8fae9662ac05c94dc3545a125cb5e838664c97c2..5abf38ed936163b14a4bfd613728f03f1494c6df 100644 +index eb5ca6401eec0ff1d6a897ea86ca2a1a252f86ec..caedd3dfde37dd136721429f90799e0493ab0a9d 100644 --- a/services/network/public/mojom/url_request.mojom +++ b/services/network/public/mojom/url_request.mojom @@ -74,6 +74,9 @@ struct TrustedUrlRequestParams { @@ -90,7 +90,7 @@ index 8fae9662ac05c94dc3545a125cb5e838664c97c2..5abf38ed936163b14a4bfd613728f03f // a cookie. If this is set to non-null, the observer passed to // URLLoaderFactory will be ignored. diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom -index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b703960dc 100644 +index 1e5b36d0a46d9c66b7a56a6668663dd8196bd172..3431f4c3959264b64ac0f35654719ab0b45b5cb7 100644 --- a/services/network/public/mojom/url_response_head.mojom +++ b/services/network/public/mojom/url_response_head.mojom @@ -13,6 +13,7 @@ import "services/network/public/mojom/attribution.mojom"; @@ -101,7 +101,7 @@ index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b import "services/network/public/mojom/ip_endpoint.mojom"; import "services/network/public/mojom/load_timing_info.mojom"; import "services/network/public/mojom/network_param.mojom"; -@@ -49,6 +50,9 @@ struct URLResponseHead { +@@ -50,6 +51,9 @@ struct URLResponseHead { // The response headers or NULL if the URL type does not support headers. HttpResponseHeaders headers; @@ -112,10 +112,10 @@ index 5c4b8a05034f8defacbc13671fe9bc92f76ade5a..e7390e01f113755613f42d592b36108b string mime_type; diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc -index d11d199c44c1f4d72765714d772066d4a36ad8e2..3d5c61726679cedfc6d9e9587eaedfe4ada2f1f9 100644 +index c5f551ca79ecfb80a3c29b901d44a83d082a6da6..bae9c2c10d240a8691787d6e52190b13575df020 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc -@@ -667,6 +667,9 @@ URLLoader::URLLoader( +@@ -669,6 +669,9 @@ URLLoader::URLLoader( mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunner::GetCurrentDefault()), per_factory_orb_state_(context.GetMutableOrbState()), @@ -125,7 +125,7 @@ index d11d199c44c1f4d72765714d772066d4a36ad8e2..3d5c61726679cedfc6d9e9587eaedfe4 devtools_request_id_(request.devtools_request_id), options_(PopulateOptions(options, factory_params_->is_orb_enabled, -@@ -964,7 +967,7 @@ void URLLoader::ConfigureRequest( +@@ -970,7 +973,7 @@ void URLLoader::ConfigureRequest( &URLLoader::IsSharedDictionaryReadAllowed, base::Unretained(this))); } @@ -134,7 +134,7 @@ index d11d199c44c1f4d72765714d772066d4a36ad8e2..3d5c61726679cedfc6d9e9587eaedfe4 url_request_->SetResponseHeadersCallback(base::BindRepeating( &URLLoader::SetRawResponseHeaders, base::Unretained(this))); } -@@ -2049,6 +2052,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { +@@ -2062,6 +2065,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { } response_ = BuildResponseHead(); @@ -155,10 +155,10 @@ index d11d199c44c1f4d72765714d772066d4a36ad8e2..3d5c61726679cedfc6d9e9587eaedfe4 // Parse and remove the Trust Tokens response headers, if any are expected, diff --git a/services/network/url_loader.h b/services/network/url_loader.h -index c45d947ad9059df5694eccd2c20774248e951c6f..d4d37eb83fcae5145922346db8d7200ddd33002d 100644 +index 06b9d01648881d1b955cc6db195a658811e47e84..730fd3ef9f95ba634b43856d432c072962d6000e 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h -@@ -711,6 +711,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader +@@ -712,6 +712,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader std::unique_ptr resource_scheduler_request_handle_; diff --git a/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch index c82e141f93c57..7ce101dd084ba 100644 --- a/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch +++ b/patches/chromium/feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch @@ -20,59 +20,59 @@ This patch will be removed when the deprecated sync api support is removed. diff --git a/components/permissions/permission_util.cc b/components/permissions/permission_util.cc -index 20056b1464699cc7d45717bfc710366eb48d85f6..b227ed62991cdf285c3b90f19e09e3718e2800ea 100644 +index 6f4d9651862e90abaa22b93e1dcf97ac07592c5c..d91d81297e7cdb79ee7b7a3979dcedf977e16c92 100644 --- a/components/permissions/permission_util.cc +++ b/components/permissions/permission_util.cc -@@ -384,6 +384,7 @@ ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( - return ContentSettingsType::AUTOMATIC_FULLSCREEN; - case PermissionType::WEB_APP_INSTALLATION: +@@ -389,6 +389,7 @@ ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( return ContentSettingsType::WEB_APP_INSTALLATION; + case PermissionType::LOCAL_NETWORK_ACCESS: + return ContentSettingsType::LOCAL_NETWORK_ACCESS; + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: case PermissionType::NUM: break; } diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc -index e991887c103618b35688cf72307ca05fdb202e6e..54894f3412d42264eae80d767be5215e52f08184 100644 +index 0c6b537ddc2afbc4cd22f1c08f1bbae3c3bbffe3..333f557c89bc03ae5b25060359b9af096b188726 100644 --- a/content/browser/permissions/permission_controller_impl.cc +++ b/content/browser/permissions/permission_controller_impl.cc -@@ -86,6 +86,7 @@ PermissionToSchedulingFeature(PermissionType permission_name) { - case PermissionType::POINTER_LOCK: +@@ -87,6 +87,7 @@ PermissionToSchedulingFeature(PermissionType permission_name) { case PermissionType::AUTOMATIC_FULLSCREEN: case PermissionType::WEB_APP_INSTALLATION: + case PermissionType::LOCAL_NETWORK_ACCESS: + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: return std::nullopt; } } diff --git a/third_party/blink/common/permissions/permission_utils.cc b/third_party/blink/common/permissions/permission_utils.cc -index 779b458094edfa866ac70d7e0ac6413cc700c6a5..b42cbc34f1d2e214d8cc6ed2aacfa9ae21c2569a 100644 +index aad897dbc32caa3469a2f04b4f004355888c84d8..875260fc2a884ab5b1e62cd45fc91c2e4dc162ee 100644 --- a/third_party/blink/common/permissions/permission_utils.cc +++ b/third_party/blink/common/permissions/permission_utils.cc -@@ -99,6 +99,8 @@ std::string GetPermissionString(PermissionType permission) { - return "AutomaticFullscreen"; - case PermissionType::WEB_APP_INSTALLATION: +@@ -101,6 +101,8 @@ std::string GetPermissionString(PermissionType permission) { return "WebAppInstallation"; + case PermissionType::LOCAL_NETWORK_ACCESS: + return "LocalNetworkAccess"; + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: + return "DeprecatedSyncClipboardRead"; case PermissionType::NUM: NOTREACHED(); } -@@ -171,6 +173,7 @@ PermissionTypeToPermissionsPolicyFeature(PermissionType permission) { - case PermissionType::NOTIFICATIONS: - case PermissionType::KEYBOARD_LOCK: +@@ -175,6 +177,7 @@ PermissionTypeToPermissionsPolicyFeature(PermissionType permission) { case PermissionType::POINTER_LOCK: + // TODO(crbug.com/394009026): Add permission policy for LNA. + case PermissionType::LOCAL_NETWORK_ACCESS: + case PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: return std::nullopt; case PermissionType::NUM: diff --git a/third_party/blink/public/common/permissions/permission_utils.h b/third_party/blink/public/common/permissions/permission_utils.h -index f9b1db54dd367d1f0e42cdfcfd04255d452f0e1b..54b4b742298af03d8924f6ad613081f1b5dfae4d 100644 +index 947bd8125bf300a3a82c811810e31996e6147c7c..caeeaf6cecf7da47c39b140dfcc295fba12f6f84 100644 --- a/third_party/blink/public/common/permissions/permission_utils.h +++ b/third_party/blink/public/common/permissions/permission_utils.h -@@ -64,6 +64,7 @@ enum class PermissionType { - AUTOMATIC_FULLSCREEN = 40, +@@ -65,6 +65,7 @@ enum class PermissionType { HAND_TRACKING = 41, WEB_APP_INSTALLATION = 42, -+ DEPRECATED_SYNC_CLIPBOARD_READ = 43, + LOCAL_NETWORK_ACCESS = 43, ++ DEPRECATED_SYNC_CLIPBOARD_READ = 44, // Always keep this at the end. NUM, diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index 81656d9ebac69..132d065cea863 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,10 +14,10 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index d68fd93d4cb912b9b27d3a7cdfecc6faf638b91e..22ed43bdf4dbaccc598135807abc8383c52db50e 100644 +index 3d8a590aacd3a65d5a19004dc11e770b31a0f614..64dd7b6e507b61fab7a044823462fb04eabba698 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -934,13 +934,13 @@ void HWNDMessageHandler::FrameTypeChanged() { +@@ -941,13 +941,13 @@ void HWNDMessageHandler::FrameTypeChanged() { void HWNDMessageHandler::PaintAsActiveChanged() { if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || @@ -33,7 +33,7 @@ index d68fd93d4cb912b9b27d3a7cdfecc6faf638b91e..22ed43bdf4dbaccc598135807abc8383 } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -2329,17 +2329,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, +@@ -2337,17 +2337,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, delegate_->SchedulePaint(); } diff --git a/patches/chromium/fix_aspect_ratio_with_max_size.patch b/patches/chromium/fix_aspect_ratio_with_max_size.patch index a9a44c05296b8..5761ac3ed7e4b 100644 --- a/patches/chromium/fix_aspect_ratio_with_max_size.patch +++ b/patches/chromium/fix_aspect_ratio_with_max_size.patch @@ -11,10 +11,10 @@ enlarge window above dimensions set during creation of the BrowserWindow. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index d1146216b6969562876a318586420636331f63d1..7f29d902ae0a2f980a56e77e6e25935dab3c0685 100644 +index adade58a533c373087d8c51a5744e8f118ba6e9d..3a60e310d1c4048f0e37e085c97b8dfc093aefda 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3749,15 +3749,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, +@@ -3759,15 +3759,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, delegate_->GetMinMaxSize(&min_window_size, &max_window_size); min_window_size = delegate_->DIPToScreenSize(min_window_size); max_window_size = delegate_->DIPToScreenSize(max_window_size); diff --git a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch index 1336f863a8bda..233722adb2e93 100644 --- a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch +++ b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch @@ -28,10 +28,10 @@ The patch should be removed in favor of either: Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397. diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc -index 0bd3df72a9a5f127cb8215ab4182e143ca99548d..f8b00313490e898c0d70667b996b3e66a0063c4b 100644 +index 4f7e3cd70d45dd9d0d67b6a39a5dac59af9b95af..a8a0bedfc14cd475ff112e5ff07e137ec4219662 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc -@@ -11059,6 +11059,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { +@@ -11068,6 +11068,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { "blob"); } diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index 16b2a230f311e..bbfafcb56c48d 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -87,10 +87,10 @@ index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac4 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 6f323c5863f4bb869a66d265fac1d372c5243187..a16c2368a4e37756642544c45d3e4b1fe7d64b26 100644 +index c3b563b0b727bc35f6d4499c589110644ebe9cd1..6010d9f9fc9bfeffb3e5a64de7352b52a202cbf7 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9768,7 +9768,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9772,7 +9772,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index 336e619b4b231..7474ebc136021 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,10 +18,10 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index ad844f4d6d150aee3e00fd6465600bfb248d79d2..d68fd93d4cb912b9b27d3a7cdfecc6faf638b91e 100644 +index 8fe48c9bef144218e34434d563883b15733d03bc..3d8a590aacd3a65d5a19004dc11e770b31a0f614 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1788,7 +1788,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { +@@ -1796,7 +1796,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); diff --git a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch index 92c7ec90f2052..3c7da3c7a1ef0 100644 --- a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch +++ b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch @@ -11,7 +11,7 @@ This patch should be upstreamed as a conditional revert of the logic in desktop vs mobile runtimes. i.e. restore the old logic only on desktop platforms diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index e46e5eb74c54bc00ead6e2e4eff99fd500e09262..c07ffdb0ae8b275a72d789e9091a807362379474 100644 +index 7c31b82e2903507bb69aaa71fc0ed51cad06d1c2..0ad97d9973fc6d637967f911cb37ada2313e2776 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -2114,9 +2114,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { diff --git a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch index 736bd84f9e9cd..398721de00d98 100644 --- a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch +++ b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch @@ -59,10 +59,10 @@ index cba373664bec3a32abad6fe0396bd67b53b7e67f..a54f1b3351efd2d8f324436f7f35cd43 #endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_EXECUTION_CALLBACK_H_ diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index feb30ced77cfc7d0a1faa637cd8bdec399155504..6137dc5905604e4309466dd82b2d2fe31e5cd6a7 100644 +index 99ae1320be10ac19d204edfe1c1cf4f52c62eb76..e6a3957848f760809deb0080ee5b87c849dca587 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -3097,6 +3097,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3099,6 +3099,7 @@ void LocalFrame::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -70,7 +70,7 @@ index feb30ced77cfc7d0a1faa637cd8bdec399155504..6137dc5905604e4309466dd82b2d2fe3 BackForwardCacheAware back_forward_cache_aware, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_behavior) { -@@ -3129,7 +3130,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3131,7 +3132,7 @@ void LocalFrame::RequestExecuteScript( PausableScriptExecutor::CreateAndRun( script_state, std::move(script_sources), execute_script_policy, user_gesture, evaluation_timing, blocking_option, want_result_option, @@ -80,7 +80,7 @@ index feb30ced77cfc7d0a1faa637cd8bdec399155504..6137dc5905604e4309466dd82b2d2fe3 void LocalFrame::SetEvictCachedSessionStorageOnFreezeOrUnload() { diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h -index 80006535d1b7a7bfff69ebc46a1d9f70ee8c609d..842aa5f37e5f09235969b8510d4aa914e385d1ad 100644 +index 5cdeaa531babca965bed7e1e18ee993f1ba0d847..651e64c0ea39ec28db117aa3a61ea87d3f24117d 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h @@ -815,6 +815,7 @@ class CORE_EXPORT LocalFrame final @@ -203,7 +203,7 @@ index fa65331f40b90d812b71a489fd560e9359152d2b..390714d631dc88ef92d59ef9618a5706 const mojom::blink::UserActivationOption user_activation_option_; const mojom::blink::LoadEventBlockingOption blocking_option_; diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc -index 2e2aa78d308157642cf27941fc22a211f6396a0d..c5bcbe1f933f2e79003f7eb9f6368174426a6f0a 100644 +index b71f1318b186d33fdedd051b0afd04cc60d4d430..5c9fb1e773d9550d2b6b248c66c97ad07e84674e 100644 --- a/third_party/blink/renderer/core/frame/web_frame_test.cc +++ b/third_party/blink/renderer/core/frame/web_frame_test.cc @@ -298,6 +298,7 @@ void ExecuteScriptsInMainWorld( diff --git a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch index 42ee1e554fb90..1e2433baad824 100644 --- a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch +++ b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch @@ -6,7 +6,7 @@ Subject: fix: select the first menu item when opened via keyboard This fixes an accessibility issue where the root view is 'focused' to the screen reader instead of the first menu item as with all other native menus. This patch will be upstreamed. diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc -index 6337d0d70f247b06c7871fb7faa75c2b0b7d541c..2bd399161329b054dd67961f767ccc4480af413c 100644 +index e82f04bf408c5f7c08df6d476ae3130705af6ae4..402e39e39b24f676ec6fc7525235c0ec5f5cdf9b 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc @@ -700,6 +700,14 @@ void MenuController::Run(Widget* parent, @@ -24,7 +24,7 @@ index 6337d0d70f247b06c7871fb7faa75c2b0b7d541c..2bd399161329b054dd67961f767ccc44 if (button_controller) { pressed_lock_ = button_controller->TakeLock( false, ui::LocatedEvent::FromIfValid(event)); -@@ -2401,19 +2409,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { +@@ -2406,19 +2414,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { } item->GetSubmenu()->ShowAt(params); diff --git a/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch b/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch index 0ee9e6d3c8b1d..3ed1c84f8fa3b 100644 --- a/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch +++ b/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch @@ -19,10 +19,10 @@ would be removed from its snapped state when re-shown. This fixes that. Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/6330848. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 22ed43bdf4dbaccc598135807abc8383c52db50e..c5457e3e58b53ca04697b22a885da654c6c0655f 100644 +index 64dd7b6e507b61fab7a044823462fb04eabba698..2cd734db007174834c70365ffe6b46d52673e5cb 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -676,7 +676,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, +@@ -683,7 +683,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, SetWindowPlacement(hwnd(), &placement); native_show_state = SW_SHOWMAXIMIZED; } else { @@ -32,7 +32,7 @@ index 22ed43bdf4dbaccc598135807abc8383c52db50e..c5457e3e58b53ca04697b22a885da654 // Use SW_SHOW/SW_SHOWNA instead of SW_SHOWNORMAL/SW_SHOWNOACTIVATE so that // the window is not restored to its original position if it is maximized. -@@ -686,7 +687,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, +@@ -693,7 +694,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, // position, some do not. See crbug.com/1296710 switch (show_state) { case ui::mojom::WindowShowState::kInactive: @@ -42,7 +42,7 @@ index 22ed43bdf4dbaccc598135807abc8383c52db50e..c5457e3e58b53ca04697b22a885da654 break; case ui::mojom::WindowShowState::kMaximized: native_show_state = SW_SHOWMAXIMIZED; -@@ -697,9 +699,11 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, +@@ -704,9 +706,11 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, case ui::mojom::WindowShowState::kNormal: if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) || (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index 32a598712c035..c485ba5d569da 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -20,7 +20,7 @@ index e9f000ae316ac64cd23827f1533ec1416b3ed040..c3fb6dbd8f4c39e1087be567f6a2c2df } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index faf574d7778e24d6fc9e3f539b39c9cb1c149bbc..ff3255b51e73aea4ec47cc30dfc0032de474ec06 100644 +index 7459b083156d1f6bc01198690c4c9ec02d88d862..d564908c539ee9f8c32c27c90f51b63c152f1392 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -339,6 +339,11 @@ class CONTENT_EXPORT ContentBrowserClient { diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index 1325460b2d3ec..7b66ab45be6cd 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,10 +6,10 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec -index dd7c5df4907dd369ff4df8f5d7286f83674f74f3..9215843a93e795123fd97848bcb2507f0960ed12 100644 +index 77707df02c02ba2ffd66ac280d53479993498fff..acc783a27d84465b81b8aef8b6f151a9ad5f0669 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec -@@ -1476,6 +1476,11 @@ +@@ -1480,6 +1480,11 @@ "<(SHARED_INTERMEDIATE_DIR)/third_party/blink/public/strings/permission_element_generated_strings.grd": { "META": {"sizes": {"messages": [2000],}}, "messages": [10080], diff --git a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch index ce72a672507a2..03982cee101e5 100644 --- a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +++ b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch @@ -11,7 +11,7 @@ Bug: N/A Change-Id: I9fc472212b2d3afac2c8e18a2159bc2d50bbdf98 diff --git a/AUTHORS b/AUTHORS -index 143ea35ef1591719589fa9a750177c7bae526fec..5883c4d63181be3bc95ee3ddbb0e33fb935e070b 100644 +index b55c6916e97a7c8e317145f5239a34d8abca810e..dbd924a91a8fca8ff9d85bf6addcdb5a7b17ca3a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -341,6 +341,7 @@ David Futcher diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 97ec5e3c6876c..4861e98f1170f 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,7 +35,7 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index 998d8906b6bd33f48199dc5a67bbd149742eb561..90015e0be79e850a9225263e90b662fa3fbe005f 100644 +index 6bf37c804ddb77a28e788af02bb215970ba343c8..4bf9d4050c44dd155b3455082c8b87afa30e9ac7 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -1031,6 +1031,7 @@ component("base") { @@ -449,10 +449,10 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec bool shouldShowWindowTitle = YES; if (_bridge) diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -index 9145261d8c3350e3df8844bf4109d9fda7b6ed46..b83c0faf8a6d4898e382d989348c8035086d0510 100644 +index aa59dfc5c689c6347a4a34fbce46ea549f46dc9d..29fa39513d0273d0a23fb45e627dda14b91c62d4 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -@@ -41,6 +41,7 @@ +@@ -42,6 +42,7 @@ #import "components/remote_cocoa/app_shim/views_nswindow_delegate.h" #import "components/remote_cocoa/app_shim/window_move_loop.h" #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" @@ -460,7 +460,7 @@ index 9145261d8c3350e3df8844bf4109d9fda7b6ed46..b83c0faf8a6d4898e382d989348c8035 #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/cert/x509_util_apple.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" -@@ -676,10 +677,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { +@@ -677,10 +678,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { // this should be treated as an error and caught early. CHECK(bridged_view_); @@ -579,7 +579,7 @@ index b712b8af0e770aa3acbeb1167b1a20bc1547c98a..fdb476a7e470c4b32649d4b3b7e4e445 return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 2413228f605776322caff19098c6b60183005df5..b392e1ee627c982c4c0b099a6b6ea8a9e698b601 100644 +index 4424636d013725023c2bf35529c034adecf823b1..62151d9e640771580ae85231762dfef260b0e4ff 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn @@ -339,6 +339,7 @@ source_set("browser") { @@ -792,7 +792,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn -index 482a1721cb2157f0221c901423423aab37efdaf0..b736432543ab619af6483070895f8968248fd71f 100644 +index 1163dd9e0d25b74fbdc584f742392a4452008724..246c50d5f05ecbf4f57dfc8f3bd68db03fe131f7 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -658,6 +658,7 @@ static_library("test_support") { @@ -819,7 +819,7 @@ index 482a1721cb2157f0221c901423423aab37efdaf0..b736432543ab619af6483070895f8968 ] if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) { -@@ -3263,6 +3266,7 @@ test("content_unittests") { +@@ -3264,6 +3267,7 @@ test("content_unittests") { "//ui/shell_dialogs:shell_dialogs", "//ui/webui:test_support", "//url", @@ -910,7 +910,7 @@ index 973cd337f2781271b4ca3e29db07939ec6917327..90658a336138e4e50f93c38f1c1f465c if (is_ios) { sources += [ "image_transport_surface_ios.mm" ] diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h -index 93665e7ec19efa8d94fe0bb4f195642f43432c24..23c25ce53e4e630e173777943770a09ee736cc3d 100644 +index ae040bbac8755b677dc6e19383a2390df407e5a6..e95ca30f49506c66a37d6d5269929f437005f863 100644 --- a/gpu/ipc/service/image_transport_surface_overlay_mac.h +++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h @@ -8,6 +8,7 @@ @@ -932,7 +932,7 @@ index 93665e7ec19efa8d94fe0bb4f195642f43432c24..23c25ce53e4e630e173777943770a09e namespace ui { diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn -index 87126a36725849cbaf478e2dc24dc3a628a30846..a3a88b07af91b86191d9e5727a1d021ebbbb22ce 100644 +index 7122165cf5ddb39056369e32fc29107f29e9f425..f11417e459e067f4976f59f1c58d4a5197520902 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn @@ -196,6 +196,7 @@ source_set("audio") { @@ -1463,10 +1463,10 @@ index d38e951cdbf1ab9b367273ad0fb8f962155a7f3b..e39ee0414cb54004096dbfffc4f768e8 blink_core_sources_editing += [ "kill_ring_none.cc" ] } diff --git a/ui/accelerated_widget_mac/BUILD.gn b/ui/accelerated_widget_mac/BUILD.gn -index 47e88da13d22b60e27b458f2fd33f10829b31fc0..b32ad8cd5af69c835ecfdbf50329347fb700f9b3 100644 +index ce3dc22f38b9d3f2f12e469b23f5dfe06ecbb98a..a69af46e05365028379dbf6abafaacd9b19e4569 100644 --- a/ui/accelerated_widget_mac/BUILD.gn +++ b/ui/accelerated_widget_mac/BUILD.gn -@@ -68,6 +68,7 @@ component("accelerated_widget_mac") { +@@ -77,6 +77,7 @@ component("accelerated_widget_mac") { "//ui/gfx", "//ui/gfx/geometry", "//ui/gl", @@ -1591,7 +1591,7 @@ index 8b25953be73da43fa2e0b5957569ae481dc6a082..7f9175e7eb67ef27fe110ee72f5e9c88 if (is_ios) { diff --git a/ui/accessibility/platform/browser_accessibility_manager_mac.mm b/ui/accessibility/platform/browser_accessibility_manager_mac.mm -index 444f315852deabbac971165a7de9751a1bb367e3..7aba9f251f869c64d7722558084f209bd5d27075 100644 +index c642b2ce1e19a48bc00822c429d833d5d45c4f98..ebe0b80b142a997aaf928fa72a2ca71492d1a54e 100644 --- a/ui/accessibility/platform/browser_accessibility_manager_mac.mm +++ b/ui/accessibility/platform/browser_accessibility_manager_mac.mm @@ -13,6 +13,7 @@ diff --git a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch index 51f58454f8bd5..fd07f9159c316 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -7,7 +7,7 @@ This adds a callback from the network service that's used to implement session.setCertificateVerifyCallback. diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 8529c98678d7a671cea947a864d0c13e031a153a..771fee7092bb25e4f7546941a04647d060e966c2 100644 +index b9a14ef983f6c985fefeee342eddfdf3f0a24a84..b1df0a09a9bfd226ffe7b37144e6599b099e619e 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -159,6 +159,11 @@ @@ -136,7 +136,7 @@ index 8529c98678d7a671cea947a864d0c13e031a153a..771fee7092bb25e4f7546941a04647d0 void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) { -@@ -2617,6 +2722,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( +@@ -2618,6 +2723,10 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( cert_verifier = std::make_unique( std::make_unique( std::move(cert_verifier))); @@ -178,7 +178,7 @@ index f2dcec57a22d95892a08f1fa43696d6eea46a820..930e0bd987c48d111b2c8d71147c09e4 std::unique_ptr internal_host_resolver_; std::set, base::UniquePtrComparator> diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 5cfbf71f644b391009d533132621a4ece224bfd3..773049b72e7784d396bb9cd1ebd29c3a523e7454 100644 +index 0f053267c23795be6bb0e75d10a92e1777532484..cf4d2dcf86b6536c37d46875f74517e478b34928 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom @@ -308,6 +308,17 @@ struct SocketBrokerRemotes { @@ -199,7 +199,7 @@ index 5cfbf71f644b391009d533132621a4ece224bfd3..773049b72e7784d396bb9cd1ebd29c3a // Parameters for constructing a network context. struct NetworkContextParams { // The user agent string. -@@ -939,6 +950,9 @@ interface NetworkContext { +@@ -945,6 +956,9 @@ interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote client); diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index 194ff0869c531..49a7656aeac03 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -133,7 +133,7 @@ index 05d3a12dd84c7005d46cc73b312f97ef418d96f5..4765de982802541b3efc7211d106acc7 const GURL& document_url, const WeakDocumentPtr& weak_document_ptr, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 5a13f11b6007eb87c59c5bdfaa02d1ab8f27a15e..c7025b0e43bcc9dd1e4b89dac39b5440e1a6ee30 100644 +index a8e3f365d2e9c72f9ae46388dc5b12ad57887b6b..bf18683a08e1c144b360f904ab65adcab66aa607 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -2146,7 +2146,7 @@ void RenderProcessHostImpl::CreateNotificationService( diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 03cc14405aa21..755e24fb055b1 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,7 +10,7 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 4ffe6251a941cb51b60a5d19fd6307b011a49f39..6f3d5528c289444c16691ea42a00b87f6f7faeaa 100644 +index 5de5748662c957f5b0e671057b827e2eb2224893..72ebb0e18dbc8c678ff93c7a3c028d7fb362263f 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -806,8 +806,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index 63e816414a6df..fe0c5162f7f29 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -881,10 +881,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index b392e1ee627c982c4c0b099a6b6ea8a9e698b601..a2e944cb987f739d75428649793e622400ed0213 100644 +index 62151d9e640771580ae85231762dfef260b0e4ff..52555181f779772dff8a471c4389af8128fd7a6a 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3123,8 +3123,9 @@ source_set("browser") { +@@ -3129,8 +3129,9 @@ source_set("browser") { "//ppapi/shared_impl", ] diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index 9b6a8dc58fcbe..1e0cb6d3513f6 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -30,7 +30,7 @@ index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9ad // RenderWidgetHost on the primary main frame, and false otherwise. virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index f13799f1cc440a20d8e3c55ee35f64e6505e5590..e46e5eb74c54bc00ead6e2e4eff99fd500e09262 100644 +index a409c64768968c81be3b6c7e7646f8df26e2afe6..7c31b82e2903507bb69aaa71fc0ed51cad06d1c2 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -2048,6 +2048,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { @@ -44,7 +44,7 @@ index f13799f1cc440a20d8e3c55ee35f64e6505e5590..e46e5eb74c54bc00ead6e2e4eff99fd5 void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 60ed731f7648497d62fbc7b43181a47a82743c4b..e84ef165670048dd2f030a48ff2d9796b5170b52 100644 +index e500fe77f55836b3e5832536f98cf9581984b8c1..952fd20f71560acd89c74f08e9d8cdbf34fb5a1a 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -5810,6 +5810,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { diff --git a/patches/chromium/refactor_expose_file_system_access_blocklist.patch b/patches/chromium/refactor_expose_file_system_access_blocklist.patch index e56a6c907c914..275fb4db72bf3 100644 --- a/patches/chromium/refactor_expose_file_system_access_blocklist.patch +++ b/patches/chromium/refactor_expose_file_system_access_blocklist.patch @@ -8,7 +8,7 @@ it in Electron and prevent drift from Chrome's blocklist. We should look for a w to upstream this change to Chrome. diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc -index 7d5813ad07169f2f779846c4541101eac376aa4c..21e17ef542a89a9829ca4583da760fb6b1c59836 100644 +index f08ff195ede9980cd0f491053510ed09c1b6c58a..2bdfcc05ef92c1983a9517deea98ed589ac0a065 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc @@ -45,7 +45,6 @@ diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index 87efe1bc232d9..5623dfa4c34b3 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index f3d223efcfa8f909e810ec43dd5ef90c9fc5c620..c48f9c00f129d1373ce64d33f83ce7a634433622 100644 +index 4325cdfd256ae7a1008e073d42da995b82df5bba..b895fb34f7fe7d48613a972dc29039c7d9c26987 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9905,25 +9905,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9909,25 +9909,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index f99653d131c0d..181389e7367f2 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,7 +6,7 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index 86399ed26f1ba6808d1f7bb0b1d7649df9da8901..b05f83c47f138f7040a175cc46dba99dcf054765 100644 +index e5701fe6ddcf8b9d846373d331e91e705870e523..a64b6d00637a177fd4f6d66b236bed9b0d8d4d8c 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -1310,7 +1310,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index 94de91d98d346..f8a580caef7f6 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,7 +22,7 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index c7025b0e43bcc9dd1e4b89dac39b5440e1a6ee30..30a3715e3dfa76e68d9a75742b1d085ec32a3fc3 100644 +index bf18683a08e1c144b360f904ab65adcab66aa607..829a0c2f09490099bb58ece6a41995e3fe3d6c81 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1754,6 +1754,10 @@ bool RenderProcessHostImpl::Init() { diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index 0ab51d0080fc9..a9064eadbde0c 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,7 +9,7 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 591c87ffc5f56b38d0f329da8b983a77af9662ee..b139a13c80eb9d356c78c587f823a4cb8452abe9 100644 +index 59b05937ceb1b81b69d913f587150021b9031106..97512399db631236afd1aeafb1ecee97cb754f60 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -3882,6 +3882,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index bc8e2416b958d..d3bab20f97d4d 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 37af686964489bd77d84be95d5f3aba9cd0a7a0c..4ffe6251a941cb51b60a5d19fd6307b011a49f39 100644 +index 27db5dbfd05106788feda9daab9d1aaaf1ae920f..5de5748662c957f5b0e671057b827e2eb2224893 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8739,6 +8739,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8745,6 +8745,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,7 +37,7 @@ index 37af686964489bd77d84be95d5f3aba9cd0a7a0c..4ffe6251a941cb51b60a5d19fd6307b0 if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index b139a13c80eb9d356c78c587f823a4cb8452abe9..6f323c5863f4bb869a66d265fac1d372c5243187 100644 +index 97512399db631236afd1aeafb1ecee97cb754f60..c3b563b0b727bc35f6d4499c589110644ebe9cd1 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -4157,21 +4157,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( @@ -88,7 +88,7 @@ index b139a13c80eb9d356c78c587f823a4cb8452abe9..6f323c5863f4bb869a66d265fac1d372 features::kAutomaticFullscreenContentSetting)) { // Ensure the window is made active to take input focus. The user may have diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc -index 983f8a795725a097adf4e13f7d9c24b7c16d3467..75f1b9b60460713da6d832a803cfc03d7c90c9ca 100644 +index 0a37c1f1eb49023a4a209e9664e0fadadb97e297..76c52a0528a100787b83ccd76f552f98216a5aaa 100644 --- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc +++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc @@ -110,7 +110,7 @@ void FullscreenElementChanged(Document& document, diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index bab3a8cbddbc3..ac939d791b229 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -26,10 +26,10 @@ index 7a2d251ba2d13d0a34df176111e6524a27b87f55..cbbe0fbdd25a0f7859b113fdb3dcd9ce // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 4b9fce8429d10cd002017128645ae8a54cd1f088..8ea2df6c0b543dec1309531573d2277b94353923 100644 +index d912ff1b05e9fe5c4d8edf1f681fc824b6f2d38a..92c28a027ebd10a3bb8a864231b237908fbf7394 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -897,6 +897,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -906,6 +906,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } @@ -43,10 +43,10 @@ index 4b9fce8429d10cd002017128645ae8a54cd1f088..8ea2df6c0b543dec1309531573d2277b const v8::Local& worker) { GetContentClient()->renderer()->DidInitializeWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index f726c03e34578032f5fc6c9a914ba72858efce89..9c5eeba6e24bb53fc112082c198a103256b06e32 100644 +index b6d525fb400ae27e04967c9b1b197e01f4a21123..5daf30bfb5cfc27148aa5f0d784ee8348c577dbf 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -196,6 +196,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; diff --git a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch index 9d9284333493b..aa28ee4c6fd73 100644 --- a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch +++ b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch @@ -35,10 +35,10 @@ index cbbe0fbdd25a0f7859b113fdb3dcd9ce57e597d6..1345bb5008e1b4fc3a450f7e353d52ec // from the worker thread. virtual void WillDestroyWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 8ea2df6c0b543dec1309531573d2277b94353923..3ac2e0783f9a7ab65d89352bf03e68f3b83c64b9 100644 +index 92c28a027ebd10a3bb8a864231b237908fbf7394..21ac02b10b698b943feb982743c09d4f1107c437 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -909,6 +909,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( +@@ -918,6 +918,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( worker); } @@ -52,10 +52,10 @@ index 8ea2df6c0b543dec1309531573d2277b94353923..3ac2e0783f9a7ab65d89352bf03e68f3 const blink::WebSecurityOrigin& script_origin) { return GetContentClient()->renderer()->AllowScriptExtensionForServiceWorker( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index 9c5eeba6e24bb53fc112082c198a103256b06e32..fad9cf104d62d4e19a2b27eff54d17f908357b41 100644 +index 5daf30bfb5cfc27148aa5f0d784ee8348c577dbf..182d574f14a679707e824e82bb4feeb4ee68de90 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -196,6 +196,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; diff --git a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch index 0c4c7e03a27ca..dcbe48b7fd685 100644 --- a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch +++ b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch @@ -10,10 +10,10 @@ to handle this without patching, but this is fairly clean for now and no longer patching legacy devtools code. diff --git a/front_end/entrypoints/main/MainImpl.ts b/front_end/entrypoints/main/MainImpl.ts -index 117f738fa274ffdb79e1666a5de322fb163d19b8..d8f512c9b4284571bc96285a64f8d0faf1826c52 100644 +index 75d54b60cb265eef90a493295dd9d495f8327b7c..2d98c8470115b70bda7d728ff4c00d2282159913 100644 --- a/front_end/entrypoints/main/MainImpl.ts +++ b/front_end/entrypoints/main/MainImpl.ts -@@ -760,6 +760,8 @@ export class MainImpl { +@@ -761,6 +761,8 @@ export class MainImpl { globalThis.Main = globalThis.Main || {}; // @ts-expect-error Exported for Tests.js globalThis.Main.Main = MainImpl; diff --git a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch index d812b9be9c90f..6c58105618a7e 100644 --- a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch +++ b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch @@ -46,10 +46,10 @@ index 3e57ae8efe33f326ef0e5d609c311d4be5b8afd6..dc521d39c2280dfc3217e97c1e413b2b V8_INLINE static void* GetAlignedPointerFromInternalField( const BasicTracedReference& object, int index) { diff --git a/src/api/api.cc b/src/api/api.cc -index 3e6a975f912cf482fbf668142080df7e9aa80455..64044e9cf44d401c249787feafb651688ee0d9f9 100644 +index c1ba8bb806927ec63af004a620768d736b122c4f..f4ed96bf45183c8a7453ebac994904c2ea7d9707 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -6322,14 +6322,33 @@ Local v8::Object::SlowGetInternalField(int index) { +@@ -6324,14 +6324,33 @@ Local v8::Object::SlowGetInternalField(int index) { i::Cast(*obj)->GetEmbedderField(index), isolate)); } diff --git a/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch b/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch index 3b74403b88001..199ce1061b774 100644 --- a/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch +++ b/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch @@ -50,10 +50,10 @@ index 97f1030dd2ca47ca4b58ac64e2e11e615bc46130..24ef6b5e0af63179e557b9896134838e /** * Stop this `Atomics.wait()` call and call the |AtomicsWaitCallback| diff --git a/src/api/api.cc b/src/api/api.cc -index 64044e9cf44d401c249787feafb651688ee0d9f9..1677e54b188b6a1699370d8cff37d2acf2933f38 100644 +index f4ed96bf45183c8a7453ebac994904c2ea7d9707..59327d4619661a138c407b468794e6a0f60a91e3 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -9876,6 +9876,16 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) { +@@ -9878,6 +9878,16 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) { i_isolate->heap()->SetEmbedderRootsHandler(handler); } @@ -71,7 +71,7 @@ index 64044e9cf44d401c249787feafb651688ee0d9f9..1677e54b188b6a1699370d8cff37d2ac const i::Isolate* i_isolate = reinterpret_cast(this); return i_isolate->heap()->cpp_heap(); diff --git a/src/heap/cppgc-js/cpp-heap.cc b/src/heap/cppgc-js/cpp-heap.cc -index e033791ca1cdeba4a304e69b922d4169a22f9caa..706f81f7bbc1b5a7a1b73afe018b0b2c0184d9ef 100644 +index a03e4d6fdb8acb2623434f08b1f63ff86cb1e77a..756e5f89a3b947761c01fc3cc59c654eb99836d7 100644 --- a/src/heap/cppgc-js/cpp-heap.cc +++ b/src/heap/cppgc-js/cpp-heap.cc @@ -513,6 +513,11 @@ CppHeap::CppHeap( @@ -87,10 +87,10 @@ index e033791ca1cdeba4a304e69b922d4169a22f9caa..706f81f7bbc1b5a7a1b73afe018b0b2c } diff --git a/src/heap/heap.cc b/src/heap/heap.cc -index cedf308a6042cc45241d4ee2731d2ee240ee8d9f..794c6380db4a669e7a83f6cce1db1dbe4fcde972 100644 +index 3823f26158a12d93636bb3376065ba7ce9d8d4d6..86cdcc8c3c4c09134db8d89f93fd3405cabcf385 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc -@@ -6065,6 +6065,21 @@ void Heap::AttachCppHeap(v8::CppHeap* cpp_heap) { +@@ -6070,6 +6070,21 @@ void Heap::AttachCppHeap(v8::CppHeap* cpp_heap) { cpp_heap_ = cpp_heap; } @@ -113,10 +113,10 @@ index cedf308a6042cc45241d4ee2731d2ee240ee8d9f..794c6380db4a669e7a83f6cce1db1dbe if (!embedder_stack_state_origin_) return {}; return embedder_stack_state_; diff --git a/src/heap/heap.h b/src/heap/heap.h -index 200a3bdcd563a54b9fbd6c7ca007fb940b1a9451..0946601a9c25a03a830fb6a8fb5389057bfe5d33 100644 +index 645340bda0137810497a9885b066e500b51428d4..b83def4691341c595dfba0b4f996345849ead640 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h -@@ -1108,6 +1108,9 @@ class Heap final { +@@ -1110,6 +1110,9 @@ class Heap final { // Unified heap (C++) support. =============================================== // =========================================================================== @@ -126,7 +126,7 @@ index 200a3bdcd563a54b9fbd6c7ca007fb940b1a9451..0946601a9c25a03a830fb6a8fb538905 v8::CppHeap* cpp_heap() const { return cpp_heap_; } std::optional overridden_stack_state() const; -@@ -1649,8 +1652,6 @@ class Heap final { +@@ -1651,8 +1654,6 @@ class Heap final { private: class AllocationTrackerForDebugging; diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 4f19581b97efc..e1778af45ff11 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -15,10 +15,12 @@ #include "base/task/sequenced_task_runner.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" +#include "content/public/browser/browser_context.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/pref_names.h" +#include "extensions/common/constants.h" #include "extensions/common/error_utils.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest_constants.h" @@ -95,7 +97,10 @@ ElectronExtensionLoader::ElectronExtensionLoader( content::BrowserContext* browser_context) : browser_context_(browser_context), extension_registrar_(ExtensionRegistrar::Get(browser_context)) { - extension_registrar_->SetDelegate(this); + extension_registrar_->Init( + this, /*extensions_enabled=*/true, + browser_context_->GetPath().AppendASCII(kInstallDirectoryName), + browser_context_->GetPath().AppendASCII(kUnpackedInstallDirectoryName)); } ElectronExtensionLoader::~ElectronExtensionLoader() = default; diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 16300fa2b6f87..8987ee026c9d0 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1221,7 +1221,7 @@ SkColor NativeWindowViews::GetBackgroundColor() const { auto* background = root_view_.background(); if (!background) return SK_ColorTRANSPARENT; - return background->get_color(); + return background->color().ConvertToSkColor(root_view_.GetColorProvider()); } void NativeWindowViews::SetBackgroundColor(SkColor background_color) { diff --git a/shell/browser/ui/views/menu_bar.cc b/shell/browser/ui/views/menu_bar.cc index 5e0b639ebc238..28a19854fe698 100644 --- a/shell/browser/ui/views/menu_bar.cc +++ b/shell/browser/ui/views/menu_bar.cc @@ -206,7 +206,8 @@ void MenuBar::ViewHierarchyChanged( const views::ViewHierarchyChangedDetails& details) { views::AccessiblePaneView::ViewHierarchyChanged(details); #if BUILDFLAG(IS_WIN) - background_color_ = GetBackground()->get_color(); + background_color_ = + GetBackground()->color().ConvertToSkColor(root_view_->GetColorProvider()); #endif } @@ -219,7 +220,8 @@ void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) { disabled_color_ = gtk::GetFgColor( "GtkMenuBar#menubar GtkMenuItem#menuitem:disabled GtkLabel"); #elif BUILDFLAG(IS_WIN) - background_color_ = GetBackground()->get_color(); + background_color_ = GetBackground()->color().ConvertToSkColor( + root_view_->GetColorProvider()); #endif } } diff --git a/shell/common/gin_converters/content_converter.cc b/shell/common/gin_converters/content_converter.cc index d5a257b8f24ac..122aa7d1e453a 100644 --- a/shell/common/gin_converters/content_converter.cc +++ b/shell/common/gin_converters/content_converter.cc @@ -223,6 +223,8 @@ v8::Local Converter::ToV8( return StringToV8(isolate, "speaker-selection"); case blink::PermissionType::WEB_APP_INSTALLATION: return StringToV8(isolate, "web-app-installation"); + case blink::PermissionType::LOCAL_NETWORK_ACCESS: + return StringToV8(isolate, "local-network-access"); case blink::PermissionType::DEPRECATED_SYNC_CLIPBOARD_READ: return StringToV8(isolate, "deprecated-sync-clipboard-read"); case blink::PermissionType::NUM: diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index ae1004554a9f1..7aee5f66f3d90 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -2924,7 +2924,7 @@ describe('font fallback', () => { } else if (process.platform === 'darwin') { expect(fonts[0].familyName).to.equal('Helvetica'); } else if (process.platform === 'linux') { - expect(fonts[0].familyName).to.equal('DejaVu Sans'); + expect(fonts[0].familyName).to.equal('DejaVu Sans (Fontations)'); } // I think this depends on the distro? We don't specify a default. }); From 33bde96d73d5fbc205d6723a652f5cce10c43392 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 12:54:11 -0500 Subject: [PATCH 044/339] refactor: reduce coupling in `electron::api::Protocol` (#46182) * refactor: decouple api::Protocol from ElectronBrowserContext now they do not know about each other Co-authored-by: Charles Kerr * refactor: make electron::api::ProtocolError private Co-authored-by: Charles Kerr * refactor: remove unused isolate arg in Protocol constructor Co-authored-by: Charles Kerr * refactor: use =default for trivial destructor Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_protocol.cc | 59 ++++++++++------------ shell/browser/api/electron_api_protocol.h | 49 +++++++++--------- shell/browser/api/electron_api_session.cc | 4 +- 3 files changed, 54 insertions(+), 58 deletions(-) diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 9e630ba493e7b..825b69aade676 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -14,7 +14,6 @@ #include "gin/handle.h" #include "gin/object_template_builder.h" #include "shell/browser/browser.h" -#include "shell/browser/electron_browser_context.h" #include "shell/browser/protocol_registry.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/net_converter.h" @@ -194,41 +193,39 @@ const char* const kBuiltinSchemes[] = { "about", "file", "http", "https", "data", "filesystem", }; +} // namespace + +Protocol::Protocol(ProtocolRegistry* protocol_registry) + : protocol_registry_{protocol_registry} {} + // Convert error code to string. -constexpr std::string_view ErrorCodeToString(ProtocolError error) { +// static +std::string_view Protocol::ErrorCodeToString(Error error) { switch (error) { - case ProtocolError::kRegistered: + case Error::kRegistered: return "The scheme has been registered"; - case ProtocolError::kNotRegistered: + case Error::kNotRegistered: return "The scheme has not been registered"; - case ProtocolError::kIntercepted: + case Error::kIntercepted: return "The scheme has been intercepted"; - case ProtocolError::kNotIntercepted: + case Error::kNotIntercepted: return "The scheme has not been intercepted"; default: return "Unexpected error"; } } -} // namespace - -Protocol::Protocol(v8::Isolate* isolate, ProtocolRegistry* protocol_registry) - : protocol_registry_(protocol_registry) {} - -Protocol::~Protocol() = default; - -ProtocolError Protocol::RegisterProtocol(ProtocolType type, - const std::string& scheme, - const ProtocolHandler& handler) { +Protocol::Error Protocol::RegisterProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler) { bool added = protocol_registry_->RegisterProtocol(type, scheme, handler); - return added ? ProtocolError::kOK : ProtocolError::kRegistered; + return added ? Error::kOK : Error::kRegistered; } bool Protocol::UnregisterProtocol(const std::string& scheme, gin::Arguments* args) { bool removed = protocol_registry_->UnregisterProtocol(scheme); - HandleOptionalCallback( - args, removed ? ProtocolError::kOK : ProtocolError::kNotRegistered); + HandleOptionalCallback(args, removed ? Error::kOK : Error::kNotRegistered); return removed; } @@ -236,18 +233,17 @@ bool Protocol::IsProtocolRegistered(const std::string& scheme) { return protocol_registry_->FindRegistered(scheme) != nullptr; } -ProtocolError Protocol::InterceptProtocol(ProtocolType type, - const std::string& scheme, - const ProtocolHandler& handler) { +Protocol::Error Protocol::InterceptProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler) { bool added = protocol_registry_->InterceptProtocol(type, scheme, handler); - return added ? ProtocolError::kOK : ProtocolError::kIntercepted; + return added ? Error::kOK : Error::kIntercepted; } bool Protocol::UninterceptProtocol(const std::string& scheme, gin::Arguments* args) { bool removed = protocol_registry_->UninterceptProtocol(scheme); - HandleOptionalCallback( - args, removed ? ProtocolError::kOK : ProtocolError::kNotIntercepted); + HandleOptionalCallback(args, removed ? Error::kOK : Error::kNotIntercepted); return removed; } @@ -275,15 +271,14 @@ v8::Local Protocol::IsProtocolHandled(const std::string& scheme, base::Contains(kBuiltinSchemes, scheme)); } -void Protocol::HandleOptionalCallback(gin::Arguments* args, - ProtocolError error) { +void Protocol::HandleOptionalCallback(gin::Arguments* args, Error error) { base::RepeatingCallback)> callback; if (args->GetNext(&callback)) { util::EmitWarning( args->isolate(), "The callback argument of protocol module APIs is no longer needed.", "ProtocolDeprecateCallback"); - if (error == ProtocolError::kOK) + if (error == Error::kOK) callback.Run(v8::Null(args->isolate())); else callback.Run(v8::Exception::Error( @@ -292,11 +287,9 @@ void Protocol::HandleOptionalCallback(gin::Arguments* args, } // static -gin::Handle Protocol::Create( - v8::Isolate* isolate, - ElectronBrowserContext* browser_context) { - return gin::CreateHandle( - isolate, new Protocol(isolate, browser_context->protocol_registry())); +gin::Handle Protocol::Create(v8::Isolate* isolate, + ProtocolRegistry* protocol_registry) { + return gin::CreateHandle(isolate, new Protocol{protocol_registry}); } // static diff --git a/shell/browser/api/electron_api_protocol.h b/shell/browser/api/electron_api_protocol.h index 273efc4dea80d..cfb648346958b 100644 --- a/shell/browser/api/electron_api_protocol.h +++ b/shell/browser/api/electron_api_protocol.h @@ -22,7 +22,6 @@ class Handle; namespace electron { -class ElectronBrowserContext; class ProtocolRegistry; namespace api { @@ -35,21 +34,12 @@ void AddServiceWorkerScheme(const std::string& scheme); void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, v8::Local val); -// Possible errors. -enum class ProtocolError { - kOK, // no error - kRegistered, - kNotRegistered, - kIntercepted, - kNotIntercepted, -}; - // Protocol implementation based on network services. class Protocol final : public gin::Wrappable, public gin_helper::Constructible { public: static gin::Handle Create(v8::Isolate* isolate, - ElectronBrowserContext* browser_context); + ProtocolRegistry* protocol_registry); // gin_helper::Constructible static gin::Handle New(gin_helper::ErrorThrower thrower); @@ -63,23 +53,34 @@ class Protocol final : public gin::Wrappable, const char* GetTypeName() override; private: - Protocol(v8::Isolate* isolate, ProtocolRegistry* protocol_registry); - ~Protocol() override; + // Possible errors. + enum class Error { + kOK, // no error + kRegistered, + kNotRegistered, + kIntercepted, + kNotIntercepted, + }; // Callback types. using CompletionCallback = base::RepeatingCallback)>; + explicit Protocol(ProtocolRegistry* protocol_registry); + ~Protocol() override = default; + + [[nodiscard]] static std::string_view ErrorCodeToString(Error error); + // JS APIs. - ProtocolError RegisterProtocol(ProtocolType type, - const std::string& scheme, - const ProtocolHandler& handler); + Error RegisterProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler); bool UnregisterProtocol(const std::string& scheme, gin::Arguments* args); bool IsProtocolRegistered(const std::string& scheme); - ProtocolError InterceptProtocol(ProtocolType type, - const std::string& scheme, - const ProtocolHandler& handler); + Error InterceptProtocol(ProtocolType type, + const std::string& scheme, + const ProtocolHandler& handler); bool UninterceptProtocol(const std::string& scheme, gin::Arguments* args); bool IsProtocolIntercepted(const std::string& scheme); @@ -92,21 +93,21 @@ class Protocol final : public gin::Wrappable, bool RegisterProtocolFor(const std::string& scheme, const ProtocolHandler& handler, gin::Arguments* args) { - auto result = RegisterProtocol(type, scheme, handler); + const auto result = RegisterProtocol(type, scheme, handler); HandleOptionalCallback(args, result); - return result == ProtocolError::kOK; + return result == Error::kOK; } template bool InterceptProtocolFor(const std::string& scheme, const ProtocolHandler& handler, gin::Arguments* args) { - auto result = InterceptProtocol(type, scheme, handler); + const auto result = InterceptProtocol(type, scheme, handler); HandleOptionalCallback(args, result); - return result == ProtocolError::kOK; + return result == Error::kOK; } // Be compatible with old interface, which accepts optional callback. - void HandleOptionalCallback(gin::Arguments* args, ProtocolError error); + void HandleOptionalCallback(gin::Arguments* args, Error error); // Weak pointer; the lifetime of the ProtocolRegistry is guaranteed to be // longer than the lifetime of this JS interface. diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 06664761261a6..0e3f41e2bbd52 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -553,7 +553,9 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context) SessionPreferences::CreateForBrowserContext(browser_context); - protocol_.Reset(isolate, Protocol::Create(isolate, browser_context).ToV8()); + protocol_.Reset( + isolate, + Protocol::Create(isolate, browser_context->protocol_registry()).ToV8()); browser_context->SetUserData( kElectronApiSessionKey, From 6927f649eea91ba8ab0055263590104d12dcd471 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:55:46 -0400 Subject: [PATCH 045/339] fix: remove `File.path` from types (#46177) * fix: remove File.path from types Co-authored-by: Ben Demboski Co-authored-by: Shelley Vohr * fixup! fix: remove File.path from types fix 'yarn install --frozen-lockfile' error Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr Co-authored-by: Charles Kerr --- docs/api/web-utils.md | 2 +- package.json | 2 +- yarn.lock | 17 +++++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/docs/api/web-utils.md b/docs/api/web-utils.md index 2162d9b36cbfc..54ff8c03ababb 100644 --- a/docs/api/web-utils.md +++ b/docs/api/web-utils.md @@ -16,7 +16,7 @@ Returns `string` - The file system path that this `File` object points to. In th This method superseded the previous augmentation to the `File` object with the `path` property. An example is included below. -```js +```js @ts-nocheck // Before const oldPath = document.querySelector('input').files[0].path diff --git a/package.json b/package.json index 12b3f777d26e6..d27cc1b4dd847 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@electron/fiddle-core": "^1.3.4", "@electron/github-app-auth": "^2.2.1", "@electron/lint-roller": "^2.4.0", - "@electron/typescript-definitions": "^9.0.0", + "@electron/typescript-definitions": "^9.1.2", "@octokit/rest": "^20.0.2", "@primer/octicons": "^10.0.0", "@types/minimist": "^1.2.5", diff --git a/yarn.lock b/yarn.lock index f7cfa58f0c4c6..cb940d319816e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -285,10 +285,10 @@ vscode-uri "^3.0.7" yaml "^2.4.5" -"@electron/typescript-definitions@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-9.0.0.tgz#1a8d9f36711ad93643af0662eef917189725c354" - integrity sha512-sK/e5ewiHZnpy0jzFW2NmH8KATkprwG962JzxJYw/GphxG/V55mP7UPJirmYUPeOA87TWhL910sjp5gdZ1SQmg== +"@electron/typescript-definitions@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-9.1.2.tgz#a9b7bfaed60a528cf1f0ce4a30f01360a27839f2" + integrity sha512-BLxuLnvGqKUdesLXh9jB6Ll5Q4Vnb0NqJxuNY+GBz5Q8icxpW2EcHO7gIBpgX+t6sHdfRn9r6Wpwh/CKXoaJng== dependencies: "@types/node" "^20.11.25" chalk "^5.3.0" @@ -7042,14 +7042,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 4b0d49898fc67cc99abac9d73988e70d1541c1ba Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:55:55 -0400 Subject: [PATCH 046/339] build: combine pipewire patches (#46145) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/webrtc/.patches | 4 +- ...nit_before_creating_generic_capturer.patch | 23 ---- ..._fallback_to_x11_capturer_on_wayland.patch | 58 ---------- ...pturer_initialization_and_management.patch | 100 ++++++++++++++++++ ...er_as_failed_after_session_is_closed.patch | 26 ----- 5 files changed, 101 insertions(+), 110 deletions(-) delete mode 100644 patches/webrtc/fix_check_pipewire_init_before_creating_generic_capturer.patch delete mode 100644 patches/webrtc/fix_fallback_to_x11_capturer_on_wayland.patch create mode 100644 patches/webrtc/fix_handle_pipewire_capturer_initialization_and_management.patch delete mode 100644 patches/webrtc/fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch diff --git a/patches/webrtc/.patches b/patches/webrtc/.patches index 63f7e05ecd6b6..1dd0c816a6743 100644 --- a/patches/webrtc/.patches +++ b/patches/webrtc/.patches @@ -1,3 +1 @@ -fix_fallback_to_x11_capturer_on_wayland.patch -fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch -fix_check_pipewire_init_before_creating_generic_capturer.patch +fix_handle_pipewire_capturer_initialization_and_management.patch diff --git a/patches/webrtc/fix_check_pipewire_init_before_creating_generic_capturer.patch b/patches/webrtc/fix_check_pipewire_init_before_creating_generic_capturer.patch deleted file mode 100644 index be76ebeb32675..0000000000000 --- a/patches/webrtc/fix_check_pipewire_init_before_creating_generic_capturer.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Athul Iddya -Date: Tue, 12 Sep 2023 22:19:46 -0700 -Subject: fix: check PipeWire init before creating generic capturer - -Check if PipeWire can be initialized before creating generic capturer. -This harmonizes the conditions with the ones used in Linux -implementations of DesktopCapturer::CreateRawScreenCapturer and -DesktopCapturer::CreateRawWindowCapturer. - -diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc -index 7fd0fc31d81bf4d5eca5f8aa7106388ea4c518e4..51dde063a78be7aade1953fbee8bb2db71b72ce5 100644 ---- a/modules/desktop_capture/desktop_capturer.cc -+++ b/modules/desktop_capture/desktop_capturer.cc -@@ -113,7 +113,7 @@ std::unique_ptr DesktopCapturer::CreateGenericCapturer( - std::unique_ptr capturer; - - #if defined(WEBRTC_USE_PIPEWIRE) -- if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { -+ if (options.allow_pipewire() && BaseCapturerPipeWire::IsSupported()) { - capturer = std::make_unique( - options, CaptureType::kAnyScreenContent); - } diff --git a/patches/webrtc/fix_fallback_to_x11_capturer_on_wayland.patch b/patches/webrtc/fix_fallback_to_x11_capturer_on_wayland.patch deleted file mode 100644 index 7db9b2393ad0a..0000000000000 --- a/patches/webrtc/fix_fallback_to_x11_capturer_on_wayland.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: VerteDinde -Date: Sun, 5 Mar 2023 21:04:37 -0800 -Subject: fix: fallback to X11 capturer on Wayland - -CL: https://webrtc-review.googlesource.com/c/src/+/279163 - -Desktop Capturer behaves inconsistently on Wayland. PipeWire does not -always successfully start; if it does not, we return a nullptr rather -than falling back on the X11 capturer, crashing the application. - -If the X11 capturer is enabled, we should at minimum try to fallback -to X11 for desktop capturer. This patch re-enables that fallback, -which was previously default behavior. - -This patch can be removed when 1) this fix is upstreamed, or 2) the -stability of PipeWire initialization is improved upstream. - -Patch_Filename: fix_fallback_to_x11_desktop_capturer_wayland.patch - -diff --git a/modules/desktop_capture/screen_capturer_linux.cc b/modules/desktop_capture/screen_capturer_linux.cc -index 44993837e8bbd84a11ec9d187349a95f83258910..cd9f8b0be6a19d057fe9150382fb72bc4032b343 100644 ---- a/modules/desktop_capture/screen_capturer_linux.cc -+++ b/modules/desktop_capture/screen_capturer_linux.cc -@@ -34,11 +34,10 @@ std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( - #endif // defined(WEBRTC_USE_PIPEWIRE) - - #if defined(WEBRTC_USE_X11) -- if (!DesktopCapturer::IsRunningUnderWayland()) -- return ScreenCapturerX11::CreateRawScreenCapturer(options); --#endif // defined(WEBRTC_USE_X11) -- -+ return ScreenCapturerX11::CreateRawScreenCapturer(options); -+#else - return nullptr; -+#endif // defined(WEBRTC_USE_X11) - } - - } // namespace webrtc -diff --git a/modules/desktop_capture/window_capturer_linux.cc b/modules/desktop_capture/window_capturer_linux.cc -index 4205bf9bc0eede48cdc39353c77ceb6e7529fd51..785dc01a1911fd027401b1461223668333e05558 100644 ---- a/modules/desktop_capture/window_capturer_linux.cc -+++ b/modules/desktop_capture/window_capturer_linux.cc -@@ -34,11 +34,10 @@ std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( - #endif // defined(WEBRTC_USE_PIPEWIRE) - - #if defined(WEBRTC_USE_X11) -- if (!DesktopCapturer::IsRunningUnderWayland()) -- return WindowCapturerX11::CreateRawWindowCapturer(options); --#endif // defined(WEBRTC_USE_X11) -- -+ return WindowCapturerX11::CreateRawWindowCapturer(options); -+#else - return nullptr; -+#endif // defined(WEBRTC_USE_X11) - } - - } // namespace webrtc diff --git a/patches/webrtc/fix_handle_pipewire_capturer_initialization_and_management.patch b/patches/webrtc/fix_handle_pipewire_capturer_initialization_and_management.patch new file mode 100644 index 0000000000000..4b18538c8dc98 --- /dev/null +++ b/patches/webrtc/fix_handle_pipewire_capturer_initialization_and_management.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Athul Iddya +Date: Tue, 12 Sep 2023 22:19:46 -0700 +Subject: fix: Handle PipeWire capturer initialization and management + +This patch handles several fixes related to PipeWire capturer initialization +and management. + +1. Mark PipeWire capturer as failed after session is closed + +After a PipeWire screencast session is successfully started, the +consumer is expected to keep calling CaptureFrame() from the +DesktopCapturer interface in a loop to pull frames. A PipeWire +screencast session can be closed by the server on user action. Once the +session is closed, there's no point in calling CaptureFrame() again as +the capture has to be restarted. Inform the caller of the failure by +returning Result::ERROR_PERMANENT on the next invocation of +CaptureFrame(). + +2. Check PipeWire init before creating generic capturer + +Check if PipeWire can be initialized before creating generic capturer. +This harmonizes the conditions with the ones used in Linux +implementations of DesktopCapturer::CreateRawScreenCapturer and +DesktopCapturer::CreateRawWindowCapturer. + +3. Establishes fallback to X11 capturer when PipeWire fails to start + +CL: https://webrtc-review.googlesource.com/c/src/+/279163 + +Desktop Capturer behaves inconsistently on Wayland. PipeWire does not +always successfully start; if it does not, we return a nullptr rather +than falling back on the X11 capturer, crashing the application. If the +X11 capturer is enabled, we should at minimum try to fallback to X11 +for desktop capturer. This change re-enables that fallback, which was +previously default behavior. + +diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc +index 7fd0fc31d81bf4d5eca5f8aa7106388ea4c518e4..51dde063a78be7aade1953fbee8bb2db71b72ce5 100644 +--- a/modules/desktop_capture/desktop_capturer.cc ++++ b/modules/desktop_capture/desktop_capturer.cc +@@ -113,7 +113,7 @@ std::unique_ptr DesktopCapturer::CreateGenericCapturer( + std::unique_ptr capturer; + + #if defined(WEBRTC_USE_PIPEWIRE) +- if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { ++ if (options.allow_pipewire() && BaseCapturerPipeWire::IsSupported()) { + capturer = std::make_unique( + options, CaptureType::kAnyScreenContent); + } +diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc +index 81caa9bd2d97ec73120c07c17d7290b2c3c5d598..3ba5267bf5c7de420131b2c14fcadffd1f5b1326 100644 +--- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc ++++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc +@@ -111,6 +111,7 @@ void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result, + void BaseCapturerPipeWire::OnScreenCastSessionClosed() { + if (!capturer_failed_) { + options_.screencast_stream()->StopScreenCastStream(); ++ capturer_failed_ = true; + } + capturer_failed_ = true; + } +diff --git a/modules/desktop_capture/screen_capturer_linux.cc b/modules/desktop_capture/screen_capturer_linux.cc +index 44993837e8bbd84a11ec9d187349a95f83258910..cd9f8b0be6a19d057fe9150382fb72bc4032b343 100644 +--- a/modules/desktop_capture/screen_capturer_linux.cc ++++ b/modules/desktop_capture/screen_capturer_linux.cc +@@ -34,11 +34,10 @@ std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( + #endif // defined(WEBRTC_USE_PIPEWIRE) + + #if defined(WEBRTC_USE_X11) +- if (!DesktopCapturer::IsRunningUnderWayland()) +- return ScreenCapturerX11::CreateRawScreenCapturer(options); +-#endif // defined(WEBRTC_USE_X11) +- ++ return ScreenCapturerX11::CreateRawScreenCapturer(options); ++#else + return nullptr; ++#endif // defined(WEBRTC_USE_X11) + } + + } // namespace webrtc +diff --git a/modules/desktop_capture/window_capturer_linux.cc b/modules/desktop_capture/window_capturer_linux.cc +index 4205bf9bc0eede48cdc39353c77ceb6e7529fd51..785dc01a1911fd027401b1461223668333e05558 100644 +--- a/modules/desktop_capture/window_capturer_linux.cc ++++ b/modules/desktop_capture/window_capturer_linux.cc +@@ -34,11 +34,10 @@ std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( + #endif // defined(WEBRTC_USE_PIPEWIRE) + + #if defined(WEBRTC_USE_X11) +- if (!DesktopCapturer::IsRunningUnderWayland()) +- return WindowCapturerX11::CreateRawWindowCapturer(options); +-#endif // defined(WEBRTC_USE_X11) +- ++ return WindowCapturerX11::CreateRawWindowCapturer(options); ++#else + return nullptr; ++#endif // defined(WEBRTC_USE_X11) + } + + } // namespace webrtc diff --git a/patches/webrtc/fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch b/patches/webrtc/fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch deleted file mode 100644 index 336b687a0d7e6..0000000000000 --- a/patches/webrtc/fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Athul Iddya -Date: Sat, 22 Jul 2023 11:11:01 -0700 -Subject: fix: mark PipeWire capturer as failed after session is closed - -After a PipeWire screencast session is successfully started, the -consumer is expected to keep calling CaptureFrame() from the -DesktopCapturer interface in a loop to pull frames. A PipeWire -screencast session can be closed by the server on user action. Once the -session is closed, there's no point in calling CaptureFrame() again as -the capture has to be restarted. Inform the caller of the failure by -returning Result::ERROR_PERMANENT on the next invocation of -CaptureFrame(). - -diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc -index 81caa9bd2d97ec73120c07c17d7290b2c3c5d598..3ba5267bf5c7de420131b2c14fcadffd1f5b1326 100644 ---- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc -+++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc -@@ -111,6 +111,7 @@ void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result, - void BaseCapturerPipeWire::OnScreenCastSessionClosed() { - if (!capturer_failed_) { - options_.screencast_stream()->StopScreenCastStream(); -+ capturer_failed_ = true; - } - capturer_failed_ = true; - } From 7f85c85b7152d312e89daa68b2d59e9b0d61c5d7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 10:44:04 -0500 Subject: [PATCH 047/339] fix: add missing `cpp_heap` to Node.js worker `CreateParams` (#46176) fix: add missing cpp_heap to Node.js worker CreateParams Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- ..._attach_cppgc_heap_on_v8_isolate_creation.patch | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch index 15b57e6905062..1dce7f1de16f0 100644 --- a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -165,10 +165,20 @@ index 4119ac1b002681d39711eac810ca2fcc2702ffc7..790347056cde949ffe6cf8498a7eca0c ExitCode NodeMainInstance::Run() { diff --git a/src/node_worker.cc b/src/node_worker.cc -index 1fc3774948dae3c0aae7d2aef563e18ecd4243a3..a610ee24ff18bddc3849aec3a43c2037b9ab5d53 100644 +index 1fc3774948dae3c0aae7d2aef563e18ecd4243a3..9d35cbf3dff538f38e8d5b8660d40c1fbaa56474 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc -@@ -230,13 +230,8 @@ class WorkerThreadData { +@@ -162,6 +162,9 @@ class WorkerThreadData { + SetIsolateCreateParamsForNode(¶ms); + w->UpdateResourceConstraints(¶ms.constraints); + params.array_buffer_allocator_shared = allocator; ++ params.cpp_heap = ++ v8::CppHeap::Create(w->platform_, v8::CppHeapCreateParams{{}}) ++ .release(); + Isolate* isolate = + NewIsolate(¶ms, &loop_, w->platform_, w->snapshot_data()); + if (isolate == nullptr) { +@@ -230,13 +233,8 @@ class WorkerThreadData { *static_cast(data) = true; }, &platform_finished); From dea35330c08e0c4e8e09145de7cd1f8800860c07 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 11:10:54 -0500 Subject: [PATCH 048/339] refactor: use `= default` to define trivial destructors (#46190) refactor: use '= default' to define trivial destructors Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_view.cc | 2 +- .../extensions/api/extension_action/extension_action_api.cc | 6 ------ .../extensions/api/extension_action/extension_action_api.h | 6 +++--- shell/browser/net/web_request_api_interface.h | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/shell/browser/api/electron_api_view.cc b/shell/browser/api/electron_api_view.cc index 2268674b3ae4f..ef37cb63c2db1 100644 --- a/shell/browser/api/electron_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -155,7 +155,7 @@ class JSLayoutManager : public views::LayoutManagerBase { public: explicit JSLayoutManager(LayoutCallback layout_callback) : layout_callback_(std::move(layout_callback)) {} - ~JSLayoutManager() override {} + ~JSLayoutManager() override = default; // views::LayoutManagerBase views::ProposedLayout CalculateProposedLayout( diff --git a/shell/browser/extensions/api/extension_action/extension_action_api.cc b/shell/browser/extensions/api/extension_action/extension_action_api.cc index 537b0f9d1a7d1..a57fef8aee609 100644 --- a/shell/browser/extensions/api/extension_action/extension_action_api.cc +++ b/shell/browser/extensions/api/extension_action/extension_action_api.cc @@ -29,8 +29,6 @@ void ExtensionActionAPI::Observer::OnExtensionActionUpdated( void ExtensionActionAPI::Observer::OnExtensionActionAPIShuttingDown() {} -ExtensionActionAPI::Observer::~Observer() {} - // // ExtensionActionAPI // @@ -38,8 +36,6 @@ ExtensionActionAPI::Observer::~Observer() {} ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context) : browser_context_(context), extension_prefs_(nullptr) {} -ExtensionActionAPI::~ExtensionActionAPI() {} - // static BrowserContextKeyedAPIFactory* ExtensionActionAPI::GetFactoryInstance() { @@ -65,8 +61,6 @@ void ExtensionActionAPI::Shutdown() {} ExtensionActionFunction::ExtensionActionFunction() {} -ExtensionActionFunction::~ExtensionActionFunction() {} - ExtensionFunction::ResponseAction ExtensionActionFunction::Run() { return RunExtensionAction(); } diff --git a/shell/browser/extensions/api/extension_action/extension_action_api.h b/shell/browser/extensions/api/extension_action/extension_action_api.h index 61f9ecbbabaae..4f4a557de0f65 100644 --- a/shell/browser/extensions/api/extension_action/extension_action_api.h +++ b/shell/browser/extensions/api/extension_action/extension_action_api.h @@ -33,7 +33,7 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI { virtual void OnExtensionActionAPIShuttingDown(); protected: - virtual ~Observer(); + virtual ~Observer() = default; }; explicit ExtensionActionAPI(content::BrowserContext* context); @@ -41,7 +41,7 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI { ExtensionActionAPI(const ExtensionActionAPI&) = delete; ExtensionActionAPI& operator=(const ExtensionActionAPI&) = delete; - ~ExtensionActionAPI() override; + ~ExtensionActionAPI() override = default; // Convenience method to get the instance for a profile. static ExtensionActionAPI* Get(content::BrowserContext* context); @@ -86,7 +86,7 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI { class ExtensionActionFunction : public ExtensionFunction { protected: ExtensionActionFunction(); - ~ExtensionActionFunction() override; + ~ExtensionActionFunction() override = default; // ExtensionFunction ResponseAction Run() override; diff --git a/shell/browser/net/web_request_api_interface.h b/shell/browser/net/web_request_api_interface.h index 5541761d73398..d93a6aa94391e 100644 --- a/shell/browser/net/web_request_api_interface.h +++ b/shell/browser/net/web_request_api_interface.h @@ -20,7 +20,7 @@ namespace electron { // Defines the interface for WebRequest API, implemented by api::WebRequestNS. class WebRequestAPI { public: - virtual ~WebRequestAPI() {} + virtual ~WebRequestAPI() = default; using BeforeSendHeadersCallback = base::OnceCallback& removed_headers, From 7e0d84f19cd59ba9deb1dd7580c23e73b0005fd0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 13:27:02 -0500 Subject: [PATCH 049/339] refactor: remove unused method `ElectronBrowserContext::GetWeakPtr()` (#46199) * refactor: use forward declaration of MediaDeviceIDSalt in electron_browser_context.h Co-authored-by: Charles Kerr * refactor: remove unused #include from browser_context.h Co-authored-by: Charles Kerr * refactor: remove unused ElectronBrowserContext::GetWeakPtr() last use removed in Aug 2020 by bac2f46 Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_context.cc | 1 + shell/browser/electron_browser_context.h | 13 +++---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 24eba93bd7b5d..933f783fa3745 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -50,6 +50,7 @@ #include "shell/browser/electron_download_manager_delegate.h" #include "shell/browser/electron_permission_manager.h" #include "shell/browser/file_system_access/file_system_access_permission_context_factory.h" +#include "shell/browser/media/media_device_id_salt.h" #include "shell/browser/net/resolve_proxy_helper.h" #include "shell/browser/protocol_registry.h" #include "shell/browser/serial/serial_chooser_context.h" diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 2985861825117..974be551a1e9d 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -13,11 +13,8 @@ #include #include -#include "base/memory/weak_ptr.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/media_stream_request.h" -#include "content/public/browser/resource_context.h" -#include "electron/shell/browser/media/media_device_id_salt.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/ssl_config.mojom.h" #include "third_party/blink/public/common/permissions/permission_utils.h" @@ -43,12 +40,13 @@ class SpecialStoragePolicy; namespace electron { +class CookieChangeNotifier; class ElectronDownloadManagerDelegate; class ElectronPermissionManager; -class CookieChangeNotifier; +class MediaDeviceIDSalt; +class ProtocolRegistry; class ResolveProxyHelper; class WebViewManager; -class ProtocolRegistry; using DisplayMediaResponseCallbackJs = base::OnceCallback; @@ -127,9 +125,6 @@ class ElectronBrowserContext : public content::BrowserContext { ValueMapPrefStore* in_memory_pref_store() const { return in_memory_pref_store_.get(); } - base::WeakPtr GetWeakPtr() { - return weak_factory_.GetWeakPtr(); - } ProtocolRegistry* protocol_registry() const { return protocol_registry_.get(); @@ -215,8 +210,6 @@ class ElectronBrowserContext : public content::BrowserContext { // In-memory cache that holds objects that have been granted permissions. DevicePermissionMap granted_devices_; - - base::WeakPtrFactory weak_factory_{this}; }; } // namespace electron From c4e0cae3eef4a9341e29ae91e0eae706a6eded7d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 13:37:38 -0500 Subject: [PATCH 050/339] feat: add ffmpeg.dll to delay load configuration (#46173) feat: set ffmpeg.dll as a delay-loaded DLL Updated the /DELAYLOAD linker config in BUILD.gn to set ffmpeg.dll as a delay-loaded DLL. This reduces startup overhead and prevents unnecessary loading when ffmpeg-related functionality is not used (e.g., the browser process was unnecessarily loading it). Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Zonglong Liu <83216456+mai-121@users.noreply.github.com> --- BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.gn b/BUILD.gn index a449ee53b5717..a64f6f57154b0 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1246,7 +1246,7 @@ if (is_mac) { "//components/crash/core/app:run_as_crashpad_handler", ] - ldflags = [] + ldflags = [ "/DELAYLOAD:ffmpeg.dll" ] libs = [ "comctl32.lib", From 6d2c7c2ab37fc1d1872f21702ab08c9d5e27b14e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 23 Mar 2025 10:14:26 +0100 Subject: [PATCH 051/339] build: Use windows src cache (#46188) build: use source cache on windows Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/checkout/action.yml | 48 ++++--- .github/actions/cipd-install/action.yml | 40 ++++++ .github/actions/fix-sync-macos/action.yml | 61 --------- .github/actions/fix-sync/action.yml | 120 ++++++++++++++++++ .../actions/install-build-tools/action.yml | 1 + .github/actions/restore-cache-aks/action.yml | 10 +- .../actions/restore-cache-azcopy/action.yml | 65 ++++++++-- .github/workflows/build.yml | 15 ++- .github/workflows/clean-src-cache.yml | 4 + .../pipeline-segment-electron-build.yml | 41 ++---- .../pipeline-segment-electron-gn-check.yml | 8 +- .../pipeline-segment-node-nan-test.yml | 22 +--- 12 files changed, 281 insertions(+), 154 deletions(-) create mode 100644 .github/actions/cipd-install/action.yml delete mode 100644 .github/actions/fix-sync-macos/action.yml create mode 100644 .github/actions/fix-sync/action.yml diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 2ca2dafe3874d..a9fcd90022283 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -9,6 +9,8 @@ inputs: description: 'Whether to persist the cache to the shared drive' required: false default: 'true' + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: @@ -22,36 +24,30 @@ runs: uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - - name: Get Depot Tools - shell: bash - run: | - if [[ ! -d depot_tools ]]; then - git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git - - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - fi - - name: Add Depot Tools to PATH - shell: bash - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Generate DEPS Hash shell: bash run: | node src/electron/script/generate-deps-hash.js - echo "DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)" >> $GITHUB_ENV + DEPSHASH="v1-src-cache-$(cat src/electron/.depshash)" + echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV + echo "CACHE_FILE=$DEPSHASH.tar" >> $GITHUB_ENV + if [ "${{ inputs.target-platform }}" = "win" ]; then + echo "CACHE_DRIVE=/mnt/win-cache" >> $GITHUB_ENV + else + echo "CACHE_DRIVE=/mnt/cross-instance-cache" >> $GITHUB_ENV + fi - name: Generate SAS Key if: ${{ inputs.generate-sas-token == 'true' }} shell: bash run: | - curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$DEPSHASH.tar" > sas-token + curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}" > sas-token - name: Save SAS Key if: ${{ inputs.generate-sas-token == 'true' }} - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-${{ github.run_attempt }} + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} + enableCrossOsArchive: true - name: Check If Cache Exists id: check-cache shell: bash @@ -60,7 +56,7 @@ runs: echo "Not using cache this time..." echo "cache_exists=false" >> $GITHUB_OUTPUT else - cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + cache_path=$CACHE_DRIVE/$CACHE_FILE echo "Using cache key: $DEPSHASH" echo "Checking for cache in: $cache_path" if [ ! -f "$cache_path" ] || [ `du $cache_path | cut -f1` = "0" ]; then @@ -76,8 +72,8 @@ runs: shell: bash run: | # if there is less than 35 GB free space then creating the cache might fail so exit early - freespace=`df -m /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` - freespace_human=`df -h /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` + freespace=`df -m $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + freespace_human=`df -h $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` if [ $freespace -le 35000 ]; then echo "The cross mount cache has $freespace_human free space which is not enough - exiting" exit 1 @@ -167,14 +163,14 @@ runs: shell: bash run: | echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')" - tar -cf $DEPSHASH.tar src - echo "Compressed src to $(du -sh $DEPSHASH.tar | cut -f1 -d' ')" - cp ./$DEPSHASH.tar /mnt/cross-instance-cache/ + tar -cf $CACHE_FILE src + echo "Compressed src to $(du -sh $CACHE_FILE | cut -f1 -d' ')" + cp ./$CACHE_FILE $CACHE_DRIVE/ - name: Persist Src Cache if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }} shell: bash run: | - final_cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + final_cache_path=$CACHE_DRIVE/$CACHE_FILE echo "Using cache key: $DEPSHASH" echo "Checking path: $final_cache_path" if [ ! -f "$final_cache_path" ]; then diff --git a/.github/actions/cipd-install/action.yml b/.github/actions/cipd-install/action.yml new file mode 100644 index 0000000000000..327e904be473e --- /dev/null +++ b/.github/actions/cipd-install/action.yml @@ -0,0 +1,40 @@ +name: 'CIPD install' +description: 'Installs the specified CIPD package' +inputs: + cipd-root-prefix-path: + description: 'Path to prepend to installation directory' + default: '' + dependency: + description: 'Name of dependency to install' + deps-file: + description: 'Location of DEPS file that defines the dependency' + installation-dir: + description: 'Location to install dependency' + target-platform: + description: 'Target platform, should be linux, win, macos' + package: + description: 'Package to install' +runs: + using: "composite" + steps: + - name: Delete wrong ${{ inputs.dependency }} + shell: bash + run : | + rm -rf ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} + - name: Create ensure file for ${{ inputs.dependency }} + shell: bash + run: | + echo '${{ inputs.package }}' `e d gclient getdep --deps-file=${{ inputs.deps-file }} -r '${{ inputs.installation-dir }}:${{ inputs.package }}'` > ${{ inputs.dependency }}_ensure_file + cat ${{ inputs.dependency }}_ensure_file + - name: CIPD installation of ${{ inputs.dependency }} (macOS) + if: ${{ inputs.target-platform == 'macos' }} + shell: bash + run: | + echo "ensuring ${{ inputs.dependency }} on macOS" + e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file + - name: CIPD installation of ${{ inputs.dependency }} (Windows) + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + echo "ensuring ${{ inputs.dependency }} on Windows" + e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file diff --git a/.github/actions/fix-sync-macos/action.yml b/.github/actions/fix-sync-macos/action.yml deleted file mode 100644 index e9e2c922280bd..0000000000000 --- a/.github/actions/fix-sync-macos/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: 'Fix Sync macOS' -description: 'Checks out Electron and stores it in the AKS Cache' -runs: - using: "composite" - steps: - - name: Fix Sync - shell: bash - # This step is required to correct for differences between "gclient sync" - # on Linux and the expected state on macOS. This requires: - # 1. Fixing Clang Install (wrong binary) - # 2. Fixing esbuild (wrong binary) - # 3. Fixing rustc (wrong binary) - # 4. Fixing gn (wrong binary) - # 5. Fix reclient (wrong binary) - # 6. Fixing dsymutil (wrong binary) - # 7. Ensuring we are using the correct ninja and adding it to PATH - # 8. Fixing angle (wrong remote) - run : | - SEDOPTION="-i ''" - rm -rf src/third_party/llvm-build - python3 src/tools/clang/scripts/update.py - - echo 'infra/3pp/tools/esbuild/${platform}' `gclient getdep --deps-file=src/third_party/devtools-frontend/src/DEPS -r 'third_party/esbuild:infra/3pp/tools/esbuild/${platform}'` > esbuild_ensure_file - # Remove extra output from calling gclient getdep which always calls update_depot_tools - sed -i '' "s/Updating depot_tools... //g" esbuild_ensure_file - cipd ensure --root src/third_party/devtools-frontend/src/third_party/esbuild -ensure-file esbuild_ensure_file - - rm -rf src/third_party/rust-toolchain - python3 src/tools/rust/update_rust.py - - # Prevent calling gclient getdep which always calls update_depot_tools - echo 'gn/gn/mac-${arch}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/mac:gn/gn/mac-${arch}'` > gn_ensure_file - sed -i '' "s/Updating depot_tools... //g" gn_ensure_file - cipd ensure --root src/buildtools/mac -ensure-file gn_ensure_file - - # Prevent calling gclient getdep which always calls update_depot_tools - echo 'infra/rbe/client/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/buildtools/reclient:infra/rbe/client/${platform}'` > gn_ensure_file - sed -i '' "s/Updating depot_tools... //g" gn_ensure_file - cipd ensure --root src/buildtools/reclient -ensure-file gn_ensure_file - python3 src/buildtools/reclient_cfgs/configure_reclient_cfgs.py --rbe_instance "projects/rbe-chrome-untrusted/instances/default_instance" --reproxy_cfg_template reproxy.cfg.template --rewrapper_cfg_project "" --skip_remoteexec_cfg_fetch - - if [ "${{ env.TARGET_ARCH }}" == "arm64" ]; then - DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1 - else - DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.x64.sha1 - fi - python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang -s $DSYM_SHA_FILE -o src/tools/clang/dsymutil/bin/dsymutil - - echo 'infra/3pp/tools/ninja/${platform}' `gclient getdep --deps-file=src/DEPS -r 'src/third_party/ninja:infra/3pp/tools/ninja/${platform}'` > ninja_ensure_file - sed $SEDOPTION "s/Updating depot_tools... //g" ninja_ensure_file - cipd ensure --root src/third_party/ninja -ensure-file ninja_ensure_file - - echo "$(pwd)/src/third_party/ninja" >> $GITHUB_PATH - - cd src/third_party/angle - rm -f .git/objects/info/alternates - git remote set-url origin https://chromium.googlesource.com/angle/angle.git - cp .git/config .git/config.backup - git remote remove origin - mv .git/config.backup .git/config - git fetch diff --git a/.github/actions/fix-sync/action.yml b/.github/actions/fix-sync/action.yml new file mode 100644 index 0000000000000..23bd710e20141 --- /dev/null +++ b/.github/actions/fix-sync/action.yml @@ -0,0 +1,120 @@ +name: 'Fix Sync' +description: 'Ensures proper binaries are in place' +# This action is required to correct for differences between "gclient sync" +# on Linux and the expected state on macOS/windows. This requires: +# 1. Fixing Clang Install (wrong binary) +# 2. Fixing esbuild (wrong binary) +# 3. Fixing rustc (wrong binary) +# 4. Fixing gn (wrong binary) +# 5. Fix reclient (wrong binary) +# 6. Fixing dsymutil (wrong binary) +# 7. Ensuring we are using the correct ninja and adding it to PATH +# 8. Fixing angle (wrong remote) +# 9. Install windows toolchain on Windows +# 10. Fix node binary on Windows +# 11. Fix rc binary on Windows +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' +runs: + using: "composite" + steps: + - name: Fix clang + shell: bash + run : | + rm -rf src/third_party/llvm-build + python3 src/tools/clang/scripts/update.py + - name: Fix esbuild + uses: ./src/electron/.github/actions/cipd-install + with: + cipd-root-prefix-path: src/third_party/devtools-frontend/src/ + dependency: esbuild + deps-file: src/third_party/devtools-frontend/src/DEPS + installation-dir: third_party/esbuild + target-platform: ${{ inputs.target-platform }} + package: infra/3pp/tools/esbuild/${platform} + - name: Fix rustc + shell: bash + run : | + rm -rf src/third_party/rust-toolchain + python3 src/tools/rust/update_rust.py + - name: Fix gn (macOS) + if: ${{ inputs.target-platform == 'macos' }} + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: gn + deps-file: src/DEPS + installation-dir: src/buildtools/mac + target-platform: ${{ inputs.target-platform }} + package: gn/gn/mac-${arch} + - name: Fix gn (Windows) + if: ${{ inputs.target-platform == 'win' }} + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: gn + deps-file: src/DEPS + installation-dir: src/buildtools/win + target-platform: ${{ inputs.target-platform }} + package: gn/gn/windows-amd64 + - name: Fix reclient + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: reclient + deps-file: src/DEPS + installation-dir: src/buildtools/reclient + target-platform: ${{ inputs.target-platform }} + package: infra/rbe/client/${platform} + - name: Configure reclient configs + shell: bash + run : | + python3 src/buildtools/reclient_cfgs/configure_reclient_cfgs.py --rbe_instance "projects/rbe-chrome-untrusted/instances/default_instance" --reproxy_cfg_template reproxy.cfg.template --rewrapper_cfg_project "" --skip_remoteexec_cfg_fetch + - name: Fix dsymutil (macOS) + if: ${{ inputs.target-platform == 'macos' }} + shell: bash + run : | + # Fix dsymutil + if [ "${{ inputs.target-platform }}" = "macos" ]; then + if [ "${{ env.TARGET_ARCH }}" == "arm64" ]; then + DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1 + else + DSYM_SHA_FILE=src/tools/clang/dsymutil/bin/dsymutil.x64.sha1 + fi + python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang -s $DSYM_SHA_FILE -o src/tools/clang/dsymutil/bin/dsymutil + fi + - name: Fix ninja + uses: ./src/electron/.github/actions/cipd-install + with: + dependency: ninja + deps-file: src/DEPS + installation-dir: src/third_party/ninja + target-platform: ${{ inputs.target-platform }} + package: infra/3pp/tools/ninja/${platform} + - name: Set ninja in path + shell: bash + run : | + echo "$(pwd)/src/third_party/ninja" >> $GITHUB_PATH + - name: Fixup angle git + shell: bash + run : | + cd src/third_party/angle + rm -f .git/objects/info/alternates + git remote set-url origin https://chromium.googlesource.com/angle/angle.git + cp .git/config .git/config.backup + git remote remove origin + mv .git/config.backup .git/config + git fetch + - name: Get Windows toolchain + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: e d vpython3 src\build\vs_toolchain.py update --force + - name: Download nodejs + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + $nodedeps = e d gclient getdep --deps-file=src/DEPS -r src/third_party/node/win | ConvertFrom-JSON + python3 src\third_party\depot_tools\download_from_google_storage.py --no_resume --no_auth --bucket chromium-nodejs -o src\third_party\node\win\node.exe $nodedeps.object_name + - name: Install rc + if: ${{ inputs.target-platform == 'win' }} + shell: bash + run: | + python3 src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-browser-clang/rc -s src/build/toolchain/win/rc/win/rc.exe.sha1 diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index 0dca6d013010e..e3d1c755071f4 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -14,6 +14,7 @@ runs: export BUILD_TOOLS_SHA=8246e57791b0af4ae5975eb96f09855f9269b1cd npm i -g @electron/build-tools e auto-update disable + e d auto-update disable if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then e d cipd.bat --version cp "C:\Python311\python.exe" "C:\Python311\python3.exe" diff --git a/.github/actions/restore-cache-aks/action.yml b/.github/actions/restore-cache-aks/action.yml index 70c67f1d1c91b..b614b3a076dce 100644 --- a/.github/actions/restore-cache-aks/action.yml +++ b/.github/actions/restore-cache-aks/action.yml @@ -1,12 +1,20 @@ name: 'Restore Cache AKS' description: 'Restores Electron src cache via AKS' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: - name: Restore and Ensure Src Cache shell: bash run: | - cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + if [ "${{ inputs.target-platform }}" = "win" ]; then + cache_path=/mnt/win-cache/$DEPSHASH.tar + else + cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar + fi + echo "Using cache key: $DEPSHASH" echo "Checking for cache in: $cache_path" if [ ! -f "$cache_path" ]; then diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index 0ccfd851ea4d2..d1d9ee8428818 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -1,22 +1,25 @@ name: 'Restore Cache AZCopy' description: 'Restores Electron src cache via AZCopy' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' runs: using: "composite" steps: - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-1 + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1 + enableCrossOsArchive: true - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf with: - path: | - sas-token - key: sas-key-${{ github.run_number }}-${{ github.run_attempt }} + path: sas-token + key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} + enableCrossOsArchive: true - name: Download Src Cache from AKS # The cache will always exist here as a result of the checkout job # Either it was uploaded to Azure in the checkout job for this commit @@ -26,21 +29,30 @@ runs: timeout_minutes: 30 max_attempts: 3 retry_on: error + shell: bash command: | sas_token=$(cat sas-token) if [ -z $sas-token ]; then echo "SAS Token not found; exiting src cache download early..." exit 1 + else + if [ "${{ inputs.target-platform }}" = "win" ]; then + azcopy copy --log-level=ERROR \ + "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_WIN_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + else + azcopy copy --log-level=ERROR \ + "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + fi fi - azcopy copy --log-level=ERROR \ - "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar env: AZURE_AKS_CACHE_STORAGE_ACCOUNT: f723719aa87a34622b5f7f3 AZURE_AKS_CACHE_SHARE_NAME: pvc-f6a4089f-b082-4bee-a3f9-c3e1c0c02d8f + AZURE_AKS_WIN_CACHE_SHARE_NAME: pvc-71dec4f2-0d44-4fd1-a2c3-add049d70bdf - name: Clean SAS Key shell: bash run: rm -f sas-token - name: Unzip and Ensure Src Cache + if: ${{ inputs.target-platform == 'macos' }} shell: bash run: | echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)" @@ -68,4 +80,35 @@ runs: fi echo "Wiping Electron Directory" - rm -rf src/electron \ No newline at end of file + rm -rf src/electron + + - name: Unzip and Ensure Src Cache (Windows) + if: ${{ inputs.target-platform == 'win' }} + shell: powershell + run: | + $src_cache = "$env:DEPSHASH.tar" + $cache_size = $(Get-Item $src_cache).length + Write-Host "Downloaded cache is $cache_size" + if ($cache_size -eq 0) { + Write-Host "Cache is empty - exiting" + exit 1 + } + + $TEMP_DIR=New-Item -ItemType Directory -Path temp-cache + $TEMP_DIR_PATH = $TEMP_DIR.FullName + C:\ProgramData\Chocolatey\bin\7z.exe -y x $src_cache -o"$TEMP_DIR_PATH" + if (Test-Path "temp-cache\src") { + Write-Host "Relocating Cache" + Remove-Item -Recurse -Force src + Move-Item temp-cache\src src + + Write-Host "Deleting zip file" + Remove-Item -Force $src_cache + } + if (-Not (Test-Path "src\third_party\blink")) { + Write-Host "Cache was not correctly restored - exiting" + exit 1 + } + + Write-Host "Wiping Electron Directory" + Remove-Item -Recurse -Force src\electron \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d540b5980b4db..ddb09c5593d61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,6 +115,7 @@ jobs: uses: ./src/electron/.github/actions/checkout with: generate-sas-token: 'true' + target-platform: macos checkout-linux: needs: setup @@ -150,7 +151,8 @@ jobs: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root --device /dev/fuse --cap-add SYS_ADMIN volumes: - - /mnt/cross-instance-cache:/mnt/cross-instance-cache + - /mnt/win-cache:/mnt/win-cache + - /var/run/sas:/var/run/sas env: CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} @@ -168,6 +170,9 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout & Sync & Save uses: ./src/electron/.github/actions/checkout + with: + generate-sas-token: 'true' + target-platform: win # GN Check Jobs macos-gn-check: @@ -198,7 +203,7 @@ jobs: target-platform: win target-archs: x64 x86 arm64 check-runs-on: electron-arc-linux-amd64-8core - check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' + check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}' gn-build-type: testing secrets: inherit @@ -326,7 +331,7 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: build-runs-on: electron-arc-windows-amd64-16core @@ -345,7 +350,7 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: build-runs-on: electron-arc-windows-amd64-16core @@ -364,7 +369,7 @@ jobs: issues: read pull-requests: read uses: ./.github/workflows/pipeline-electron-build-and-test.yml - needs: setup + needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: build-runs-on: electron-arc-windows-amd64-16core diff --git a/.github/workflows/clean-src-cache.yml b/.github/workflows/clean-src-cache.yml index f1bd158a4eb21..0c4c5919a0ca3 100644 --- a/.github/workflows/clean-src-cache.yml +++ b/.github/workflows/clean-src-cache.yml @@ -16,6 +16,7 @@ jobs: options: --user root volumes: - /mnt/cross-instance-cache:/mnt/cross-instance-cache + - /mnt/win-cache:/mnt/win-cache steps: - name: Cleanup Source Cache shell: bash @@ -23,3 +24,6 @@ jobs: df -h /mnt/cross-instance-cache find /mnt/cross-instance-cache -type f -mtime +15 -delete df -h /mnt/cross-instance-cache + df -h /mnt/win-cache + find /mnt/win-cache -type f -mtime +15 -delete + df -h /mnt/win-cache diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index 66ac1202ce7a1..a0dcf0688affe 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -129,24 +129,8 @@ jobs: echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV - name: Set Chromium Git Cookie uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - - SEDOPTION="-i" - if [ "`uname`" = "Darwin" ]; then - SEDOPTION="-i ''" - fi - - # remove ninjalog_uploader_wrapper.py from autoninja since we don't use it and it causes problems - sed $SEDOPTION '/ninjalog_uploader_wrapper.py/d' ./depot_tools/autoninja - - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash run: | node src/electron/script/generate-deps-hash.js @@ -154,24 +138,26 @@ jobs: echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV - name: Restore src cache via AZCopy - if: ${{ inputs.target-platform == 'macos' }} + if: ${{ inputs.target-platform != 'linux' }} uses: ./src/electron/.github/actions/restore-cache-azcopy + with: + target-platform: ${{ inputs.target-platform }} - name: Restore src cache via AKS if: ${{ inputs.target-platform == 'linux' }} uses: ./src/electron/.github/actions/restore-cache-aks - - name: Checkout src via gclient sync - if: ${{ inputs.target-platform == 'win' }} - uses: ./src/electron/.github/actions/checkout - with: - use-cache: 'false' - name: Checkout Electron uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Install Build Tools - uses: ./src/electron/.github/actions/install-build-tools + - name: Fix Sync + if: ${{ inputs.target-platform != 'linux' }} + uses: ./src/electron/.github/actions/fix-sync + with: + target-platform: ${{ inputs.target-platform }} + env: + ELECTRON_DEPOT_TOOLS_DISABLE_LOG: true - name: Init Build Tools run: | e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }} @@ -184,9 +170,6 @@ jobs: echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV - name: Add CHROMIUM_BUILDTOOLS_PATH to env run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV - - name: Fix Sync (macOS) - if: ${{ inputs.target-platform == 'macos' }} - uses: ./src/electron/.github/actions/fix-sync-macos - name: Setup Number of Ninja Processes run: | echo "NUMBER_OF_NINJA_PROCESSES=${{ inputs.target-platform != 'macos' && '300' || '200' }}" >> $GITHUB_ENV diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml index 550cd42b7a859..48fe703078145 100644 --- a/.github/workflows/pipeline-segment-electron-gn-check.yml +++ b/.github/workflows/pipeline-segment-electron-gn-check.yml @@ -65,7 +65,9 @@ jobs: sudo rm -rf $TMPDIR/del-target - name: Check disk space after freeing up space if: ${{ inputs.target-platform == 'macos' }} - run: df -h + run: df -h + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Enable windows toolchain @@ -81,9 +83,13 @@ jobs: - name: Restore src cache via AZCopy if: ${{ inputs.target-platform == 'macos' }} uses: ./src/electron/.github/actions/restore-cache-azcopy + with: + target-platform: ${{ inputs.target-platform }} - name: Restore src cache via AKS if: ${{ inputs.target-platform == 'linux' || inputs.target-platform == 'win' }} uses: ./src/electron/.github/actions/restore-cache-aks + with: + target-platform: ${{ inputs.target-platform }} - name: Run Electron Only Hooks run: | echo "solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]" > tmpgclient diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index 981ffe39828d9..ee381add0dbb4 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -60,15 +60,6 @@ jobs: e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu ${{ inputs.target-arch }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Download Generated Artifacts uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 with: @@ -114,6 +105,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -121,17 +114,6 @@ jobs: e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie - - name: Get Depot Tools - timeout-minutes: 5 - run: | - git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git - # Ensure depot_tools does not update. - test -d depot_tools && cd depot_tools - touch .disable_auto_update - - name: Add Depot Tools to PATH - run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Download Generated Artifacts uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 with: From 5f0be2e153fdbaedcafa1aeb72eff3ae22d89043 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 23 Mar 2025 10:26:24 -0500 Subject: [PATCH 052/339] refactor: remove unused field `ServiceWorkerMain::weak_factory_` (#46205) refactor: remove unused field ServiceWorkerMain::weak_factory_ Added in a467d06, appears to have never been used Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_service_worker_main.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/browser/api/electron_api_service_worker_main.h b/shell/browser/api/electron_api_service_worker_main.h index 7166c6fac090a..3493a85d2f1c3 100644 --- a/shell/browser/api/electron_api_service_worker_main.h +++ b/shell/browser/api/electron_api_service_worker_main.h @@ -8,7 +8,6 @@ #include #include "base/memory/raw_ptr.h" -#include "base/memory/weak_ptr.h" #include "base/process/process.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/service_worker_context.h" @@ -167,8 +166,6 @@ class ServiceWorkerMain final mojo::AssociatedRemote remote_; std::unique_ptr> start_worker_promise_; - - base::WeakPtrFactory weak_factory_{this}; }; } // namespace electron::api From 243016b31e8ba5739c3782905aac43bc12626585 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:08:59 +0100 Subject: [PATCH 053/339] docs: fix capitalization for `InputEvent.modifiers` (#46208) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erik Moura --- docs/api/structures/input-event.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/structures/input-event.md b/docs/api/structures/input-event.md index a68a9304dfca1..34b6891faf283 100644 --- a/docs/api/structures/input-event.md +++ b/docs/api/structures/input-event.md @@ -12,6 +12,6 @@ `pointerDown`, `pointerUp`, `pointerMove`, `pointerRawUpdate`, `pointerCancel` or `pointerCausedUaAction`. * `modifiers` string[] (optional) - An array of modifiers of the event, can - be `shift`, `control`, `ctrl`, `alt`, `meta`, `command`, `cmd`, `isKeypad`, - `isAutoRepeat`, `leftButtonDown`, `middleButtonDown`, `rightButtonDown`, - `capsLock`, `numLock`, `left`, `right`. + be `shift`, `control`, `ctrl`, `alt`, `meta`, `command`, `cmd`, `iskeypad`, + `isautorepeat`, `leftbuttondown`, `middlebuttondown`, `rightbuttondown`, + `capslock`, `numlock`, `left`, `right`. From e1b2b8ef0c5bd2ce28d031e3ee75f7dc28324e57 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 08:20:34 -0500 Subject: [PATCH 054/339] refactor: remove unused method ProxyingWebSocket::web_request_api() (#46214) Appears to have been added in c608d6d7 but never used Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/net/proxying_websocket.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/browser/net/proxying_websocket.h b/shell/browser/net/proxying_websocket.h index 955be09420769..8940757adaa98 100644 --- a/shell/browser/net/proxying_websocket.h +++ b/shell/browser/net/proxying_websocket.h @@ -111,8 +111,6 @@ class ProxyingWebSocket : public network::mojom::WebSocketHandshakeClient, content::BrowserContext* browser_context, uint64_t* request_id_generator); - WebRequestAPI* web_request_api() { return web_request_api_; } - private: void OnBeforeRequestComplete(int error_code); void OnBeforeSendHeadersComplete(const std::set& removed_headers, From 76fa5b7af1df4544eced433f764ecf250be532b3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:30:52 -0500 Subject: [PATCH 055/339] perf: use `absl::flat_hash_map` instead of `std::unordered_map` (#46217) * perf: make ElectronUsbDelegate::controller_map_ an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make ElectronSerialDelegate::controller_map_ an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make ElectronHidDelegate::controller_map_ an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make FrameTreeNodeIdMap an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make AutofillDriverFactory::driver_map_ an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make asar::Archive::external_files_ an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make VersionIdMap an absl::flat_hash_map Co-authored-by: Charles Kerr * perf: make ObjectCache::proxy_map_ an absl::flat_hash_map Co-authored-by: Charles Kerr * docs: add TODO to investigate absl map in KeyWeakMap Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_service_worker_main.cc | 9 ++++----- shell/browser/api/electron_api_web_frame_main.cc | 4 ++-- shell/browser/electron_autofill_driver_factory.h | 5 +++-- shell/browser/hid/electron_hid_delegate.h | 6 +++--- shell/browser/serial/electron_serial_delegate.h | 6 +++--- shell/browser/usb/electron_usb_delegate.h | 6 +++--- shell/common/asar/archive.h | 6 +++--- shell/common/key_weak_map.h | 1 + shell/renderer/api/context_bridge/object_cache.h | 5 ++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/shell/browser/api/electron_api_service_worker_main.cc b/shell/browser/api/electron_api_service_worker_main.cc index 4d06cc083a127..5d3482f3e1b7d 100644 --- a/shell/browser/api/electron_api_service_worker_main.cc +++ b/shell/browser/api/electron_api_service_worker_main.cc @@ -5,7 +5,6 @@ #include "shell/browser/api/electron_api_service_worker_main.h" #include -#include #include #include "base/logging.h" @@ -28,6 +27,7 @@ #include "shell/common/gin_helper/promise.h" #include "shell/common/node_includes.h" #include "shell/common/v8_util.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_map.h" namespace { @@ -58,10 +58,9 @@ std::optional GetLiveVersionInfo( namespace electron::api { // ServiceWorkerKey -> ServiceWorkerMain* -typedef std::unordered_map - VersionIdMap; +using VersionIdMap = absl::flat_hash_map; VersionIdMap& GetVersionIdMap() { static base::NoDestructor instance; diff --git a/shell/browser/api/electron_api_web_frame_main.cc b/shell/browser/api/electron_api_web_frame_main.cc index 40e3d811baf8e..2cc7a1b47c0f0 100644 --- a/shell/browser/api/electron_api_web_frame_main.cc +++ b/shell/browser/api/electron_api_web_frame_main.cc @@ -5,7 +5,6 @@ #include "shell/browser/api/electron_api_web_frame_main.h" #include -#include #include #include @@ -36,6 +35,7 @@ #include "shell/common/gin_helper/promise.h" #include "shell/common/node_includes.h" #include "shell/common/v8_util.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_map.h" namespace { @@ -99,7 +99,7 @@ namespace electron::api { // Using FrameTreeNode allows us to track frame across navigations. This // is most similar to how + + + From 06ad7634125f2d197ab67b1512761e1e486b4298 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:41:49 -0500 Subject: [PATCH 068/339] refactor: migrate hashing code to new upstream `crypto::hash` API (#46273) * refactor: migrate AsarFileValidator to crypto::hash This change migrates AsarFileValidator's uses of crypto::secure_hash to the new crypto::hash API, which has more memory safety and less heap allocations. Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6287609 Co-authored-by: Charles Kerr * refactor: migrate ValidateIntegrityOrDie to crypto::hash This change migrates ValidateIntegrityOrDie's use of crypto::SHA256Hash to the new crypto::hash API, which has more memory safety and less heap allocations. Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6287609 Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/net/asar/asar_file_validator.cc | 7 +++---- shell/browser/net/asar/asar_file_validator.h | 6 +++--- shell/common/asar/asar_util.cc | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/shell/browser/net/asar/asar_file_validator.cc b/shell/browser/net/asar/asar_file_validator.cc index 0c9ddf44ee21e..19d9dd9ff420c 100644 --- a/shell/browser/net/asar/asar_file_validator.cc +++ b/shell/browser/net/asar/asar_file_validator.cc @@ -14,7 +14,6 @@ #include "base/notreached.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "crypto/sha2.h" namespace asar { @@ -34,7 +33,7 @@ void AsarFileValidator::EnsureBlockHashExists() { current_hash_byte_count_ = 0U; switch (integrity_.algorithm) { case HashAlgorithm::kSHA256: - current_hash_ = crypto::SecureHash::Create(crypto::SecureHash::SHA256); + current_hash_.emplace(crypto::hash::kSha256); break; case HashAlgorithm::kNone: NOTREACHED(); @@ -86,7 +85,7 @@ bool AsarFileValidator::FinishBlock() { if (!current_hash_) { // This happens when we fail to read the resource. Compute empty content's // hash in this case. - current_hash_ = crypto::SecureHash::Create(crypto::SecureHash::SHA256); + current_hash_.emplace(crypto::hash::kSha256); } // If the file reader is done we need to make sure we've either read up to the @@ -108,7 +107,7 @@ bool AsarFileValidator::FinishBlock() { current_hash_->Update(abandoned_buffer); } - auto actual = std::array{}; + auto actual = std::array{}; current_hash_->Finish(actual); current_hash_.reset(); current_hash_byte_count_ = 0; diff --git a/shell/browser/net/asar/asar_file_validator.h b/shell/browser/net/asar/asar_file_validator.h index 48c9c432e1f37..9caa80115f66f 100644 --- a/shell/browser/net/asar/asar_file_validator.h +++ b/shell/browser/net/asar/asar_file_validator.h @@ -5,9 +5,9 @@ #ifndef ELECTRON_SHELL_BROWSER_NET_ASAR_ASAR_FILE_VALIDATOR_H_ #define ELECTRON_SHELL_BROWSER_NET_ASAR_ASAR_FILE_VALIDATOR_H_ -#include +#include -#include "crypto/secure_hash.h" +#include "crypto/hash.h" #include "mojo/public/cpp/system/file_data_source.h" #include "mojo/public/cpp/system/filtered_data_source.h" #include "shell/common/asar/archive.h" @@ -56,7 +56,7 @@ class AsarFileValidator : public mojo::FilteredDataSource::Filter { int max_block_; uint64_t current_hash_byte_count_ = 0U; uint64_t total_hash_byte_count_ = 0; - std::unique_ptr current_hash_; + std::optional current_hash_; }; } // namespace asar diff --git a/shell/common/asar/asar_util.cc b/shell/common/asar/asar_util.cc index c456dd3386b1a..36c1e69ae8296 100644 --- a/shell/common/asar/asar_util.cc +++ b/shell/common/asar/asar_util.cc @@ -16,8 +16,7 @@ #include "base/strings/string_util.h" #include "base/synchronization/lock.h" #include "base/threading/thread_local.h" -#include "crypto/secure_hash.h" -#include "crypto/sha2.h" +#include "crypto/hash.h" #include "shell/common/asar/archive.h" #include "shell/common/thread_restrictions.h" @@ -139,7 +138,7 @@ void ValidateIntegrityOrDie(base::span input, const IntegrityPayload& integrity) { if (integrity.algorithm == HashAlgorithm::kSHA256) { const std::string hex_hash = - base::ToLowerASCII(base::HexEncode(crypto::SHA256Hash(input))); + base::ToLowerASCII(base::HexEncode(crypto::hash::Sha256(input))); if (integrity.hash != hex_hash) { LOG(FATAL) << "Integrity check failed for asar archive (" << integrity.hash << " vs " << hex_hash << ")"; From 12d11c09a1b7e0536f252cf106323938eb515dc9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:45:42 -0700 Subject: [PATCH 069/339] build: fixup windows source cache for release (#46270) * build: fixup ffmpeg gn gen Co-authored-by: John Kleinschmidt * build: add build-tools depot_tools to PATH There are some cases where it is still expected that depot_tools be in the path Co-authored-by: John Kleinschmidt * put back regular gn gen for ffmpeg Co-authored-by: John Kleinschmidt * build: add retry to moving source cache This resolves the error: `Move-Item : The process cannot access the file because it is being used by another process.` Co-authored-by: John Kleinschmidt --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/build-electron/action.yml | 2 +- .../actions/install-build-tools/action.yml | 1 + .../actions/restore-cache-azcopy/action.yml | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 710c0b8ac5767..58a416e01f404 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -185,7 +185,7 @@ runs: if: ${{ inputs.is-release == 'true' }} run: | cd src - e d gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true $GN_EXTRA_ARGS" + gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true $GN_EXTRA_ARGS" e build --target electron:electron_ffmpeg_zip -C ../../out/ffmpeg -j $NUMBER_OF_NINJA_PROCESSES - name: Generate Hunspell Dictionaries ${{ inputs.step-suffix }} shell: bash diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index e3d1c755071f4..d405dfa1cd212 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -19,3 +19,4 @@ runs: e d cipd.bat --version cp "C:\Python311\python.exe" "C:\Python311\python3.exe" fi + echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index d1d9ee8428818..4c34ba496340b 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -97,18 +97,28 @@ runs: $TEMP_DIR=New-Item -ItemType Directory -Path temp-cache $TEMP_DIR_PATH = $TEMP_DIR.FullName C:\ProgramData\Chocolatey\bin\7z.exe -y x $src_cache -o"$TEMP_DIR_PATH" - if (Test-Path "temp-cache\src") { - Write-Host "Relocating Cache" - Remove-Item -Recurse -Force src - Move-Item temp-cache\src src - Write-Host "Deleting zip file" - Remove-Item -Force $src_cache - } - if (-Not (Test-Path "src\third_party\blink")) { - Write-Host "Cache was not correctly restored - exiting" - exit 1 - } + - name: Move Src Cache (Windows) + if: ${{ inputs.target-platform == 'win' }} + uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 + with: + timeout_minutes: 30 + max_attempts: 3 + retry_on: error + shell: powershell + command: | + if (Test-Path "temp-cache\src") { + Write-Host "Relocating Cache" + Remove-Item -Recurse -Force src + Move-Item temp-cache\src src + + Write-Host "Deleting zip file" + Remove-Item -Force $src_cache + } + if (-Not (Test-Path "src\third_party\blink")) { + Write-Host "Cache was not correctly restored - exiting" + exit 1 + } - Write-Host "Wiping Electron Directory" - Remove-Item -Recurse -Force src\electron \ No newline at end of file + Write-Host "Wiping Electron Directory" + Remove-Item -Recurse -Force src\electron From 3e2aa2f443b9c13bcaa36af7d245698f4bff9f26 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 22:01:02 -0400 Subject: [PATCH 070/339] perf: avoid redundant map lookup in `WebFrameMain` constructor (#46276) perf: avoid double map lookup in WebFrameMain constructor Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_web_frame_main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/api/electron_api_web_frame_main.cc b/shell/browser/api/electron_api_web_frame_main.cc index 2cc7a1b47c0f0..c81a2cb128f91 100644 --- a/shell/browser/api/electron_api_web_frame_main.cc +++ b/shell/browser/api/electron_api_web_frame_main.cc @@ -155,8 +155,8 @@ WebFrameMain::WebFrameMain(content::RenderFrameHost* rfh) if (!render_frame_detached_) GetFrameTreeNodeIdMap().emplace(frame_tree_node_id_, this); - DCHECK(!GetFrameTokenMap().contains(frame_token_)); - GetFrameTokenMap().emplace(frame_token_, this); + const auto [_, inserted] = GetFrameTokenMap().emplace(frame_token_, this); + DCHECK(inserted); // WebFrameMain should only be created for active or unloading frames. DCHECK(GetLifecycleState(rfh) == LifecycleState::kActive || From 32c352bff53134a582f22fe6ba8e28da33f5451e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 12:48:49 +0100 Subject: [PATCH 071/339] fix: build failure when printing is disabled (#46284) fix: ftbfs when printing is disabled Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_web_contents.cc | 2 ++ shell/browser/api/electron_api_web_contents.h | 2 ++ shell/browser/feature_list.cc | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 209e47949ee01..a5b7026d0eb32 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1988,6 +1988,7 @@ void WebContents::DraggableRegionsChanged( draggable_region_ = DraggableRegionsToSkRegion(regions); } +#if BUILDFLAG(ENABLE_PRINTING) void WebContents::PrintCrossProcessSubframe( content::WebContents* web_contents, const gfx::Rect& rect, @@ -1998,6 +1999,7 @@ void WebContents::PrintCrossProcessSubframe( client->PrintCrossProcessSubframe(rect, document_cookie, subframe_host); } } +#endif SkRegion* WebContents::draggable_region() { return g_disable_draggable_regions ? nullptr : draggable_region_.get(); diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 6398bc89d2c11..30a2365bc0a9c 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -585,11 +585,13 @@ class WebContents final : public ExclusiveAccessContext, void DraggableRegionsChanged( const std::vector& regions, content::WebContents* contents) override; +#if BUILDFLAG(ENABLE_PRINTING) void PrintCrossProcessSubframe( content::WebContents* web_contents, const gfx::Rect& rect, int document_cookie, content::RenderFrameHost* subframe_host) const override; +#endif // content::WebContentsObserver: void BeforeUnloadFired(bool proceed) override; diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index a4be79d37e984..e6249b383fda2 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -16,6 +16,7 @@ #include "electron/buildflags/buildflags.h" #include "media/base/media_switches.h" #include "net/base/features.h" +#include "printing/buildflags/buildflags.h" #include "services/network/public/cpp/features.h" #include "third_party/blink/public/common/features.h" @@ -60,7 +61,7 @@ void InitializeFeatureList() { std::string(",") + features::kMacWebContentsOcclusion.name; #endif -#if BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_PRINTING) disable_features += // EnableOopPrintDrivers is still a bit half-baked on Linux and // causes crashes when trying to show dialogs. From 3ff5c4ea5f251af5fdb68abf7796d4ec1b916547 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:34:37 -0500 Subject: [PATCH 072/339] perf: avoid redundant map lookup in `WebContents::DevToolsIndexPath()` (#46295) perf: avoid double map lookup in WebContents::DevToolsIndexPath() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../browser/api/electron_api_web_contents.cc | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index a5b7026d0eb32..ad4b2e7c0fad6 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -4139,8 +4139,11 @@ void WebContents::DevToolsIndexPath( OnDevToolsIndexingDone(request_id, file_system_path); return; } - if (devtools_indexing_jobs_.contains(request_id)) + + auto& indexing_job = devtools_indexing_jobs_[request_id]; + if (indexing_job) return; + std::vector excluded_folders; std::optional parsed_excluded_folders = base::JSONReader::Read(excluded_folders_message); @@ -4150,19 +4153,18 @@ void WebContents::DevToolsIndexPath( excluded_folders.push_back(folder_path.GetString()); } } - devtools_indexing_jobs_[request_id] = - scoped_refptr( - devtools_file_system_indexer_->IndexPath( - file_system_path, excluded_folders, - base::BindRepeating( - &WebContents::OnDevToolsIndexingWorkCalculated, - weak_factory_.GetWeakPtr(), request_id, file_system_path), - base::BindRepeating(&WebContents::OnDevToolsIndexingWorked, - weak_factory_.GetWeakPtr(), request_id, - file_system_path), - base::BindRepeating(&WebContents::OnDevToolsIndexingDone, - weak_factory_.GetWeakPtr(), request_id, - file_system_path))); + + indexing_job = devtools_file_system_indexer_->IndexPath( + file_system_path, excluded_folders, + base::BindRepeating(&WebContents::OnDevToolsIndexingWorkCalculated, + weak_factory_.GetWeakPtr(), request_id, + file_system_path), + base::BindRepeating(&WebContents::OnDevToolsIndexingWorked, + weak_factory_.GetWeakPtr(), request_id, + file_system_path), + base::BindRepeating(&WebContents::OnDevToolsIndexingDone, + weak_factory_.GetWeakPtr(), request_id, + file_system_path)); } void WebContents::DevToolsStopIndexing(int request_id) { From dde045db9cfc3107be7d35d3b6b9f9cf1c2fdb36 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 19:25:49 -0500 Subject: [PATCH 073/339] perf: avoid `std::map` temporaries in `WebContents::DevToolsRequestFileSystems()` (#46309) * perf: move the GetDevToolsWebContents() call outside of the loop Co-authored-by: Charles Kerr * perf: remove std::map temporary in WebContents::DevToolsRequestFileSystems() Co-authored-by: Charles Kerr * refactor: remove unused GetAddedFileSystemPaths() Co-authored-by: Charles Kerr * perf: remove std::vector temporary in WebContents::DevToolsRequestFileSystems() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../browser/api/electron_api_web_contents.cc | 44 +++++-------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index ad4b2e7c0fad6..482620cc286d6 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -682,17 +682,6 @@ PrefService* GetPrefService(content::WebContents* web_contents) { return GetPrefService(web_contents)->GetDict(prefs::kDevToolsFileSystemPaths); } -std::map GetAddedFileSystemPaths( - content::WebContents* web_contents) { - std::map result; - for (auto it : GetAddedFileSystems(web_contents)) { - std::string type = - it.second.is_string() ? it.second.GetString() : std::string(); - result[it.first] = type; - } - return result; -} - bool IsDevToolsFileSystemAdded(content::WebContents* web_contents, const std::string_view file_system_path) { return GetAddedFileSystems(web_contents).contains(file_system_path); @@ -4053,31 +4042,22 @@ void WebContents::DevToolsAppendToFile(const std::string& url, } void WebContents::DevToolsRequestFileSystems() { - auto file_system_paths = GetAddedFileSystemPaths(GetDevToolsWebContents()); - if (file_system_paths.empty()) { - inspectable_web_contents_->CallClientFunction( - "DevToolsAPI", "fileSystemsLoaded", base::Value(base::Value::List())); - return; - } + const std::string empty_str; + content::WebContents* const dtwc = GetDevToolsWebContents(); + const base::Value::Dict& added_paths = GetAddedFileSystems(dtwc); - std::vector file_systems; - for (const auto& file_system_path : file_system_paths) { - base::FilePath path = - base::FilePath::FromUTF8Unsafe(file_system_path.first); - std::string file_system_id = - RegisterFileSystem(GetDevToolsWebContents(), path); - FileSystem file_system = - CreateFileSystemStruct(GetDevToolsWebContents(), file_system_id, - file_system_path.first, file_system_path.second); - file_systems.push_back(file_system); + auto filesystems = base::Value::List::with_capacity(added_paths.size()); + for (const auto path_and_type : added_paths) { + const auto& [path, type_val] = path_and_type; + const auto& type = type_val.is_string() ? type_val.GetString() : empty_str; + const std::string file_system_id = + RegisterFileSystem(dtwc, base::FilePath::FromUTF8Unsafe(path)); + filesystems.Append(CreateFileSystemValue( + CreateFileSystemStruct(dtwc, file_system_id, path, type))); } - base::Value::List file_system_value; - for (const auto& file_system : file_systems) - file_system_value.Append(CreateFileSystemValue(file_system)); inspectable_web_contents_->CallClientFunction( - "DevToolsAPI", "fileSystemsLoaded", - base::Value(std::move(file_system_value))); + "DevToolsAPI", "fileSystemsLoaded", base::Value{std::move(filesystems)}); } void WebContents::DevToolsAddFileSystem( From bf87315ae86fc38fca7334f2e01a3f3c075ca1ea Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 19:39:40 -0500 Subject: [PATCH 074/339] perf: avoid redundant map lookup in `AddComponentResourceEntries()` (#46290) * perf: avoid double map lookup in ElectronComponentExtensionResourceManager::AddComponentResourceEntries() Co-authored-by: Charles Kerr * perf: move the path key when calling try_emplace() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../electron_component_extension_resource_manager.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/shell/browser/extensions/electron_component_extension_resource_manager.cc b/shell/browser/extensions/electron_component_extension_resource_manager.cc index d3ac6fd3acebe..0a1b2df5d61d5 100644 --- a/shell/browser/extensions/electron_component_extension_resource_manager.cc +++ b/shell/browser/extensions/electron_component_extension_resource_manager.cc @@ -84,12 +84,14 @@ void ElectronComponentExtensionResourceManager::AddComponentResourceEntries( gen_folder_path = gen_folder_path.NormalizePathSeparators(); for (const auto& entry : entries) { + const int id = entry.id; base::FilePath resource_path = base::FilePath().AppendASCII(entry.path); resource_path = resource_path.NormalizePathSeparators(); if (!gen_folder_path.IsParent(resource_path)) { - DCHECK(!path_to_resource_id_.contains(resource_path)); - path_to_resource_id_[resource_path] = entry.id; + const auto [_, inserted] = + path_to_resource_id_.try_emplace(std::move(resource_path), id); + DCHECK(inserted); } else { // If the resource is a generated file, strip the generated folder's path, // so that it can be served from a normal URL (as if it were not @@ -97,8 +99,9 @@ void ElectronComponentExtensionResourceManager::AddComponentResourceEntries( base::FilePath effective_path = base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr( gen_folder_path.value().length())); - DCHECK(!path_to_resource_id_.contains(effective_path)); - path_to_resource_id_[effective_path] = entry.id; + const auto [_, inserted] = + path_to_resource_id_.try_emplace(std::move(effective_path), id); + DCHECK(inserted); } } } From 71f17fcf5b2aa98f4148b784e2b0953eb1641aa4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 19:53:12 -0500 Subject: [PATCH 075/339] fix: set `userAgent` on `navigationHistory.restore()` (#46299) fix: set userAgent on navigationHistory restore Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../browser/api/electron_api_web_contents.cc | 7 ++++++ spec/api-web-contents-spec.ts | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 482620cc286d6..005e5d91e5c6e 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2522,6 +2522,9 @@ void WebContents::RestoreHistory( auto navigation_entries = std::make_unique< std::vector>>(); + blink::UserAgentOverride ua_override; + ua_override.ua_string_override = GetUserAgent(); + for (const auto& entry : entries) { content::NavigationEntry* nav_entry = nullptr; if (!gin::Converter::FromV8(isolate, entry, @@ -2534,11 +2537,15 @@ void WebContents::RestoreHistory( std::to_string(index) + "."); return; } + + nav_entry->SetIsOverridingUserAgent( + !ua_override.ua_string_override.empty()); navigation_entries->push_back( std::unique_ptr(nav_entry)); } if (!navigation_entries->empty()) { + web_contents()->SetUserAgentOverride(ua_override, false); web_contents()->GetController().Restore( index, content::RestoreType::kRestored, navigation_entries.get()); web_contents()->GetController().LoadIfNecessary(); diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index 8a4bf08304734..69e462c1df2c4 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -887,6 +887,31 @@ describe('webContents module', () => { }); }); }); + + it('should restore an overridden user agent', async () => { + const partition = 'persist:wcvtest'; + const testUA = 'MyCustomUA'; + + const ses = session.fromPartition(partition); + ses.setUserAgent(testUA); + + const wcv = new WebContentsView({ + webPreferences: { partition } + }); + + wcv.webContents.navigationHistory.restore({ + entries: [{ + url: urlPage1, + title: 'url1' + }], + index: 0 + }); + + const ua = wcv.webContents.getUserAgent(); + const wcvua = await wcv.webContents.executeJavaScript('navigator.userAgent'); + + expect(ua).to.equal(wcvua); + }); }); describe('getFocusedWebContents() API', () => { From 8b05b6e59b29d29fba66b3978ec17c4209279e0d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:08:34 +0100 Subject: [PATCH 076/339] fix: crash when drag-dropping some files (#46312) * fix: crash when drag-dropping some files Co-authored-by: Shelley Vohr * fix: extra destination context scope Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../api/electron_api_context_bridge.cc | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 119b869c9f250..ff91fee761af8 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -687,17 +687,26 @@ v8::MaybeLocal CreateProxyForAPI( continue; } } - v8::Local value; - if (!api.Get(key, &value)) - continue; - - auto passed_value = PassValueToOtherContextInner( - source_context, source_execution_context, destination_context, value, - api.GetHandle(), object_cache, support_dynamic_properties, - recursion_depth + 1, error_target); - if (passed_value.IsEmpty()) - return {}; - proxy.Set(key, passed_value.ToLocalChecked()); + + { + v8::Context::Scope source_context_scope(source_context); + v8::Local value; + if (!api.Get(key, &value)) + continue; + + auto passed_value = PassValueToOtherContextInner( + source_context, source_execution_context, destination_context, + value, api.GetHandle(), object_cache, support_dynamic_properties, + recursion_depth + 1, error_target); + if (passed_value.IsEmpty()) + return {}; + + { + v8::Context::Scope inner_destination_context_scope( + destination_context); + proxy.Set(key, passed_value.ToLocalChecked()); + } + } } return proxy.GetHandle(); From 4df1063e30612e35c8ff29d1f37f2c876a16144f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:09:06 -0500 Subject: [PATCH 077/339] build: roll build-images SHAs (#46320) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .devcontainer/docker-compose.yml | 2 +- .github/workflows/build.yml | 4 ++-- .github/workflows/linux-publish.yml | 2 +- .github/workflows/macos-publish.yml | 2 +- .github/workflows/windows-publish.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index f867f0eb621a4..b422e8a938ed7 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: buildtools: - image: ghcr.io/electron/devcontainer:77262e58c37631ab082482f42c33cdf68c6c394b + image: ghcr.io/electron/devcontainer:9f11982e806f439d0a0a8ebbbf566cd5e0d9e952 volumes: - ..:/workspaces/gclient/src/electron:cached diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddb09c5593d61..f9d393575956c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' required: true skip-macos: type: boolean @@ -64,7 +64,7 @@ jobs: id: set-output run: | if [ -z "${{ inputs.build-image-sha }}" ]; then - echo "build-image-sha=bc2f48b2415a670de18d13605b1cf0eb5fdbaae1" >> "$GITHUB_OUTPUT" + echo "build-image-sha=9f11982e806f439d0a0a8ebbbf566cd5e0d9e952" >> "$GITHUB_OUTPUT" else echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT" fi diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index cfba90632237e..9443d54f324e0 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' upload-to-storage: description: 'Uploads to Azure storage' required: false diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index 95673bde170c1..d20b2d8d94a00 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' required: true upload-to-storage: description: 'Uploads to Azure storage' diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index 0f4ee59152985..3b0f0ad3e5e9b 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: 'bc2f48b2415a670de18d13605b1cf0eb5fdbaae1' + default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' required: true upload-to-storage: description: 'Uploads to Azure storage' From c89bebdee1538764a4ca1d53bef533a0db2691e0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 18:13:39 -0500 Subject: [PATCH 078/339] perf: avoid double map lookup in `WebFrameMain::UpdateRenderFrameHost()` (#46331) perf: avoid double map lookup in WebFrameMain::UpdateRenderFrameHost() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_web_frame_main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/api/electron_api_web_frame_main.cc b/shell/browser/api/electron_api_web_frame_main.cc index c81a2cb128f91..741bc2328710f 100644 --- a/shell/browser/api/electron_api_web_frame_main.cc +++ b/shell/browser/api/electron_api_web_frame_main.cc @@ -193,8 +193,8 @@ void WebFrameMain::UpdateRenderFrameHost(content::RenderFrameHost* rfh) { // Ensure that RFH being swapped in doesn't already exist as its own // WebFrameMain instance. frame_token_ = rfh->GetGlobalFrameToken(); - DCHECK(!GetFrameTokenMap().contains(frame_token_)); - GetFrameTokenMap().emplace(frame_token_, this); + const auto [_, inserted] = GetFrameTokenMap().emplace(frame_token_, this); + DCHECK(inserted); render_frame_disposed_ = false; TeardownMojoConnection(); From b4fc140f1abbdeb729fe634627b600e07b81736b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 18:14:37 -0500 Subject: [PATCH 079/339] fix: possible crash in `shell.readShortcutLink` (#46324) fix: possible crash in shell.readShortcutLink Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/chromium/.patches | 2 +- ...key_appusermodel_toastactivatorclsid.patch | 40 ----------------- ...errors_for_resolveshortcutproperties.patch | 45 +++++++++++++++++++ 3 files changed, 46 insertions(+), 41 deletions(-) delete mode 100644 patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch create mode 100644 patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 2966787e13c29..cb7cb32aa758e 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -136,7 +136,7 @@ refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch build_add_public_config_simdutf_config.patch revert_code_health_clean_up_stale_macwebcontentsocclusion.patch -ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +ignore_parse_errors_for_resolveshortcutproperties.patch feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch diff --git a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch b/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch deleted file mode 100644 index 03982cee101e5..0000000000000 --- a/patches/chromium/ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?David=20L=C3=B6nnhager?= -Date: Fri, 17 Jan 2025 14:30:48 +0100 -Subject: Ignore parse errors for PKEY_AppUserModel_ToastActivatorCLSID - -Some shortcuts store this as a string UUID as opposed to VT_CLSID, -hitting NOTREACHED() and sometimes breaking parsing in Electron. -Ignore this error instead. - -Bug: N/A -Change-Id: I9fc472212b2d3afac2c8e18a2159bc2d50bbdf98 - -diff --git a/AUTHORS b/AUTHORS -index b55c6916e97a7c8e317145f5239a34d8abca810e..dbd924a91a8fca8ff9d85bf6addcdb5a7b17ca3a 100644 ---- a/AUTHORS -+++ b/AUTHORS -@@ -341,6 +341,7 @@ David Futcher - David Jin - David Lechner - David Leen -+David Lönnhager - David Manouchehri - David McAllister - David Michael Barr -diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc -index 967e130e823f41c402411dfadb53b805e8a8c92b..3a9df7f31861ca69168fd24513ee554d0984798d 100644 ---- a/base/win/shortcut.cc -+++ b/base/win/shortcut.cc -@@ -356,8 +356,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, - *(pv_toast_activator_clsid.get().puuid)); - break; - default: -- NOTREACHED() << "Unexpected variant type: " -- << pv_toast_activator_clsid.get().vt; -+ // Shortcuts may use strings to represent the CLSID. This case is -+ // ignored. -+ break; - } - } - } diff --git a/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch b/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch new file mode 100644 index 0000000000000..e3fe60af6e365 --- /dev/null +++ b/patches/chromium/ignore_parse_errors_for_resolveshortcutproperties.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?David=20L=C3=B6nnhager?= +Date: Fri, 17 Jan 2025 14:30:48 +0100 +Subject: Ignore parse errors in ResolveShortcutProperties + +Some shortcuts store this as a string UUID as opposed to VT_CLSID, +hitting NOTREACHED() and sometimes breaking parsing in Electron. +Ignore this error instead. + +diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc +index 967e130e823f41c402411dfadb53b805e8a8c92b..c1cc95fa8993cc5bdab422710934fb6217272b96 100644 +--- a/base/win/shortcut.cc ++++ b/base/win/shortcut.cc +@@ -317,7 +317,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + break; + } + default: { +- NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt; ++ LOG(WARNING) << "Unexpected variant type: " << pv_app_id.get().vt; ++ break; + } + } + } +@@ -336,7 +337,8 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE); + break; + default: +- NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt; ++ LOG(WARNING) << "Unexpected variant type: " << pv_dual_mode.get().vt; ++ break; + } + } + +@@ -356,8 +358,9 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, + *(pv_toast_activator_clsid.get().puuid)); + break; + default: +- NOTREACHED() << "Unexpected variant type: " +- << pv_toast_activator_clsid.get().vt; ++ LOG(INFO) << "Unexpected variant type: " ++ << pv_toast_activator_clsid.get().vt; ++ break; + } + } + } From cb7335b4f0173ef9c9e82d57254ce0c19eb4d1e0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 19:21:42 -0500 Subject: [PATCH 080/339] perf: avoid a triple-redundant map lookup in `ViewsDelegate::GetAppbarAutohideEdges()` (#46335) perf: avoid a triple-redundant map lookup in ViewsDelegate::GetAppbarAutohideEdges() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/ui/views/electron_views_delegate_win.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/ui/views/electron_views_delegate_win.cc b/shell/browser/ui/views/electron_views_delegate_win.cc index cb67cccd7c5cf..a6bce43396ee1 100644 --- a/shell/browser/ui/views/electron_views_delegate_win.cc +++ b/shell/browser/ui/views/electron_views_delegate_win.cc @@ -119,8 +119,8 @@ int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, // in us thinking there is no auto-hide edges. By returning at least one edge // we don't initially go fullscreen until we figure out the real auto-hide // edges. - if (!appbar_autohide_edge_map_.contains(monitor)) - appbar_autohide_edge_map_[monitor] = EDGE_BOTTOM; + auto [iter, _] = appbar_autohide_edge_map_.try_emplace(monitor, EDGE_BOTTOM); + const int edges{iter->second}; // We use the SHAppBarMessage API to get the taskbar autohide state. This API // spins a modal loop which could cause callers to be reentered. To avoid @@ -133,9 +133,9 @@ int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, base::BindOnce(&GetAppbarAutohideEdgesOnWorkerThread, monitor), base::BindOnce(&ViewsDelegate::OnGotAppbarAutohideEdges, weak_factory_.GetWeakPtr(), std::move(callback), monitor, - appbar_autohide_edge_map_[monitor])); + edges)); } - return appbar_autohide_edge_map_[monitor]; + return edges; } void ViewsDelegate::OnGotAppbarAutohideEdges(base::OnceClosure callback, From 200550da3c2234d4e883f71354696ce4aee43642 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:26:29 -0500 Subject: [PATCH 081/339] fix: allow NSMenuItems to be disabled (#46342) * fix: disable NSMenu autoenable feature to allow disabling of NSMenuItems Co-authored-by: Hailey Schauman * style: fix linter issues and update comments Co-authored-by: Hailey Schauman * chore: remove unneeded comment Co-authored-by: Hailey --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Hailey Schauman --- .../browser/ui/cocoa/electron_menu_controller.mm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/shell/browser/ui/cocoa/electron_menu_controller.mm b/shell/browser/ui/cocoa/electron_menu_controller.mm index d23e007dd0bad..2c5eae1499b16 100644 --- a/shell/browser/ui/cocoa/electron_menu_controller.mm +++ b/shell/browser/ui/cocoa/electron_menu_controller.mm @@ -320,6 +320,10 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:label action:@selector(itemSelected:) keyEquivalent:@""]; + if (model->IsEnabledAt(index)) + [item setEnabled:YES]; + else + [item setEnabled:NO]; // If the menu item has an icon, set it. ui::ImageModel icon = model->GetIconAt(index); @@ -349,11 +353,6 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index [item setSubmenu:[self createShareMenuForItem:sharing_item]]; } else if (type == electron::ElectronMenuModel::TYPE_SUBMENU && model->IsVisibleAt(index)) { - // We need to specifically check that the submenu top-level item has been - // enabled as it's not validated by validateUserInterfaceItem - if (!model->IsEnabledAt(index)) - [item setEnabled:NO]; - // Recursively build a submenu from the sub-model at this index. [item setTarget:nil]; [item setAction:nil]; @@ -493,8 +492,10 @@ - (void)performShare:(NSMenuItem*)sender { } - (NSMenu*)menu { - if (menu_) + if (menu_) { + [menu_ setAutoenablesItems:NO]; return menu_; + } if (model_ && model_->sharing_item()) { NSMenu* menu = [self createShareMenuForItem:*model_->sharing_item()]; @@ -504,6 +505,8 @@ - (NSMenu*)menu { if (model_) [self populateWithModel:model_.get()]; } + + [menu_ setAutoenablesItems:NO]; [menu_ setDelegate:self]; return menu_; } From f78a66d988a42948c82508023eefb8db396b4e6d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 09:50:29 +0100 Subject: [PATCH 082/339] build: validate Chromium cookie authentication (#46326) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../actions/set-chromium-cookie/action.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/actions/set-chromium-cookie/action.yml b/.github/actions/set-chromium-cookie/action.yml index 70d0852624a64..4c37d14b7c7f2 100644 --- a/.github/actions/set-chromium-cookie/action.yml +++ b/.github/actions/set-chromium-cookie/action.yml @@ -17,6 +17,16 @@ runs: ${{ env.CHROMIUM_GIT_COOKIE }} __END__ eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null + + RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self) + if [[ $RESPONSE == ")]}'"* ]]; then + # Extract account email for verification + EMAIL=$(echo "$RESPONSE" | tail -c +5 | jq -r '.email // "No email found"') + echo "Cookie authentication successful - authenticated as: $EMAIL" + else + echo "Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE is set correctly" + echo $RESPONSE + fi - name: Set the git cookie from chromium.googlesource.com (Windows) if: ${{ runner.os == 'Windows' && env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} shell: cmd @@ -24,3 +34,15 @@ runs: git config --global http.cookiefile "%USERPROFILE%\.gitcookies" powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies" + curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt + + findstr /B /C:")]}'" response.txt > nul + if %ERRORLEVEL% EQU 0 ( + echo Cookie authentication successful + powershell -NoProfile -Command "& {$content = Get-Content -Raw response.txt; $content = $content.Substring(4); try { $json = ConvertFrom-Json $content; if($json.email) { Write-Host 'Authenticated as:' $json.email } else { Write-Host 'No email found in response' } } catch { Write-Host 'Error parsing JSON:' $_ }}" + ) else ( + echo Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE_WINDOWS_STRING is set correctly + type response.txt + ) + + del response.txt From 11c35626dae4db455ee47719a76a3fe76f849fda Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:27:28 -0500 Subject: [PATCH 083/339] perf: avoid redundant map lookups in `GetStorageMask()` (#46346) * perf: avoid a redundant map lookuop in GetStorageMask() Co-authored-by: Charles Kerr * perf: avoid a redundant map lookup in GetDataTypeMask() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_session.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 0e3f41e2bbd52..dcb89142e73ff 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -13,6 +13,7 @@ #include "base/command_line.h" #include "base/containers/fixed_flat_map.h" +#include "base/containers/map_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -139,9 +140,8 @@ uint32_t GetStorageMask(const std::vector& storage_types) { uint32_t storage_mask = 0; for (const auto& it : storage_types) { - auto type = base::ToLowerASCII(it); - if (Lookup.contains(type)) - storage_mask |= Lookup.at(type); + if (const uint32_t* val = base::FindOrNull(Lookup, base::ToLowerASCII(it))) + storage_mask |= *val; } return storage_mask; } @@ -179,9 +179,8 @@ BrowsingDataRemover::DataType GetDataTypeMask( const std::vector& data_types) { BrowsingDataRemover::DataType mask = 0u; for (const auto& type : data_types) { - if (kDataTypeLookup.contains(type)) { - mask |= kDataTypeLookup.at(type); - } + if (const auto* val = base::FindOrNull(kDataTypeLookup, type)) + mask |= *val; } return mask; } From b647c8935db71b0644392a02619019963850fe5a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 29 Mar 2025 08:12:15 -0500 Subject: [PATCH 084/339] perf: avoid 3x call to `GetID()` in RegisterPendingSiteInstance() (#46359) perf: avoid 3x call to GetID() in RegisterPendingSiteInstance() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_browser_client.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 2766dc7b4e7c9..510963b8f8bb0 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -446,13 +446,13 @@ void ElectronBrowserClient::RegisterPendingSiteInstance( content::SiteInstance* pending_site_instance) { // Remember the original web contents for the pending renderer process. auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); - auto* pending_process = pending_site_instance->GetProcess(); - pending_processes_[pending_process->GetID()] = web_contents; + const auto pending_process_id = pending_site_instance->GetProcess()->GetID(); + pending_processes_[pending_process_id] = web_contents; if (rfh->GetParent()) - renderer_is_subframe_.insert(pending_process->GetID()); + renderer_is_subframe_.insert(pending_process_id); else - renderer_is_subframe_.erase(pending_process->GetID()); + renderer_is_subframe_.erase(pending_process_id); } void ElectronBrowserClient::AppendExtraCommandLineSwitches( From 45c78deff8ba9db22a41978a5595af01d315ecfa Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 29 Mar 2025 08:30:58 -0500 Subject: [PATCH 085/339] perf: avoid a double-map lookup in `NotificationPresenter::RemoveNotification()` (#46354) perf: avoid a double-map lokup in NotificationPresenter::RemoveNotification() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/notifications/notification_presenter.cc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/shell/browser/notifications/notification_presenter.cc b/shell/browser/notifications/notification_presenter.cc index 16ac82a368288..a996dba971cc9 100644 --- a/shell/browser/notifications/notification_presenter.cc +++ b/shell/browser/notifications/notification_presenter.cc @@ -27,13 +27,8 @@ base::WeakPtr NotificationPresenter::CreateNotification( } void NotificationPresenter::RemoveNotification(Notification* notification) { - auto it = notifications_.find(notification); - if (it == notifications_.end()) { - return; - } - - notifications_.erase(notification); - delete notification; + if (const auto nh = notifications_.extract(notification)) + delete nh.value(); } void NotificationPresenter::CloseNotificationWithId( From 33df588e8e97cdfbd34d37d5b940742c380f521d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:59:07 -0500 Subject: [PATCH 086/339] perf: avoid double map lookup in HidChooserContext::DeviceRemoved() (#46362) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/hid/hid_chooser_context.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/hid/hid_chooser_context.cc b/shell/browser/hid/hid_chooser_context.cc index e31d77267caaa..6e288fa19e540 100644 --- a/shell/browser/hid/hid_chooser_context.cc +++ b/shell/browser/hid/hid_chooser_context.cc @@ -253,10 +253,10 @@ void HidChooserContext::DeviceAdded(device::mojom::HidDeviceInfoPtr device) { void HidChooserContext::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) { DCHECK(device); - DCHECK(devices_.contains(device->guid)); // Update the device list. - devices_.erase(device->guid); + const size_t n_erased = devices_.erase(device->guid); + DCHECK_EQ(n_erased, 1U); // Notify all device observers. for (auto& observer : device_observer_list_) From 7e80e8f610023ea7df88d77ba4d3ef3bb9e8ed1f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:39:48 +0200 Subject: [PATCH 087/339] refactor: use `v8::String::Empty()` when creating empty strings (#46371) refactor: use v8::String::Empty() when creating empty strings Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_app.cc | 4 ++-- shell/browser/ui/cocoa/electron_bundle_mover.mm | 2 +- shell/common/gin_converters/guid_converter.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc index 61238f86da142..3c19d191720b3 100644 --- a/shell/browser/api/electron_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -164,7 +164,7 @@ struct Converter { if (item_val == val) return gin::ConvertToV8(isolate, name); - return gin::ConvertToV8(isolate, ""); + return v8::String::Empty(isolate); } private: @@ -255,7 +255,7 @@ struct Converter { if (type_val == val) return gin::ConvertToV8(isolate, name); - return gin::ConvertToV8(isolate, ""); + return v8::String::Empty(isolate); } private: diff --git a/shell/browser/ui/cocoa/electron_bundle_mover.mm b/shell/browser/ui/cocoa/electron_bundle_mover.mm index 1ea871443cb71..3eb2692594442 100644 --- a/shell/browser/ui/cocoa/electron_bundle_mover.mm +++ b/shell/browser/ui/cocoa/electron_bundle_mover.mm @@ -30,7 +30,7 @@ case electron::BundlerMoverConflictType::kExistsAndRunning: return gin::StringToV8(isolate, "existsAndRunning"); default: - return gin::StringToV8(isolate, ""); + return v8::String::Empty(isolate); } } }; diff --git a/shell/common/gin_converters/guid_converter.h b/shell/common/gin_converters/guid_converter.h index ec18678fa2c6d..88dd8cf79262e 100644 --- a/shell/common/gin_converters/guid_converter.h +++ b/shell/common/gin_converters/guid_converter.h @@ -70,7 +70,7 @@ struct Converter { #if BUILDFLAG(IS_WIN) const GUID GUID_NULL = {}; if (val == GUID_NULL) { - return StringToV8(isolate, ""); + return v8::String::Empty(isolate); } else { std::wstring uid = base::win::WStringFromGUID(val); return StringToV8(isolate, base::SysWideToUTF8(uid)); From 8327ed0eea5cfc52ca21569993449e9ebca36e49 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 08:25:33 -0500 Subject: [PATCH 088/339] perf: improve temporaries in `WebWorkerObserver::WorkerScriptReadyForEvaluation()` (#46378) refactor: small refactor to WebWorkerObserver::WorkerScriptReadyForEvaluation() - replace a std::vector local with a compile-time array of std::string_view - remove .c_str() pessimization when making v8 Strings from string_views Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/renderer/web_worker_observer.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index a3a1caf8ded6d..d1953998cb72e 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -4,9 +4,11 @@ #include "shell/renderer/web_worker_observer.h" +#include #include #include "base/no_destructor.h" +#include "base/strings/strcat.h" #include "base/threading/thread_local.h" #include "shell/common/api/electron_bindings.h" #include "shell/common/gin_helper/event_emitter_caller.h" @@ -71,15 +73,14 @@ void WebWorkerObserver::WorkerScriptReadyForEvaluation( // is loaded. See corresponding change in node/init.ts. v8::Local global = worker_context->Global(); - std::vector keys = {"fetch", "Response", "FormData", - "Request", "Headers", "EventSource"}; - for (const auto& key : keys) { + for (const std::string_view key : + {"fetch", "Response", "FormData", "Request", "Headers", "EventSource"}) { v8::MaybeLocal value = - global->Get(worker_context, gin::StringToV8(isolate, key.c_str())); + global->Get(worker_context, gin::StringToV8(isolate, key)); if (!value.IsEmpty()) { - std::string blink_key = "blink" + key; + std::string blink_key = base::StrCat({"blink", key}); global - ->Set(worker_context, gin::StringToV8(isolate, blink_key.c_str()), + ->Set(worker_context, gin::StringToV8(isolate, blink_key), value.ToLocalChecked()) .Check(); } From 6b1d1bf893b68a9d5468d6578219292583bb6cba Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 09:14:51 -0500 Subject: [PATCH 089/339] perf: prefer `absl::flat_hash_set` over `std::unordered_set` (#46375) * perf: use absl::flat_hash_set in SpellCheckClient::SpellCheckText() Co-authored-by: Charles Kerr * perf: use absl::flat_hash_set in MessagePort::DisentanglePorts() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/message_port.cc | 5 +++-- shell/renderer/api/electron_api_spell_check_client.cc | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shell/browser/api/message_port.cc b/shell/browser/api/message_port.cc index a4ffcf5fe5771..7d48119484cb2 100644 --- a/shell/browser/api/message_port.cc +++ b/shell/browser/api/message_port.cc @@ -5,7 +5,6 @@ #include "shell/browser/api/message_port.h" #include -#include #include #include "base/containers/to_vector.h" @@ -20,6 +19,7 @@ #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/node_includes.h" #include "shell/common/v8_util.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "third_party/blink/public/common/messaging/transferable_message.h" #include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h" #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h" @@ -226,7 +226,8 @@ std::vector MessagePort::DisentanglePorts( if (ports.empty()) return {}; - std::unordered_set visited; + absl::flat_hash_set visited; + visited.reserve(ports.size()); // Walk the incoming array - if there are any duplicate ports, or null ports // or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec). diff --git a/shell/renderer/api/electron_api_spell_check_client.cc b/shell/renderer/api/electron_api_spell_check_client.cc index e3be831ee2761..46b8a03163a69 100644 --- a/shell/renderer/api/electron_api_spell_check_client.cc +++ b/shell/renderer/api/electron_api_spell_check_client.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -20,6 +19,7 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/function_template.h" #include "shell/common/gin_helper/microtasks_scope.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "third_party/blink/public/web/web_text_checking_completion.h" #include "third_party/blink/public/web/web_text_checking_result.h" #include "third_party/icu/source/common/unicode/uscript.h" @@ -182,9 +182,9 @@ void SpellCheckClient::SpellCheckText() { void SpellCheckClient::OnSpellCheckDone( const std::vector& misspelled_words) { + const absl::flat_hash_set misspelled{misspelled_words.begin(), + misspelled_words.end()}; std::vector results; - std::unordered_set misspelled(misspelled_words.begin(), - misspelled_words.end()); auto& word_list = pending_request_param_->wordlist(); From 17487df08d366275fc36de65f7747e2488f39ffd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:35:27 +0200 Subject: [PATCH 090/339] fix: flicker and ghosting in transparent windows on macOS (#46393) * fix: transparent flicker on MAS Co-authored-by: clavin * Gate condition on `IsTranslucent` instead Co-authored-by: clavin --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: clavin --- shell/browser/native_window_mac.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 37ff7da6d625f..dbeceb836ea77 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -196,6 +196,9 @@ static bool FromV8(v8::Isolate* isolate, params.type = views::Widget::InitParams::TYPE_WINDOW; // Allow painting before shown, to be later disabled in ElectronNSWindow. params.headless_mode = true; + if (IsTranslucent()) { + params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; + } params.native_widget = new ElectronNativeWidgetMac(this, windowType, styleMask, widget()); widget()->Init(std::move(params)); From 71e53c925e3f5efced68ce0f77cb5b93bde36259 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 1 Apr 2025 03:35:55 -0500 Subject: [PATCH 091/339] docs: deprecate setting ProtocolResponse.session to null (36-x-y) (#46366) docs: mark null ProtocolResponse.session as deprecated in v36 --- docs/breaking-changes.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index f09a7a3f3d7c1..b717ca9d1e00c 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -32,6 +32,16 @@ When calling `Session.clearStorageData(options)`, the `options.quota` property is deprecated. Since the `syncable` type was removed, there is only type left -- `'temporary'` -- so specifying it is unnecessary. +### Deprecated: `null` value for `session` property in `ProtocolResponse` + +Previously, setting the ProtocolResponse.session property to `null` +Would create a random independent session. This is no longer supported. + +Using single-purpose sessions here is discouraged due to overhead costs; +however, old code that needs to preserve this behavior can emulate it by +creating a random session with `session.fromPartition(some_random_string)` +and then using it in `ProtocolResponse.session`. + ### Deprecated: Extension methods and events on `session` `session.loadExtension`, `session.removeExtension`, `session.getExtension`, From 75e44e5f05f3c243074919f0d9dd42e8e73f80cf Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 08:49:44 -0500 Subject: [PATCH 092/339] feat: Corner Smoothing CSS rule (Reland) (#46385) * feat: Corner Smoothing CSS rule (Reland) Reland of #45185 Co-authored-by: Calvin * Fix patch conflicts Co-authored-by: clavin * fixup! Fix patch conflicts Co-authored-by: clavin * Update expected image The dashed border is subtly different. The new version is correct and the old one was incorrect. Co-authored-by: clavin --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Calvin Co-authored-by: clavin --- docs/README.md | 3 +- docs/api/corner-smoothing-css.md | 78 +++ docs/api/structures/web-preferences.md | 1 + docs/images/corner-smoothing-example-0.svg | 3 + docs/images/corner-smoothing-example-100.svg | 3 + docs/images/corner-smoothing-example-30.svg | 3 + docs/images/corner-smoothing-example-60.svg | 3 + docs/images/corner-smoothing-summary.svg | 15 + filenames.auto.gni | 1 + filenames.gni | 2 + patches/chromium/.patches | 1 + ...moothing_css_rule_and_blink_painting.patch | 485 ++++++++++++++++++ shell/browser/web_contents_preferences.cc | 5 + shell/browser/web_contents_preferences.h | 1 + shell/common/options_switches.h | 4 + shell/renderer/electron_smooth_round_rect.cc | 299 +++++++++++ shell/renderer/electron_smooth_round_rect.h | 36 ++ spec/api-corner-smoothing-spec.ts | 156 ++++++ .../corner-smoothing/shape/expected-false.png | Bin 0 -> 166667 bytes .../corner-smoothing/shape/expected-true.png | Bin 0 -> 148213 bytes .../api/corner-smoothing/shape/image.png | Bin 0 -> 738050 bytes .../api/corner-smoothing/shape/test.html | 136 +++++ .../system-ui-keyword/expected-darwin.png | Bin 0 -> 9236 bytes .../system-ui-keyword/expected-linux.png | Bin 0 -> 8055 bytes .../system-ui-keyword/expected-win32.png | Bin 0 -> 8055 bytes .../system-ui-keyword/test.html | 42 ++ 26 files changed, 1276 insertions(+), 1 deletion(-) create mode 100644 docs/api/corner-smoothing-css.md create mode 100644 docs/images/corner-smoothing-example-0.svg create mode 100644 docs/images/corner-smoothing-example-100.svg create mode 100644 docs/images/corner-smoothing-example-30.svg create mode 100644 docs/images/corner-smoothing-example-60.svg create mode 100644 docs/images/corner-smoothing-summary.svg create mode 100644 patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch create mode 100644 shell/renderer/electron_smooth_round_rect.cc create mode 100644 shell/renderer/electron_smooth_round_rect.h create mode 100644 spec/api-corner-smoothing-spec.ts create mode 100644 spec/fixtures/api/corner-smoothing/shape/expected-false.png create mode 100644 spec/fixtures/api/corner-smoothing/shape/expected-true.png create mode 100644 spec/fixtures/api/corner-smoothing/shape/image.png create mode 100644 spec/fixtures/api/corner-smoothing/shape/test.html create mode 100644 spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-darwin.png create mode 100644 spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-linux.png create mode 100644 spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-win32.png create mode 100644 spec/fixtures/api/corner-smoothing/system-ui-keyword/test.html diff --git a/docs/README.md b/docs/README.md index 98ae1ad9a79a4..01663cc017769 100644 --- a/docs/README.md +++ b/docs/README.md @@ -96,8 +96,9 @@ These individual tutorials expand on topics discussed in the guide above. * [Chrome Extensions Support](api/extensions.md) * [Breaking API Changes](breaking-changes.md) -### Custom DOM Elements: +### Custom Web Features: +* [`-electron-corner-smoothing` CSS Rule](api/corner-smoothing-css.md) * [`` Tag](api/webview-tag.md) * [`window.open` Function](api/window-open.md) diff --git a/docs/api/corner-smoothing-css.md b/docs/api/corner-smoothing-css.md new file mode 100644 index 0000000000000..029e74657ff3c --- /dev/null +++ b/docs/api/corner-smoothing-css.md @@ -0,0 +1,78 @@ +## CSS Rule: `-electron-corner-smoothing` + +> Smoothes out the corner rounding of the `border-radius` CSS rule. + +The rounded corners of elements with [the `border-radius` CSS rule](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius) can be smoothed out using the `-electron-corner-smoothing` CSS rule. This smoothness is very similar to Apple's "continuous" rounded corners in SwiftUI and Figma's "corner smoothing" control on design elements. + +![There is a black rectangle on the left using simple rounded corners, and a blue rectangle on the right using smooth rounded corners. In between those rectangles is a magnified view of the same corner from both rectangles overlapping to show the subtle difference in shape.](../images/corner-smoothing-summary.svg) + +Integrating with the operating system and its design language is important to many desktop applications. The shape of a rounded corner can be a subtle detail to many users. However, aligning closely to the system's design language that users are familiar with makes the application's design feel familiar too. Beyond matching the design language of macOS, designers may decide to use smoother round corners for many other reasons. + +`-electron-corner-smoothing` affects the shape of borders, outlines, and shadows on the target element. Mirroring the behavior of `border-radius`, smoothing will gradually back off if an element's size is too small for the chosen value. + +The `-electron-corner-smoothing` CSS rule is **only implemented for Electron** and has no effect in browsers. Avoid using this rule outside of Electron. This CSS rule is considered experimental and may require migration in the future if replaced by a CSS standard. + +### Example + +The following example shows the effect of corner smoothing at different percents. + +```css +.box { + width: 128px; + height: 128px; + background-color: cornflowerblue; + border-radius: 24px; + -electron-corner-smoothing: var(--percent); /* Column header in table below. */ +} +``` + +| 0% | 30% | 60% | 100% | +| --- | --- | --- | --- | +| ![A rectangle with round corners at 0% smoothness](../images/corner-smoothing-example-0.svg) | ![A rectangle with round corners at 30% smoothness](../images/corner-smoothing-example-30.svg) | ![A rectangle with round corners at 60% smoothness](../images/corner-smoothing-example-60.svg) | ![A rectangle with round corners at 100% smoothness](../images/corner-smoothing-example-100.svg) | + +### Matching the system UI + +Use the `system-ui` keyword to match the smoothness to the OS design language. + +```css +.box { + width: 128px; + height: 128px; + background-color: cornflowerblue; + border-radius: 24px; + -electron-corner-smoothing: system-ui; /* Match the system UI design. */ +} +``` + +| OS: | macOS | Windows, Linux | +| --- | --- | --- | +| Value: | `60%` | `0%` | +| Example: | ![A rectangle with round corners whose smoothness matches macOS](../images/corner-smoothing-example-60.svg) | ![A rectangle with round corners whose smoothness matches Windows and Linux](../images/corner-smoothing-example-0.svg) | + +### Controlling availibility + +This CSS rule can be disabled by setting [the `cornerSmoothingCSS` web preference](./structures/web-preferences.md) to `false`. + +```js +const myWindow = new BrowserWindow({ + // [...] + webPreferences: { + enableCornerSmoothingCSS: false // Disables the `-electron-corner-smoothing` CSS rule + } +}) +``` + +The CSS rule will still parse, but will have no visual effect. + +### Formal reference + +* **Initial value**: `0%` +* **Inherited**: No +* **Animatable**: No +* **Computed value**: As specified + +```css +-electron-corner-smoothing = + | + system-ui +``` diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index 26ead57c2329f..4e6710523615d 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -149,6 +149,7 @@ `WebContents` when the preferred size changes. Default is `false`. * `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent. * `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`. +* `enableCornerSmoothingCSS` boolean (optional) _Experimental_ - Whether the [`-electron-corner-smoothing` CSS rule](../corner-smoothing-css.md) is enabled. Default is `true`. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/docs/images/corner-smoothing-example-0.svg b/docs/images/corner-smoothing-example-0.svg new file mode 100644 index 0000000000000..928435ed0b006 --- /dev/null +++ b/docs/images/corner-smoothing-example-0.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-100.svg b/docs/images/corner-smoothing-example-100.svg new file mode 100644 index 0000000000000..34d0802239bef --- /dev/null +++ b/docs/images/corner-smoothing-example-100.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-30.svg b/docs/images/corner-smoothing-example-30.svg new file mode 100644 index 0000000000000..4996a25253ce0 --- /dev/null +++ b/docs/images/corner-smoothing-example-30.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-example-60.svg b/docs/images/corner-smoothing-example-60.svg new file mode 100644 index 0000000000000..cb1e68a8ff688 --- /dev/null +++ b/docs/images/corner-smoothing-example-60.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/images/corner-smoothing-summary.svg b/docs/images/corner-smoothing-summary.svg new file mode 100644 index 0000000000000..75bffa8e259d3 --- /dev/null +++ b/docs/images/corner-smoothing-summary.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/filenames.auto.gni b/filenames.auto.gni index a2514d5e99676..df963e2c831b6 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -14,6 +14,7 @@ auto_filenames = { "docs/api/content-tracing.md", "docs/api/context-bridge.md", "docs/api/cookies.md", + "docs/api/corner-smoothing-css.md", "docs/api/crash-reporter.md", "docs/api/debugger.md", "docs/api/desktop-capturer.md", diff --git a/filenames.gni b/filenames.gni index 0d58b9e147ffd..ea1637c990309 100644 --- a/filenames.gni +++ b/filenames.gni @@ -718,6 +718,8 @@ filenames = { "shell/renderer/electron_renderer_client.h", "shell/renderer/electron_sandboxed_renderer_client.cc", "shell/renderer/electron_sandboxed_renderer_client.h", + "shell/renderer/electron_smooth_round_rect.cc", + "shell/renderer/electron_smooth_round_rect.h", "shell/renderer/preload_realm_context.cc", "shell/renderer/preload_realm_context.h", "shell/renderer/preload_utils.cc", diff --git a/patches/chromium/.patches b/patches/chromium/.patches index cb7cb32aa758e..c6e8b731e2be8 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -134,6 +134,7 @@ fix_software_compositing_infinite_loop.patch fix_add_method_which_disables_headless_mode_on_native_widget.patch refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch +feat_corner_smoothing_css_rule_and_blink_painting.patch build_add_public_config_simdutf_config.patch revert_code_health_clean_up_stale_macwebcontentsocclusion.patch ignore_parse_errors_for_resolveshortcutproperties.patch diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch new file mode 100644 index 0000000000000..0e52381457975 --- /dev/null +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -0,0 +1,485 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Calvin Watford +Date: Mon, 9 Dec 2024 16:58:15 -0700 +Subject: feat: Corner Smoothing CSS rule and Blink painting + +This patch implements the `-electron-corner-smoothing` CSS rule by +making three primary changes to Blink: + +1. Adds the `-electron-corner-smoothing` CSS rule: + * Metadata in `blink/renderer/core/css/css_properties.json5` + * Parsing in `blink/renderer/core/css/properties/longhands/longhands_custom.cc` + * Other required definitions for all CSS rules (`css_property_id.mojom`, `css_property_equality.cc`) + +2. Modifies how Blink paints rounded rectangles: + * Augments `blink::ContouredRect` to add smoothness. + * Modifies graphics to handle smooth `ContouredRect`s, delegating to + `//electron/shell/renderer/electron_smooth_round_rect`. + +3. Adds a renderer preference / web setting: + * Controls whether the CSS rule is available. + * Mostly simple "plumbing" for the setting through blink. + +diff --git a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc +index 25cf6b544dcee15a9616b6963eaae0264aba3db6..13d5b30d00ce8dca96eb3bc5454f9d353375d4c6 100644 +--- a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc ++++ b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc +@@ -128,6 +128,8 @@ bool StructTraitselectron_corner_smoothing_css = data.electron_corner_smoothing_css(); ++ + return true; + } + +diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h +index cae096396b0635f1c4bba6ac8fee47fd957dc698..03db6cddab5cd1b9f3f7c90390bc53baa9e14b65 100644 +--- a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h ++++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h +@@ -91,6 +91,7 @@ struct BLINK_COMMON_EXPORT RendererPreferences { + bool caret_browsing_enabled{false}; + bool uses_platform_autofill{false}; + std::vector explicitly_allowed_network_ports; ++ bool electron_corner_smoothing_css; + + RendererPreferences(); + RendererPreferences(const RendererPreferences& other); +diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h +index 33b4bd3f0c9488f1013aea026c7fe559ba750cd8..6b4157199c14a4c276e65512e89f2429253aec5c 100644 +--- a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h ++++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h +@@ -275,6 +275,11 @@ struct BLINK_COMMON_EXPORT + return data.explicitly_allowed_network_ports; + } + ++ static const bool& electron_corner_smoothing_css( ++ const ::blink::RendererPreferences& data) { ++ return data.electron_corner_smoothing_css; ++ } ++ + static bool Read(blink::mojom::RendererPreferencesDataView, + ::blink::RendererPreferences* out); + }; +diff --git a/third_party/blink/public/mojom/renderer_preferences.mojom b/third_party/blink/public/mojom/renderer_preferences.mojom +index bbcec1dcdaaaf932b3d82c64e8aeb2e7c04b05bf..689205607a763c1d6e040069b1357d84e8ba4bd5 100644 +--- a/third_party/blink/public/mojom/renderer_preferences.mojom ++++ b/third_party/blink/public/mojom/renderer_preferences.mojom +@@ -201,4 +201,6 @@ struct RendererPreferences { + bool uses_platform_autofill = false; + + array explicitly_allowed_network_ports; ++ ++ bool electron_corner_smoothing_css; + }; +diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +index 3e3d56992ab135ee88257681f93e39a470192857..26e87c2be381c0fd7d5116d95a107082e2549eae 100644 +--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom ++++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +@@ -48,6 +48,7 @@ enum CSSSampleId { + kInternalForcedVisitedColor = 0, + kInternalOverflowBlock = 0, + kInternalOverflowInline = 0, ++ kElectronCornerSmoothing = 0, + + // This CSSSampleId represents page load for CSS histograms. It is recorded once + // per page visit for each CSS histogram being logged on the blink side and the +diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h +index a53b4901dde0dc83dce6c9b56616eef0d02d94a5..b419672af985f673f375fbb63b4d2b2c419e3e03 100644 +--- a/third_party/blink/public/web/web_settings.h ++++ b/third_party/blink/public/web/web_settings.h +@@ -285,6 +285,7 @@ class WebSettings { + virtual void SetRequireTransientActivationAndAuthorizationForSubAppsAPIs( + bool) = 0; + virtual void SetRootScrollbarThemeColor(std::optional) = 0; ++ virtual void SetCornerSmoothingCSS(bool) = 0; + + protected: + ~WebSettings() = default; +diff --git a/third_party/blink/renderer/build/scripts/core/css/css_properties.py b/third_party/blink/renderer/build/scripts/core/css/css_properties.py +index 753ba8990f722bafd1770a5e70307cff3764d3f1..16cec517d72887c089f85867e8e37c03199ab394 100755 +--- a/third_party/blink/renderer/build/scripts/core/css/css_properties.py ++++ b/third_party/blink/renderer/build/scripts/core/css/css_properties.py +@@ -311,7 +311,13 @@ class CSSProperties(object): + name_without_leading_dash = property_.name.original + if name_without_leading_dash.startswith('-'): + name_without_leading_dash = name_without_leading_dash[1:] ++ # Extra sort level to avoid -internal-* properties being assigned ++ # values too large to fit in a byte. ++ internal_weight = 0 ++ if property_.name.original.startswith('-internal'): ++ internal_weight = -1 + property_.sorting_key = (-property_.priority, ++ internal_weight, + name_without_leading_dash) + + sorting_keys = {} +diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 +index 6cf39b4a15ac290891d56a8d1d7b30846a329f79..5a0d840d5c01fb1ed95bacd36cc4f01443afdf94 100644 +--- a/third_party/blink/renderer/core/css/css_properties.json5 ++++ b/third_party/blink/renderer/core/css/css_properties.json5 +@@ -8724,6 +8724,24 @@ + property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + }, + ++ { ++ name: "-electron-corner-smoothing", ++ property_methods: ["ParseSingleValue"], ++ field_group: "*", ++ field_template: "external", ++ // To keep this patch small, Length is used instead of a more descriptive ++ // custom type. ++ // - `system-ui` = `Length::Auto()` ++ // - percent = `Length::Percent` ++ type_name: "Length", ++ converter: "ConvertCornerSmoothing", ++ keywords: ["system-ui"], ++ default_value: "Length::None()", ++ typedom_types: ["Keyword", "Percentage"], ++ is_border_radius: true, ++ invalidate: ["paint", "border-radius", "clip"], ++ }, ++ + // Visited properties. + { + name: "-internal-visited-color", +diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc +index 998fb2cfb682e61d89bb6f832cd91efa658f9773..8ad689bd9327569d26eb5f449a707d6b0d7c2536 100644 +--- a/third_party/blink/renderer/core/css/css_property_equality.cc ++++ b/third_party/blink/renderer/core/css/css_property_equality.cc +@@ -346,6 +346,8 @@ bool CSSPropertyEquality::PropertiesEqual(const PropertyHandle& property, + return a.DominantBaseline() == b.DominantBaseline(); + case CSSPropertyID::kDynamicRangeLimit: + return a.GetDynamicRangeLimit() == b.GetDynamicRangeLimit(); ++ case CSSPropertyID::kElectronCornerSmoothing: ++ return a.ElectronCornerSmoothing() == b.ElectronCornerSmoothing(); + case CSSPropertyID::kEmptyCells: + return a.EmptyCells() == b.EmptyCells(); + case CSSPropertyID::kFill: +diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +index c1aa3851a8530f1993de160773d3fae107c4d8bd..d0b87808b0d0466473d21720e44366228daed218 100644 +--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc ++++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +@@ -11857,5 +11857,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( + CSSValueID::kNone>(stream); + } + ++const CSSValue* ElectronCornerSmoothing::ParseSingleValue( ++ CSSParserTokenStream& stream, ++ const CSSParserContext& context, ++ const CSSParserLocalContext&) const { ++ // Fail parsing if this rule is disabled by document settings. ++ if (Settings* settings = context.GetDocument()->GetSettings(); ++ settings && !settings->GetElectronCornerSmoothingCSS()) { ++ return nullptr; ++ } ++ ++ // Try to parse `system-ui` keyword first. ++ if (auto* ident = ++ css_parsing_utils::ConsumeIdent(stream)) { ++ return ident; ++ } ++ // Try to parse as percent. ++ return css_parsing_utils::ConsumePercent( ++ stream, context, CSSPrimitiveValue::ValueRange::kNonNegative); ++} ++ + } // namespace css_longhand + } // namespace blink +diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +index 797c8e2d7ee777bcd88e0e4e6a65992342c2a098..c8d024213eb4dfe1ae82e0543f066df55555213e 100644 +--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc ++++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +@@ -3861,4 +3861,12 @@ PositionArea StyleBuilderConverter::ConvertPositionArea( + return PositionArea(span[0], span[1], span[2], span[3]); + } + ++Length StyleBuilderConverter::ConvertCornerSmoothing(StyleResolverState& state, const CSSValue& value) { ++ auto* ident = DynamicTo(value); ++ if (ident && ident->GetValueID() == CSSValueID::kSystemUi) { ++ return Length::Auto(); ++ } ++ return ConvertLength(state, value); ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +index c0f4544a38dc486708dec5a4b3646fb3f15ff2e0..8b3d4e95fb690f9e7b38265be0a77d6e49271944 100644 +--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h ++++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +@@ -421,6 +421,8 @@ class StyleBuilderConverter { + const CSSValue&); + + static PositionArea ConvertPositionArea(StyleResolverState&, const CSSValue&); ++ ++ static Length ConvertCornerSmoothing(StyleResolverState&, const CSSValue&); + }; + + template +diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc +index 4a29a2200eaab5084078e928a68c862296c6ff91..fcd879deec0e68b3b6988402d19570cf0065daa2 100644 +--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc ++++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc +@@ -816,4 +816,8 @@ void WebSettingsImpl::SetRootScrollbarThemeColor( + settings_->SetRootScrollbarThemeColor(theme_color); + } + ++void WebSettingsImpl::SetCornerSmoothingCSS(bool available) { ++ settings_->SetElectronCornerSmoothingCSS(available); ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h +index eabcddfa5f17497ef0611fa43f77dd13e2a54e00..96266c4f8c17b589f3d9c549e2836a147b7401ce 100644 +--- a/third_party/blink/renderer/core/exported/web_settings_impl.h ++++ b/third_party/blink/renderer/core/exported/web_settings_impl.h +@@ -237,6 +237,7 @@ class CORE_EXPORT WebSettingsImpl final : public WebSettings { + void SetRequireTransientActivationAndAuthorizationForSubAppsAPIs( + bool) override; + void SetRootScrollbarThemeColor(std::optional) override; ++ void SetCornerSmoothingCSS(bool) override; + + bool RenderVSyncNotificationEnabled() const { + return render_v_sync_notification_enabled_; +diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc +index 9810991e0a5d8b931a70e056b6651b8e5fdb9881..2b37a0209d370629f08e9065a22b92ff52053141 100644 +--- a/third_party/blink/renderer/core/exported/web_view_impl.cc ++++ b/third_party/blink/renderer/core/exported/web_view_impl.cc +@@ -3574,6 +3574,9 @@ void WebViewImpl::UpdateRendererPreferences( + #endif + + MaybePreloadSystemFonts(GetPage()); ++ ++ GetSettings()->SetCornerSmoothingCSS( ++ renderer_preferences_.electron_corner_smoothing_css); + } + + void WebViewImpl::SetHistoryIndexAndLength(int32_t history_index, +diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5 +index f4cdee12ea4352067f5de3e074e43d51ef56d2e5..6377e4b1ea8aa46b0bf69f8420b6c439bea70dba 100644 +--- a/third_party/blink/renderer/core/frame/settings.json5 ++++ b/third_party/blink/renderer/core/frame/settings.json5 +@@ -1261,5 +1261,10 @@ + initial: false, + type: "bool" + }, ++ { ++ name: "electronCornerSmoothingCSS", ++ initial: true, ++ invalidate: ["Style"], ++ }, + ], + } +diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc +index 68dbf4accafc0ce8100d6d488195e9dcde8b1502..dcd13e67acde42b181b219b2f690e2fc76ad917d 100644 +--- a/third_party/blink/renderer/core/paint/box_painter_base.cc ++++ b/third_party/blink/renderer/core/paint/box_painter_base.cc +@@ -324,8 +324,9 @@ void BoxPainterBase::PaintNormalBoxShadow(const PaintInfo& info, + if (has_border_radius) { + FloatRoundedRect rounded_fill_rect(fill_rect, border.GetRadii()); + ApplySpreadToShadowShape(rounded_fill_rect, shadow.Spread()); +- context.FillRoundedRect( +- rounded_fill_rect, Color::kBlack, ++ ContouredRect contoured_fill_rect(rounded_fill_rect, border.GetCornerCurvature()); ++ context.FillContouredRect( ++ contoured_fill_rect, Color::kBlack, + PaintAutoDarkMode(style, DarkModeFilter::ElementRole::kBackground)); + } else { + fill_rect.Outset(shadow.Spread()); +@@ -413,16 +414,20 @@ void BoxPainterBase::PaintInsetBoxShadow(const PaintInfo& info, + AdjustRectForSideClipping(inner_rect, shadow, sides_to_include); + FloatRoundedRect inner_rounded_rect(inner_rect, bounds.GetRadii()); + ApplySpreadToShadowShape(inner_rounded_rect, -shadow.Spread()); ++ ContouredRect contoured_bounds( ++ bounds, ContouredBorderGeometry::ContouredBorder( ++ style, PhysicalRect::EnclosingRect(bounds.Rect())) ++ .GetCornerCurvature()); + if (inner_rounded_rect.IsEmpty()) { + // |AutoDarkMode::Disabled()| is used because |shadow_color| has already + // been adjusted for dark mode. +- context.FillRoundedRect(bounds, shadow_color, AutoDarkMode::Disabled()); ++ context.FillContouredRect(contoured_bounds, shadow_color, AutoDarkMode::Disabled()); + continue; + } + GraphicsContextStateSaver state_saver(context); + if (bounds.IsRounded()) { + // TODO(crbug.com/397459628) render corner-shape with box-shadow +- context.ClipContouredRect(ContouredRect(bounds)); ++ context.ClipContouredRect(contoured_bounds); + } else { + context.Clip(bounds.Rect()); + } +diff --git a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc +index b96a3ba1e16b15807086c8e6a256b256b48e8adb..1396fd3214e18e1ded8fd8a83d964c8c824fbc5e 100644 +--- a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc ++++ b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc +@@ -43,6 +43,24 @@ float EffectiveCurvature(Superellipse superellipse, const gfx::SizeF& radius) { + : superellipse.Exponent(); + } + ++float SmoothnessFromLength(const Length& length) { ++ // `none` = 0% ++ if (length.IsNone()) { ++ return 0.0f; ++ } ++ ++ // `system-ui` keyword, represented internally as "auto" length ++ if (length.HasAuto()) { ++#if BUILDFLAG(IS_MAC) ++ return 0.6f; ++#else ++ return 0.0f; ++#endif // BUILDFLAG(IS_MAC) ++ } ++ ++ return length.Percent() / 100.0f; ++} ++ + ContouredRect::CornerCurvature CalcCurvatureFor( + const ComputedStyle& style, + const FloatRoundedRect::Radii& radii) { +@@ -50,7 +68,8 @@ ContouredRect::CornerCurvature CalcCurvatureFor( + EffectiveCurvature(style.CornerTopLeftShape(), radii.TopLeft()), + EffectiveCurvature(style.CornerTopRightShape(), radii.TopRight()), + EffectiveCurvature(style.CornerBottomRightShape(), radii.BottomRight()), +- EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft())); ++ EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft()), ++ SmoothnessFromLength(style.ElectronCornerSmoothing())); + } + + ContouredRect PixelSnappedContouredBorderInternal( +diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn +index ae923ecf6c58d608ed3583312edb48ecb6b7f576..759d991162702d31e77590474bc5764c8fd8229b 100644 +--- a/third_party/blink/renderer/platform/BUILD.gn ++++ b/third_party/blink/renderer/platform/BUILD.gn +@@ -1642,6 +1642,8 @@ component("platform") { + "widget/widget_base.h", + "widget/widget_base_client.h", + "windows_keyboard_codes.h", ++ "//electron/shell/renderer/electron_smooth_round_rect.h", ++ "//electron/shell/renderer/electron_smooth_round_rect.cc", + ] + + sources -= blink_platform_avx_files +diff --git a/third_party/blink/renderer/platform/geometry/contoured_rect.h b/third_party/blink/renderer/platform/geometry/contoured_rect.h +index 1a5d76b145307c11ac71cd5840f7ca166655fde2..a40aee431d9ecf8bdb011ccc4e8a9ecd813ffe3a 100644 +--- a/third_party/blink/renderer/platform/geometry/contoured_rect.h ++++ b/third_party/blink/renderer/platform/geometry/contoured_rect.h +@@ -42,19 +42,29 @@ class PLATFORM_EXPORT ContouredRect { + constexpr CornerCurvature(float top_left, + float top_right, + float bottom_right, +- float bottom_left) ++ float bottom_left, ++ float smoothness) + : top_left_(top_left), + top_right_(top_right), + bottom_right_(bottom_right), +- bottom_left_(bottom_left) { ++ bottom_left_(bottom_left), ++ smoothness_(smoothness) { + DCHECK_GE(top_left, 0); + DCHECK_GE(top_right, 0); + DCHECK_GE(bottom_right, 0); + DCHECK_GE(bottom_left, 0); ++ DCHECK_GE(smoothness, 0); + } ++ constexpr CornerCurvature(float top_left, ++ float top_right, ++ float bottom_right, ++ float bottom_left) ++ : CornerCurvature(top_left, top_right, bottom_right, bottom_left, 0) {} ++ ++ constexpr bool IsSmooth() const { return smoothness_ > 0.0f; } + + constexpr bool IsRound() const { +- return (top_left_ == kRound) && IsUniform(); ++ return (top_left_ == kRound) && IsUniform() && !IsSmooth(); + } + + constexpr bool IsUniform() const { +@@ -66,6 +76,7 @@ class PLATFORM_EXPORT ContouredRect { + constexpr float TopRight() const { return top_right_; } + constexpr float BottomRight() const { return bottom_right_; } + constexpr float BottomLeft() const { return bottom_left_; } ++ constexpr float Smoothness() const { return smoothness_; } + + constexpr bool operator==(const CornerCurvature&) const = default; + +@@ -76,6 +87,7 @@ class PLATFORM_EXPORT ContouredRect { + float top_right_ = kRound; + float bottom_right_ = kRound; + float bottom_left_ = kRound; ++ float smoothness_ = 0.0f; + }; + + constexpr ContouredRect() = default; +diff --git a/third_party/blink/renderer/platform/geometry/path.cc b/third_party/blink/renderer/platform/geometry/path.cc +index 4b63f7f3e113e77bf91810b91c5fad1b6bf5de92..6121bd490717ce6bf4ba7d933e1a9f3eae1752e1 100644 +--- a/third_party/blink/renderer/platform/geometry/path.cc ++++ b/third_party/blink/renderer/platform/geometry/path.cc +@@ -33,6 +33,7 @@ + + #include + ++#include "electron/shell/renderer/electron_smooth_round_rect.h" + #include "third_party/blink/renderer/platform/geometry/contoured_rect.h" + #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h" + #include "third_party/blink/renderer/platform/geometry/skia_geometry_utils.h" +@@ -660,6 +661,18 @@ void Path::AddContouredRect(const ContouredRect& contoured_rect) { + return; + } + ++ // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners ++ if (contoured_rect.GetCornerCurvature().IsSmooth()) { ++ const gfx::RectF& box = rect.Rect(); ++ const FloatRoundedRect::Radii& radii = rect.GetRadii(); ++ path_.addPath(electron::DrawSmoothRoundRect( ++ box.x(), box.y(), box.width(), box.height(), ++ std::min(contoured_rect.GetCornerCurvature().Smoothness(), 1.0f), ++ radii.TopLeft().width(), radii.TopRight().width(), ++ radii.BottomRight().width(), radii.BottomLeft().width())); ++ return; ++ } ++ + const ContouredRect::CornerCurvature& curvature = + contoured_rect.GetCornerCurvature(); + path_.moveTo(gfx::PointFToSkPoint(rect.TopLeftCorner().top_right())); +diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc +index 6361cc655af8c2bef6803efe6f3c382c1eadb851..9439df63a7d265d1f93c89c275d84a8a1dde30c6 100644 +--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc ++++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc +@@ -924,6 +924,19 @@ void GraphicsContext::FillRectWithRoundedHole( + DarkModeFlags(this, auto_dark_mode, flags)); + } + ++void GraphicsContext::FillContouredRect(const ContouredRect& contoured_rect, ++ const Color& color, ++ const AutoDarkMode& auto_dark_mode) { ++ if (contoured_rect.HasRoundCurvature()) { ++ FillRoundedRect(contoured_rect.AsRoundedRect(), color, auto_dark_mode); ++ return; ++ } ++ ++ cc::PaintFlags flags = ImmutableState()->FillFlags(); ++ flags.setColor(color.toSkColor4f()); ++ canvas_->drawPath(contoured_rect.GetPath().GetSkPath(), flags); ++} ++ + void GraphicsContext::FillEllipse(const gfx::RectF& ellipse, + const AutoDarkMode& auto_dark_mode) { + DrawOval(gfx::RectFToSkRect(ellipse), ImmutableState()->FillFlags(), +diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h +index 632b0ec1faebc87d13a5538812333bf14f9e402a..ee51cb455600f507e3a97fe3e6f293ff0f47bbd6 100644 +--- a/third_party/blink/renderer/platform/graphics/graphics_context.h ++++ b/third_party/blink/renderer/platform/graphics/graphics_context.h +@@ -318,6 +318,9 @@ class PLATFORM_EXPORT GraphicsContext { + const FloatRoundedRect& rounded_hole_rect, + const Color&, + const AutoDarkMode& auto_dark_mode); ++ void FillContouredRect(const ContouredRect& contoured_rect, ++ const Color& color, ++ const AutoDarkMode& auto_dark_mode); + + void StrokeRect(const gfx::RectF&, + const AutoDarkMode& auto_dark_mode); diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index 5066bc2e00d20..7096c8bc5f81f 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -149,6 +149,7 @@ void WebContentsPreferences::Clear() { preload_path_ = std::nullopt; v8_cache_options_ = blink::mojom::V8CacheOptions::kDefault; deprecated_paste_enabled_ = false; + corner_smoothing_css_ = true; #if BUILDFLAG(IS_MAC) scroll_bounce_ = false; @@ -228,6 +229,8 @@ void WebContentsPreferences::SetFromDictionary( if (web_preferences.Get(options::kDisableBlinkFeatures, &disable_blink_features)) disable_blink_features_ = disable_blink_features; + web_preferences.Get(options::kEnableCornerSmoothingCSS, + &corner_smoothing_css_); base::FilePath::StringType preload_path; if (web_preferences.Get(options::kPreloadScript, &preload_path)) { @@ -478,6 +481,8 @@ void WebContentsPreferences::OverrideWebkitPrefs( prefs->v8_cache_options = v8_cache_options_; prefs->dom_paste_enabled = deprecated_paste_enabled_; + + renderer_prefs->electron_corner_smoothing_css = corner_smoothing_css_; } WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPreferences); diff --git a/shell/browser/web_contents_preferences.h b/shell/browser/web_contents_preferences.h index 4bb6132752cc1..1e0b47631f47e 100644 --- a/shell/browser/web_contents_preferences.h +++ b/shell/browser/web_contents_preferences.h @@ -134,6 +134,7 @@ class WebContentsPreferences std::optional preload_path_; blink::mojom::V8CacheOptions v8_cache_options_; bool deprecated_paste_enabled_ = false; + bool corner_smoothing_css_; #if BUILDFLAG(IS_MAC) bool scroll_bounce_; diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 7f2608410c4a0..6d0895d4cbb36 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -210,6 +210,10 @@ inline constexpr std::string_view kSpellcheck = "spellcheck"; // document.execCommand("paste"). inline constexpr std::string_view kEnableDeprecatedPaste = "enableDeprecatedPaste"; + +// Whether the -electron-corner-smoothing CSS rule is enabled. +inline constexpr std::string_view kEnableCornerSmoothingCSS = + "enableCornerSmoothingCSS"; } // namespace options // Following are actually command line switches, should be moved to other files. diff --git a/shell/renderer/electron_smooth_round_rect.cc b/shell/renderer/electron_smooth_round_rect.cc new file mode 100644 index 0000000000000..dcd681387c1f6 --- /dev/null +++ b/shell/renderer/electron_smooth_round_rect.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2024 Salesforce, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "electron/shell/renderer/electron_smooth_round_rect.h" + +#include +#include "base/check.h" + +namespace electron { + +namespace { + +// Applies quarter rotations (n * 90°) to a point relative to the origin. +constexpr SkPoint QuarterRotate(const SkPoint& p, + unsigned int quarter_rotations) { + switch (quarter_rotations % 4) { + case 0: + return p; + case 1: + return {-p.y(), p.x()}; + case 2: + return {-p.x(), -p.y()}; + // case 3: + default: + return {p.y(), -p.x()}; + } +} + +// Edge length consumed for a given smoothness and corner radius. +constexpr float LengthForCornerSmoothness(float smoothness, float radius) { + return (1.0f + smoothness) * radius; +} + +// The smoothness value when consuming an edge length for a corner with a given +// radius. +// +// This function complements `LengthForCornerSmoothness`: +// SmoothnessForCornerLength(LengthForCornerSmoothness(s, r), r) = s +constexpr float SmoothnessForCornerLength(float length, float radius) { + return (length / radius) - 1.0f; +} + +// Geometric measurements for constructing the curves of smooth round corners on +// a rectangle. +// +// Each measurement's value is relative to the rectangle's natural corner point. +// An "offset" measurement is a one-dimensional length and a "vector" +// measurement is a two-dimensional pair of lengths. +// +// Each measurement's direction is relative to the direction of an edge towards +// the corner. Offsets are in the same direction as the edge toward the corner. +// For vectors, the X direction is parallel and the Y direction is +// perpendicular. +struct CurveGeometry { + constexpr CurveGeometry(float radius, float smoothness); + + constexpr SkVector edge_connecting_vector() const { + return {edge_connecting_offset, 0.0f}; + } + constexpr SkVector edge_curve_vector() const { + return {edge_curve_offset, 0.0f}; + } + constexpr SkVector arc_curve_vector() const { + return {arc_curve_offset, 0.0f}; + } + constexpr SkVector arc_connecting_vector_transposed() const { + return {arc_connecting_vector.y(), arc_connecting_vector.x()}; + } + + // The point where the edge connects to the curve. + float edge_connecting_offset; + + // The control point for the curvature where the edge connects to the curve. + float edge_curve_offset; + + // The control point for the curvature where the arc connects to the curve. + float arc_curve_offset; + + // The point where the arc connects to the curve. + SkVector arc_connecting_vector; +}; + +constexpr CurveGeometry::CurveGeometry(float radius, float smoothness) { + edge_connecting_offset = LengthForCornerSmoothness(smoothness, radius); + + float arc_angle = (std::numbers::pi / 4.0f) * smoothness; + + arc_connecting_vector = + SkVector::Make(1.0f - std::sin(arc_angle), 1.0f - std::cos(arc_angle)) * + radius; + + arc_curve_offset = (1.0f - std::tan(arc_angle / 2.0f)) * radius; + + constexpr float EDGE_CURVE_POINT_RATIO = 2.0f / 3.0f; + edge_curve_offset = + edge_connecting_offset - + ((edge_connecting_offset - arc_curve_offset) * EDGE_CURVE_POINT_RATIO); +} + +void DrawCorner(SkPath& path, + float radius, + const CurveGeometry& curve1, + const CurveGeometry& curve2, + const SkPoint& corner, + unsigned int quarter_rotations) { + // Move/Line to the edge connecting point + { + SkPoint edge_connecting_point = + corner + + QuarterRotate(curve1.edge_connecting_vector(), quarter_rotations + 1); + + if (quarter_rotations == 0) { + path.moveTo(edge_connecting_point); + } else { + path.lineTo(edge_connecting_point); + } + } + + // Draw the first smoothing curve + { + SkPoint edge_curve_point = + corner + + QuarterRotate(curve1.edge_curve_vector(), quarter_rotations + 1); + SkPoint arc_curve_point = corner + QuarterRotate(curve1.arc_curve_vector(), + quarter_rotations + 1); + SkPoint arc_connecting_point = + corner + QuarterRotate(curve1.arc_connecting_vector_transposed(), + quarter_rotations); + path.cubicTo(edge_curve_point, arc_curve_point, arc_connecting_point); + } + + // Draw the arc + { + SkPoint arc_connecting_point = + corner + QuarterRotate(curve2.arc_connecting_vector, quarter_rotations); + path.arcTo(SkPoint::Make(radius, radius), 0.0f, SkPath::kSmall_ArcSize, + SkPathDirection::kCW, arc_connecting_point); + } + + // Draw the second smoothing curve + { + SkPoint arc_curve_point = + corner + QuarterRotate(curve2.arc_curve_vector(), quarter_rotations); + SkPoint edge_curve_point = + corner + QuarterRotate(curve2.edge_curve_vector(), quarter_rotations); + SkPoint edge_connecting_point = + corner + + QuarterRotate(curve2.edge_connecting_vector(), quarter_rotations); + path.cubicTo(arc_curve_point, edge_curve_point, edge_connecting_point); + } +} + +// Constrains the smoothness of two corners along the same edge. +// +// If the smoothness value needs to be constrained, it will try to keep the +// ratio of the smoothness values the same as the ratio of the radii +// (`s1/s2 = r1/r2`). +constexpr std::pair ConstrainSmoothness(float size, + float smoothness, + float radius1, + float radius2) { + float edge_consumed1 = LengthForCornerSmoothness(smoothness, radius1); + float edge_consumed2 = LengthForCornerSmoothness(smoothness, radius2); + + // If both corners fit within the edge size then keep the smoothness + if (edge_consumed1 + edge_consumed2 <= size) { + return {smoothness, smoothness}; + } + + float ratio = radius1 / (radius1 + radius2); + float length1 = size * ratio; + float length2 = size - length1; + + float smoothness1 = + std::max(SmoothnessForCornerLength(length1, radius1), 0.0f); + float smoothness2 = + std::max(SmoothnessForCornerLength(length2, radius2), 0.0f); + + return {smoothness1, smoothness2}; +} + +} // namespace + +// The algorithm for drawing this shape is based on the article +// "Desperately seeking squircles" by Daniel Furse. A brief summary: +// +// In a simple round rectangle, each corner of a plain rectangle is replaced +// with a quarter circle and connected to each edge of the corner. +// +// Edge +// ←------→ ↖ +// ----------o--__ `、 Corner (Quarter Circle) +// `、 `、 +// | ↘ +// | +// o +// | ↑ +// | | Edge +// | ↓ +// +// This creates sharp changes in the curvature at the points where the edge +// transitions to the corner, suddenly curving at a constant rate. Our primary +// goal is to smooth out that curvature profile, slowly ramping up and back +// down, like turning a car with the steering wheel. +// +// To achieve this, we "expand" that point where the circular corner meets the +// straight edge in both directions. We use this extra space to construct a +// small curved path that eases the curvature from the edge to the corner +// circle. +// +// Edge Curve +// ←--→ ←-----→ +// -----o----___o ↖、 Corner (Circular Arc) +// `、 `↘ +// o +// | ↑ +// | | Curve +// | ↓ +// o +// | ↕ Edge +// +// Each curve is implemented as a cubic Bézier curve, composed of four control +// points: +// +// * The first control point connects to the straight edge. +// * The fourth (last) control point connects to the circular arc. +// * The second & third control points both lie on the infinite line extending +// from the straight edge. +// * The third control point (only) also lies on the infinite line tangent to +// the circular arc at the fourth control point. +// +// The first and fourth (last) control points are firmly fixed by attaching to +// the straight edge and circular arc, respectively. The third control point is +// fixed at the intersection between the edge and tangent lines. The second +// control point, however, is only constrained to the infinite edge line, but +// we may choose where. +SkPath DrawSmoothRoundRect(float x, + float y, + float width, + float height, + float smoothness, + float top_left_radius, + float top_right_radius, + float bottom_right_radius, + float bottom_left_radius) { + DCHECK(0.0f <= smoothness && smoothness <= 1.0f); + + // Assume the radii are already constrained within the rectangle size + DCHECK(top_left_radius + top_right_radius <= width); + DCHECK(bottom_left_radius + bottom_right_radius <= width); + DCHECK(top_left_radius + bottom_left_radius <= height); + DCHECK(top_right_radius + bottom_right_radius <= height); + + if (width <= 0.0f || height <= 0.0f) { + return SkPath(); + } + + // Constrain the smoothness for each curve on each edge + auto [top_left_smoothness, top_right_smoothness] = + ConstrainSmoothness(width, smoothness, top_left_radius, top_right_radius); + auto [right_top_smoothness, right_bottom_smoothness] = ConstrainSmoothness( + height, smoothness, top_right_radius, bottom_right_radius); + auto [bottom_left_smoothness, bottom_right_smoothness] = ConstrainSmoothness( + width, smoothness, bottom_left_radius, bottom_right_radius); + auto [left_top_smoothness, left_bottom_smoothness] = ConstrainSmoothness( + height, smoothness, top_left_radius, bottom_left_radius); + + SkPath path; + + // Top left corner + DrawCorner(path, top_left_radius, + CurveGeometry(top_left_radius, left_top_smoothness), + CurveGeometry(top_left_radius, top_left_smoothness), + SkPoint::Make(x, y), 0); + + // Top right corner + DrawCorner(path, top_right_radius, + CurveGeometry(top_right_radius, top_right_smoothness), + CurveGeometry(top_right_radius, right_top_smoothness), + SkPoint::Make(x + width, y), 1); + + // Bottom right corner + DrawCorner(path, bottom_right_radius, + CurveGeometry(bottom_right_radius, right_bottom_smoothness), + CurveGeometry(bottom_right_radius, bottom_right_smoothness), + SkPoint::Make(x + width, y + height), 2); + + // Bottom left corner + DrawCorner(path, bottom_left_radius, + CurveGeometry(bottom_left_radius, bottom_left_smoothness), + CurveGeometry(bottom_left_radius, left_bottom_smoothness), + SkPoint::Make(x, y + height), 3); + + path.close(); + return path; +} + +} // namespace electron diff --git a/shell/renderer/electron_smooth_round_rect.h b/shell/renderer/electron_smooth_round_rect.h new file mode 100644 index 0000000000000..1c84da904838c --- /dev/null +++ b/shell/renderer/electron_smooth_round_rect.h @@ -0,0 +1,36 @@ +// Copyright (c) 2024 Salesforce, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ELECTRON_SHELL_RENDERER_API_ELECTRON_SMOOTH_ROUND_RECT_H_ +#define ELECTRON_SHELL_RENDERER_API_ELECTRON_SMOOTH_ROUND_RECT_H_ + +#include "third_party/skia/include/core/SkPath.h" + +namespace electron { + +// Draws a rectangle that has smooth round corners for a given "smoothness" +// value between 0.0 and 1.0 (representing 0% to 100%). +// +// The smoothness value determines how much edge length can be consumed by each +// corner, scaling with respect to that corner's radius. The smoothness will +// dynamically scale back if there is not enough edge length, similar to how +// the corner radius backs off when there isn't enough edge length. +// +// Each corner's radius can be supplied independently. Corner radii are expected +// to already be balanced (Radius1 + Radius2 <= Length, for each given side). +// +// Elliptical corner radii are not currently supported. +SkPath DrawSmoothRoundRect(float x, + float y, + float width, + float height, + float smoothness, + float top_left_radius, + float top_right_radius, + float bottom_right_radius, + float bottom_left_radius); + +} // namespace electron + +#endif // ELECTRON_SHELL_RENDERER_API_ELECTRON_SMOOTH_ROUND_RECT_H_ diff --git a/spec/api-corner-smoothing-spec.ts b/spec/api-corner-smoothing-spec.ts new file mode 100644 index 0000000000000..a25a787db7625 --- /dev/null +++ b/spec/api-corner-smoothing-spec.ts @@ -0,0 +1,156 @@ +import { NativeImage, nativeImage } from 'electron/common'; +import { BrowserWindow } from 'electron/main'; + +import { AssertionError, expect } from 'chai'; + +import path = require('node:path'); + +import { createArtifact } from './lib/artifacts'; +import { ifdescribe } from './lib/spec-helpers'; +import { closeAllWindows } from './lib/window-helpers'; + +const FIXTURE_PATH = path.resolve( + __dirname, + 'fixtures', + 'api', + 'corner-smoothing' +); + +/** + * Rendered images may "match" but slightly differ due to rendering artifacts + * like anti-aliasing and vector path resolution, among others. This tolerance + * is the cutoff for whether two images "match" or not. + * + * From testing, matching images were found to have an average global difference + * up to about 1.3 and mismatched images were found to have a difference of at + * least about 7.3. + * + * See the documentation on `compareImages` for more information. + */ +const COMPARISON_TOLERANCE = 2.5; + +/** + * Compares the average global difference of two images to determine if they + * are similar enough to "match" each other. + * + * "Average global difference" is the average difference of pixel components + * (RGB each) across an entire image. + * + * The cutoff for matching/not-matching is defined by the `COMPARISON_TOLERANCE` + * constant. + */ +function compareImages (img1: NativeImage, img2: NativeImage): boolean { + expect(img1.getSize()).to.deep.equal( + img2.getSize(), + 'Cannot compare images with different sizes' + ); + + const bitmap1 = img1.toBitmap(); + const bitmap2 = img2.toBitmap(); + + const { width, height } = img1.getSize(); + + let totalDiff = 0; + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + const index = (x + y * width) * 4; + const pixel1 = bitmap1.subarray(index, index + 4); + const pixel2 = bitmap2.subarray(index, index + 4); + const diff = + Math.abs(pixel1[0] - pixel2[0]) + + Math.abs(pixel1[1] - pixel2[1]) + + Math.abs(pixel1[2] - pixel2[2]); + totalDiff += diff; + } + } + + const avgDiff = totalDiff / (width * height); + return avgDiff <= COMPARISON_TOLERANCE; +} + +/** + * Recipe for tests. + * + * The page is rendered, captured as an image, then compared to an expected + * result image. + */ +async function pageCaptureTestRecipe ( + pagePath: string, + expectedImgPath: string, + artifactName: string, + cornerSmoothingAvailable: boolean = true +): Promise { + const w = new BrowserWindow({ + show: false, + width: 800, + height: 600, + useContentSize: true, + webPreferences: { + enableCornerSmoothingCSS: cornerSmoothingAvailable + } + }); + await w.loadFile(pagePath); + w.show(); + + // Wait for a render frame to prepare the page. + await w.webContents.executeJavaScript( + 'new Promise((resolve) => { requestAnimationFrame(() => resolve()); })' + ); + + const actualImg = await w.webContents.capturePage(); + expect(actualImg.isEmpty()).to.be.false('Failed to capture page image'); + + const expectedImg = nativeImage.createFromPath(expectedImgPath); + expect(expectedImg.isEmpty()).to.be.false( + 'Failed to read expected reference image' + ); + + const matches = compareImages(actualImg, expectedImg); + if (!matches) { + const artifactFileName = `corner-rounding-expected-${artifactName}.png`; + await createArtifact(artifactFileName, actualImg.toPNG()); + + throw new AssertionError( + `Actual image did not match expected reference image. Actual: "${artifactFileName}" in artifacts, Expected: "${path.relative( + path.resolve(__dirname, '..'), + expectedImgPath + )}" in source` + ); + } +} + +// FIXME: these tests rely on live rendering results, which are too variable to +// reproduce outside of CI, primarily due to display scaling. +ifdescribe(!!process.env.CI)('-electron-corner-smoothing', () => { + afterEach(async () => { + await closeAllWindows(); + }); + + describe('shape', () => { + for (const available of [true, false]) { + it(`matches the reference with web preference = ${available}`, async () => { + await pageCaptureTestRecipe( + path.join(FIXTURE_PATH, 'shape', 'test.html'), + path.join(FIXTURE_PATH, 'shape', `expected-${available}.png`), + `shape-${available}`, + available + ); + }); + } + }); + + describe('system-ui keyword', () => { + const { platform } = process; + it(`matches the reference for platform = ${platform}`, async () => { + await pageCaptureTestRecipe( + path.join(FIXTURE_PATH, 'system-ui-keyword', 'test.html'), + path.join( + FIXTURE_PATH, + 'system-ui-keyword', + `expected-${platform}.png` + ), + `system-ui-${platform}` + ); + }); + }); +}); diff --git a/spec/fixtures/api/corner-smoothing/shape/expected-false.png b/spec/fixtures/api/corner-smoothing/shape/expected-false.png new file mode 100644 index 0000000000000000000000000000000000000000..31aea673f8b06fcd4b92d4ea48bff0aa73021c9f GIT binary patch literal 166667 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�CphU2Hr9iQpV|b6$L}5Q| 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 OU$()FSX~ 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 z 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%0AL1Dr(5`KJ^xV`_I|ZFqCPQr0Pf;Z4_1 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

mTKVO-Usrzpg%#2|Qdk}o7JX2-Wux%cN$UbT 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�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%0ADbS6nL{b+XTJ4<3m|~zVmG>{m6B#+9sci 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 + + + + + +

+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-darwin.png b/spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..e1d753c50fa4a1c8edf0f5c1c856b6f017f81c40 GIT binary patch literal 9236 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 literal 0 HcmV?d00001 diff --git a/spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-linux.png b/spec/fixtures/api/corner-smoothing/system-ui-keyword/expected-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..79341eecfd763374a52299df878e921e8c69f3e5 GIT binary patch literal 8055 zcmeHMXIRr&vrmiyN_7w6#e_e41bSD7B!p zPIqH50p6W3n>xH5c9cCojABDjCN;=WbEeH(AO&z4W$97z?M@Mi@#2fUB~V3I$y2Ttw@1~D zN6RdC@GW5u$>5h>%d8$oXL6?(&blN~zSF1L7CXctmv;Kl5ctxe<++9<}KXGMe;#UvpW%A}B3d<5(wcT>iZKsfxlFeFfW_8>Qb&$h5p23GczPmD}U4$fh z6hA<{lkbdIN|Xx94qlA#;1h9bZ<|2|*ijVB==7>qtjlRW9{q%;;AH#ylt!;cuf;30 zj`8a6PA=_)q0`l-=#!Qu1xeO5*pDGPp5xn}=T;)EMye`mP36j_E}AgEmcGvju3J6x z$J`>NbJF)QfnrPcBh{;@-muVwXuoiW#`r}U4BG`)Zfuk<>$jiwNE^5f&nifAC7Vyw zVXExyJr~~3+$6tp|M_0hTnSpV9u&A?_B_Nx=pk_~KYD5cJv{#gX^DX4O}DM(N0XJy zO01MuS`qR_QYo7Ld4a31KipgLb%jqg<}S=bHCD`Ke``AoSxI~@&&6XFpnRkQCPtn9Cft|-`Sp~H#HN3XWN>LCY1Z<&3+vfvYcya*w%bJuEiuz zgnsK?<^oBe{XxW!|*iG}-G+ov{@bz!8m3%x!y-A$}rimeA{ zSC5o*q3P?{>8WEsMDOyN*8F&YtqZX^XbT-_?D2{?WM?;3H+*L~) zIXxRsZgBf1jlizrj`N;{RlT%UtfRaK1XI1~TG{?qlYNh2#gsi+;I=k!-R1cvI+$>C z$kbE|m7e*5lPL8D_v6`k)&o(ZooV2Y7M&ulv78D%o;63nrKHH`s(e2x9A9Bw*+Ftp ztE~urp0$}NaVGm41k%m9)XiU<>*UN~qoXpQekj8rF|i&Fsr|VX^X(;w_W04|?wzG6 z6EoKb)G+l;=Q(+#M5X7|22vg7m#h@jwz@HhFHzW>fVX9AZ1r-v72;*L`gdB)JRXvS zWw|1JM{eKHM3W!!W_oQ@+8Lqexb54X(5nJrTTgLC3Es@L0mw=LcQ<+`mulMBDU@Q; zBg!!uPZmVE~QK_^>|ffJI8X9h^f%pMm*2lM(DNR`g=cxx6c7 zZe)1}uf+<76c&K9D_Jzb_KbkXbJ(-!`Qa|JVd3=ez*B?PY59B|IgpiavCD}~h~-9Z zXr4+uYuHcMsJLTXg^vg71i*~J%q`8Ze)%MZXlTl50pKNzA;u#)%&#pT3fme*9^leK+i*U1&J{$sX#kSX}K1-;7X+(t-c@^H$_J~y$y zQ|J8xQV$fDW3~~J-hgGSHzk`s#NER@)SEOFvg-MP4sIq zeJ9BNuECC^0ysdu>>B>_z6jS|O^HBN$+0hfnU?-{?cin0dKINFrNGc-HDsoBpM5#N+KoII z-#yV1xH8cbE?rZ)s7+{8#eKuUM0tyyY}0L%Ya_Mv)5J0@`l@RIUHZ~3m_s%y`zLFP2n>b$EcqkZYFdb{ze^L_d36L~@ zQuWWPSEjHdL;`6JwEma*#oiEY5!UwNZ`=;_OsLUjt6IOMM|$R^(626!l#ZA`RPPZT z>z5`d+{cIuDc^eti969*SsFkWwppKv3}$~iZ5CHxOWnDAQa$8D_&QcehbWo_D>$#lz*wSxkCeGl z)9PXxAEWa)VM|O%`F{ECUEv?On{~!hFtT_}-{yQxBb13tr}y(-F0qY?m3P+%OVKJY zhu$16hmmk;$A}vj{ip3;2JBgR_kFl9-KHYXQeOb-K~KvBA9E480IS?9BIfyHai6Yz z2B3x^RLrGbr67nlmx411uc7K4`WbSxBcesNNVJ~Uq3M&?>}C3S{3<_>Sk;I7=wAw3 zVKKtA69JoR3_=!xzgWg~pN{B7pPaEClapK#uFhUsj!@0I4*8m_c$>u+_uNsq&@P&c z!>#IsMo;HyFJ+j+?Rbdt%A-M!!Q!4$gqV4I-^C4BZi)qqE276alq#j+cB3PPH>z#o z)5e2~%0cK329>IPC9f@D|`2icycc~3}ZHV(eg)x02;7f-0TOjWA=SU zL$U=6b#A`yCTE?XH-0+q zZ5Y*8xhlTLWvtFyx$y4`IS)lqu^LVbR&;NO%K%u_Vz_4p0dp{okL2`x1P;yH&aC1|BeK|FWJ}=zyY9vbTHFeWfzc zi?i>s9eL$?YUZFkN1ran<%$Qcg;V~n$AGQ7+>t%bjl5S-U?KYn0TGJ}Dl%oDYwaHw zl448_&DH6O?%@qPB6(QX(cnTO#bTGL@OW8=T=+gM%SQcIJt>C)o_SO?F6Da_cbTTZ ze(vhwV-^=|l9{r*CkIW!ul{~PF%+QZ3%}}U0+g7ilCd@0hqNMZ5a-!1Kkb1%gTj@& z1w=p(AF|$rzy2@;kSu*TpC$wRR=vujb-nPMZ&}BOLMR+$Jm4=RD0VBAiFE?_#iRgl zzK#ONh^U!6B;1Yw<>gahlFFX}R+vYDF>n`a4j=(&Pb9pEn0|aOY~Q&)!MMA=!0WEI zfYlwYecu)}2klL|W$uu)%X|7LyaMP7V_r2+Sr;MqRK1Rz0AFjpLJ|=nnQT@#fd*nK z!>>LI=$8XwI^^NX#bb{SCIHfFO>ONLbL8d?yEE&CioJVbSI5~geqz8WOyABSKu)2p zgK;KNE_Q%!bnIenB+nlJ#i#%cXBph^;2uz(;eYQ$!4uj%6ufw@ z*NYD?)(idpj~pPi@yu~Q<)U_*T|NaT0QgNkYuybbU(K9pvSA{?!MuC|5G{tu(tvcj z9xZ-+4p%Y(_mtTI1US75f+JdyR=-&a0R z|M&IqBdfg^B(08c0Wu%ZD|L#B)7Z?A0FW9XR`c>Iy?8m(iA31R&G`x|`YV!qQSFo+ zu63m8bEWbW8-JB~c(=wI+zN}Q(+<`DQ z9nk0AlO8erM4-R(gQEx@e5^9Xnzdr47Hv51n&v?Q@|8hi690dKJYMXWi>cu3d=sQ^i9N`p-tDMLieSZG*h0W+TbwYE!t6Symy*BpkZTHR*KK_*iU6@?ww4b?> zN3BsZdKLEZB$zk0u8L$fbW}+B0|T5~QhpxETNQin7^wzZcRNBa&e|IT(4=s#pMg2_ z=cGKGEMz^xbEm}YA4eSa7Byep+&f~Ds(^(CU#%bUoOSqPE-o)M6RgZnlEdGh{#UUqE_ zEpO8zE9&Rr_#&lX_*pb+jYLwT*z098fh7$!(XE}kf0i?VX~mT~qxk|S#o8EU75R^uV3xK_@~6#@Sk*u7h;4fs3lU%Jg|`2$H0jd3TO9_HYX;_4aK^8&)t-;p zZEN~?VrN+YWKD^DqsMp!HrETUFzK9ys9nfe;07vT>$nN;TzjAT5m|Srd7rcnKTxVt z6uLtsm~F!ha?|Jhb)W18Uxi@0efXi-AndB=YeTd8xvmVWQp@$+z}=d8gZ5M$%j2Ig z9D=ySlRf_SK&P&|xi4zYyVFVYqEGhLs@C&m1sU{@@bEc~1Vr1cYc7s^U7|}DzFMRr zS~~g-7J>C@r-eWa8pl!7R_fT%n-3i2`BXb(J)q|24tt~H!P(4->LR5_E_6ZUnIp8^ zthvBfo$bDeYNhcZzaCjK89B57OIIwX#<6Vh(Bvyz5$?*$Nt_`jgziUvOiP$C=>C|x zcdTnl15%XQ5L}D-v@AcT=|?G35~^=;(mdk5{@sQv0voFz$4!wufIc)-Ksh4ke?lbP z!$A&Bi0YNhL^RBn;;KGwi4ac@P}dtUl(Brxi`3)5>+ez&cJxCl7OxgV`zVw4T8sXi zWo-7mps_Yx`KH6NLw;e-G}qzTEDlLVqaLl=K;oSHuE_G+cCd_b<0QZ#AXZs ziiBN1o5@<5nx++rXu$N7KcuI=5}a77JL&Tp(AZer!?`~@#st8u)}7!wT44quxIe7_ ztR^#7CiDrM8Z$?C36lEgeTHu9$CKDC!(r(>X%G#Rak>c6F7W@>*F0{Gp4cg&&^j5jaL-j-Mw*(|Hq15TGFDt;=xZud>@dPK{fltA8Q4z$?o5{?LswF2va%Ev0+D@-*9I|k23GTUs` zEZk_^;eM;5<}4OYryH+y8der}A{l$q4(-E|`C04CKpn-bsQtwjK;Mn0j{4XAM%zSr2I_Y{ z@e{0GbGgsYQ&R12!mgAL(p}4uP`k~fvtQcb+_fJ>jbqmtKcP>;jh;mE0Xi~_?hOv& zd?DB+3$T*=Ur(w9X@)RAb*Zm9ck=O+c(p&qaelsOrlVuF!=;Bg^muBE$EEzfuDa=0 z1G*mTf=D`#K(Ph)RBX=^g)mN_=vo$V(6dVRUD1(tmF8B@5_K;(otZfQ=O6jBx%P_E}_|$~A zJ=x7{%XcDvke^5X+cWv=L0dlNmN+7>$DNGefb9h(BO{Xu$7^{C(oa+U=s>{m_=s$6 z7k)2nP6=ewx?1o_>W{uak6)>uADF=*=#=`#H6wrcm#~;;;#j$mjU9*;(bw6sms-^F zuD~`qbbffrQ_u$`=IefXstOoSu4%}7M+Yp2Us$2tu6~}G!D7e{%5T(utp)%yQWUt} zl#L$WkTe=_=i@o%rB@N;L};h~^fLe$c2Sp=1Pr7u8hgfy7yyw6J8oI;?n13E$v2Re zR~3~%Y)(-H2FbJfgM>zvkgk1l=iPwRw+__+qV3v5Jk&rL2EXjfHH$M$OTXm%p5BI8 z2BzYFjd*h+Pgtm>k%9SV#d|>IpGO`)T@$zT4OqXN=+7?-&Lak z&vS#|GO!5Ls?@-u~cSZW@egLYTCnKy+xn= z(9jA^l}qO=Fp2WCCFhX(lsknK)-$1eZDaVXBU*vY!7J^|6Rk17`tWNCO_jg6gEk?! zIue?nK0nS0mtF&^J<#NrvqJxTZu{rMw6#e_e41bSD7B!p zPIqH50p6W3n>xH5c9cCojABDjCN;=WbEeH(AO&z4W$97z?M@Mi@#2fUB~V3I$y2Ttw@1~D zN6RdC@GW5u$>5h>%d8$oXL6?(&blN~zSF1L7CXctmv;Kl5ctxe<++9<}KXGMe;#UvpW%A}B3d<5(wcT>iZKsfxlFeFfW_8>Qb&$h5p23GczPmD}U4$fh z6hA<{lkbdIN|Xx94qlA#;1h9bZ<|2|*ijVB==7>qtjlRW9{q%;;AH#ylt!;cuf;30 zj`8a6PA=_)q0`l-=#!Qu1xeO5*pDGPp5xn}=T;)EMye`mP36j_E}AgEmcGvju3J6x z$J`>NbJF)QfnrPcBh{;@-muVwXuoiW#`r}U4BG`)Zfuk<>$jiwNE^5f&nifAC7Vyw zVXExyJr~~3+$6tp|M_0hTnSpV9u&A?_B_Nx=pk_~KYD5cJv{#gX^DX4O}DM(N0XJy zO01MuS`qR_QYo7Ld4a31KipgLb%jqg<}S=bHCD`Ke``AoSxI~@&&6XFpnRkQCPtn9Cft|-`Sp~H#HN3XWN>LCY1Z<&3+vfvYcya*w%bJuEiuz zgnsK?<^oBe{XxW!|*iG}-G+ov{@bz!8m3%x!y-A$}rimeA{ zSC5o*q3P?{>8WEsMDOyN*8F&YtqZX^XbT-_?D2{?WM?;3H+*L~) zIXxRsZgBf1jlizrj`N;{RlT%UtfRaK1XI1~TG{?qlYNh2#gsi+;I=k!-R1cvI+$>C z$kbE|m7e*5lPL8D_v6`k)&o(ZooV2Y7M&ulv78D%o;63nrKHH`s(e2x9A9Bw*+Ftp ztE~urp0$}NaVGm41k%m9)XiU<>*UN~qoXpQekj8rF|i&Fsr|VX^X(;w_W04|?wzG6 z6EoKb)G+l;=Q(+#M5X7|22vg7m#h@jwz@HhFHzW>fVX9AZ1r-v72;*L`gdB)JRXvS zWw|1JM{eKHM3W!!W_oQ@+8Lqexb54X(5nJrTTgLC3Es@L0mw=LcQ<+`mulMBDU@Q; zBg!!uPZmVE~QK_^>|ffJI8X9h^f%pMm*2lM(DNR`g=cxx6c7 zZe)1}uf+<76c&K9D_Jzb_KbkXbJ(-!`Qa|JVd3=ez*B?PY59B|IgpiavCD}~h~-9Z zXr4+uYuHcMsJLTXg^vg71i*~J%q`8Ze)%MZXlTl50pKNzA;u#)%&#pT3fme*9^leK+i*U1&J{$sX#kSX}K1-;7X+(t-c@^H$_J~y$y zQ|J8xQV$fDW3~~J-hgGSHzk`s#NER@)SEOFvg-MP4sIq zeJ9BNuECC^0ysdu>>B>_z6jS|O^HBN$+0hfnU?-{?cin0dKINFrNGc-HDsoBpM5#N+KoII z-#yV1xH8cbE?rZ)s7+{8#eKuUM0tyyY}0L%Ya_Mv)5J0@`l@RIUHZ~3m_s%y`zLFP2n>b$EcqkZYFdb{ze^L_d36L~@ zQuWWPSEjHdL;`6JwEma*#oiEY5!UwNZ`=;_OsLUjt6IOMM|$R^(626!l#ZA`RPPZT z>z5`d+{cIuDc^eti969*SsFkWwppKv3}$~iZ5CHxOWnDAQa$8D_&QcehbWo_D>$#lz*wSxkCeGl z)9PXxAEWa)VM|O%`F{ECUEv?On{~!hFtT_}-{yQxBb13tr}y(-F0qY?m3P+%OVKJY zhu$16hmmk;$A}vj{ip3;2JBgR_kFl9-KHYXQeOb-K~KvBA9E480IS?9BIfyHai6Yz z2B3x^RLrGbr67nlmx411uc7K4`WbSxBcesNNVJ~Uq3M&?>}C3S{3<_>Sk;I7=wAw3 zVKKtA69JoR3_=!xzgWg~pN{B7pPaEClapK#uFhUsj!@0I4*8m_c$>u+_uNsq&@P&c z!>#IsMo;HyFJ+j+?Rbdt%A-M!!Q!4$gqV4I-^C4BZi)qqE276alq#j+cB3PPH>z#o z)5e2~%0cK329>IPC9f@D|`2icycc~3}ZHV(eg)x02;7f-0TOjWA=SU zL$U=6b#A`yCTE?XH-0+q zZ5Y*8xhlTLWvtFyx$y4`IS)lqu^LVbR&;NO%K%u_Vz_4p0dp{okL2`x1P;yH&aC1|BeK|FWJ}=zyY9vbTHFeWfzc zi?i>s9eL$?YUZFkN1ran<%$Qcg;V~n$AGQ7+>t%bjl5S-U?KYn0TGJ}Dl%oDYwaHw zl448_&DH6O?%@qPB6(QX(cnTO#bTGL@OW8=T=+gM%SQcIJt>C)o_SO?F6Da_cbTTZ ze(vhwV-^=|l9{r*CkIW!ul{~PF%+QZ3%}}U0+g7ilCd@0hqNMZ5a-!1Kkb1%gTj@& z1w=p(AF|$rzy2@;kSu*TpC$wRR=vujb-nPMZ&}BOLMR+$Jm4=RD0VBAiFE?_#iRgl zzK#ONh^U!6B;1Yw<>gahlFFX}R+vYDF>n`a4j=(&Pb9pEn0|aOY~Q&)!MMA=!0WEI zfYlwYecu)}2klL|W$uu)%X|7LyaMP7V_r2+Sr;MqRK1Rz0AFjpLJ|=nnQT@#fd*nK z!>>LI=$8XwI^^NX#bb{SCIHfFO>ONLbL8d?yEE&CioJVbSI5~geqz8WOyABSKu)2p zgK;KNE_Q%!bnIenB+nlJ#i#%cXBph^;2uz(;eYQ$!4uj%6ufw@ z*NYD?)(idpj~pPi@yu~Q<)U_*T|NaT0QgNkYuybbU(K9pvSA{?!MuC|5G{tu(tvcj z9xZ-+4p%Y(_mtTI1US75f+JdyR=-&a0R z|M&IqBdfg^B(08c0Wu%ZD|L#B)7Z?A0FW9XR`c>Iy?8m(iA31R&G`x|`YV!qQSFo+ zu63m8bEWbW8-JB~c(=wI+zN}Q(+<`DQ z9nk0AlO8erM4-R(gQEx@e5^9Xnzdr47Hv51n&v?Q@|8hi690dKJYMXWi>cu3d=sQ^i9N`p-tDMLieSZG*h0W+TbwYE!t6Symy*BpkZTHR*KK_*iU6@?ww4b?> zN3BsZdKLEZB$zk0u8L$fbW}+B0|T5~QhpxETNQin7^wzZcRNBa&e|IT(4=s#pMg2_ z=cGKGEMz^xbEm}YA4eSa7Byep+&f~Ds(^(CU#%bUoOSqPE-o)M6RgZnlEdGh{#UUqE_ zEpO8zE9&Rr_#&lX_*pb+jYLwT*z098fh7$!(XE}kf0i?VX~mT~qxk|S#o8EU75R^uV3xK_@~6#@Sk*u7h;4fs3lU%Jg|`2$H0jd3TO9_HYX;_4aK^8&)t-;p zZEN~?VrN+YWKD^DqsMp!HrETUFzK9ys9nfe;07vT>$nN;TzjAT5m|Srd7rcnKTxVt z6uLtsm~F!ha?|Jhb)W18Uxi@0efXi-AndB=YeTd8xvmVWQp@$+z}=d8gZ5M$%j2Ig z9D=ySlRf_SK&P&|xi4zYyVFVYqEGhLs@C&m1sU{@@bEc~1Vr1cYc7s^U7|}DzFMRr zS~~g-7J>C@r-eWa8pl!7R_fT%n-3i2`BXb(J)q|24tt~H!P(4->LR5_E_6ZUnIp8^ zthvBfo$bDeYNhcZzaCjK89B57OIIwX#<6Vh(Bvyz5$?*$Nt_`jgziUvOiP$C=>C|x zcdTnl15%XQ5L}D-v@AcT=|?G35~^=;(mdk5{@sQv0voFz$4!wufIc)-Ksh4ke?lbP z!$A&Bi0YNhL^RBn;;KGwi4ac@P}dtUl(Brxi`3)5>+ez&cJxCl7OxgV`zVw4T8sXi zWo-7mps_Yx`KH6NLw;e-G}qzTEDlLVqaLl=K;oSHuE_G+cCd_b<0QZ#AXZs ziiBN1o5@<5nx++rXu$N7KcuI=5}a77JL&Tp(AZer!?`~@#st8u)}7!wT44quxIe7_ ztR^#7CiDrM8Z$?C36lEgeTHu9$CKDC!(r(>X%G#Rak>c6F7W@>*F0{Gp4cg&&^j5jaL-j-Mw*(|Hq15TGFDt;=xZud>@dPK{fltA8Q4z$?o5{?LswF2va%Ev0+D@-*9I|k23GTUs` zEZk_^;eM;5<}4OYryH+y8der}A{l$q4(-E|`C04CKpn-bsQtwjK;Mn0j{4XAM%zSr2I_Y{ z@e{0GbGgsYQ&R12!mgAL(p}4uP`k~fvtQcb+_fJ>jbqmtKcP>;jh;mE0Xi~_?hOv& zd?DB+3$T*=Ur(w9X@)RAb*Zm9ck=O+c(p&qaelsOrlVuF!=;Bg^muBE$EEzfuDa=0 z1G*mTf=D`#K(Ph)RBX=^g)mN_=vo$V(6dVRUD1(tmF8B@5_K;(otZfQ=O6jBx%P_E}_|$~A zJ=x7{%XcDvke^5X+cWv=L0dlNmN+7>$DNGefb9h(BO{Xu$7^{C(oa+U=s>{m_=s$6 z7k)2nP6=ewx?1o_>W{uak6)>uADF=*=#=`#H6wrcm#~;;;#j$mjU9*;(bw6sms-^F zuD~`qbbffrQ_u$`=IefXstOoSu4%}7M+Yp2Us$2tu6~}G!D7e{%5T(utp)%yQWUt} zl#L$WkTe=_=i@o%rB@N;L};h~^fLe$c2Sp=1Pr7u8hgfy7yyw6J8oI;?n13E$v2Re zR~3~%Y)(-H2FbJfgM>zvkgk1l=iPwRw+__+qV3v5Jk&rL2EXjfHH$M$OTXm%p5BI8 z2BzYFjd*h+Pgtm>k%9SV#d|>IpGO`)T@$zT4OqXN=+7?-&Lak z&vS#|GO!5Ls?@-u~cSZW@egLYTCnKy+xn= z(9jA^l}qO=Fp2WCCFhX(lsknK)-$1eZDaVXBU*vY!7J^|6Rk17`tWNCO_jg6gEk?! zIue?nK0nS0mtF&^J<#NrvqJxTZu{rM + + + + + +
+
+
+ + \ No newline at end of file From e2b9cedf0fc76a25a9c106c2fcb1ce617f41817b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:00:46 -0500 Subject: [PATCH 093/339] feat: add support for associating a Menu with a WebFrameMain (#46350) feat: add support for associating a Menu with a WebFrameMain (#45138) * feat: add support for associating a Menu with a WebFrameMain This allows certain OS level features to activate such as Writing Tools, Autofill.. and Services. There appears to be a bug in macOS where the responder chain isn't traversed if the menu is not popped up using an event, as such we spoof a fake mouse event at the write coordinates in the right window and use that to open the menu. * build: fix build on non-mac * build: oops missed a header * fix: safely handle optional T* by checking nullptr too * build: fix gn check and build errors * docs: suggested changes * feat: default `frame` to `window.webContents.mainFrame` when possible * fix: avoid deref nullptr view * Revert "feat: default `frame` to `window.webContents.mainFrame` when possible" This reverts commit 2e888368199317d67f6ad931a7e9eff0295c4b1b. * fix: lint * Remove redundant scoped objects This code, including the comments, matches almost exactly the behavior of this argument to the function. * Add ScopedPumpMessagesInPrivateModes patch * More null pointer safety --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard --- docs/api/menu.md | 2 + lib/browser/api/menu.ts | 2 +- patches/chromium/.patches | 1 + ...pumpmessagesinprivatemodes_instances.patch | 51 ++++++++++++++++++ shell/browser/api/electron_api_menu.cc | 2 + shell/browser/api/electron_api_menu.h | 2 + shell/browser/api/electron_api_menu_mac.h | 3 ++ shell/browser/api/electron_api_menu_mac.mm | 52 +++++++++++++------ shell/browser/api/electron_api_menu_views.cc | 2 + shell/browser/api/electron_api_menu_views.h | 1 + .../browser/api/electron_api_web_frame_main.h | 4 ++ typings/internal-electron.d.ts | 2 +- 12 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch diff --git a/docs/api/menu.md b/docs/api/menu.md index b9a8a7b09e07f..949720dc0e8c8 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -73,6 +73,8 @@ The `menu` object has the following instance methods: * `options` Object (optional) * `window` [BaseWindow](base-window.md) (optional) - Default is the focused window. + * `frame` [WebFrameMain](web-frame-main.md) (optional) - Provide the relevant frame + if you want certain OS-level features such as Writing Tools on macOS to function correctly. Typically, this should be `params.frame` from the [`context-menu` event](web-contents.md#event-context-menu) on a WebContents. * `x` number (optional) - Default is the current mouse cursor position. Must be declared if `y` is declared. * `y` number (optional) - Default is the current mouse cursor position. diff --git a/lib/browser/api/menu.ts b/lib/browser/api/menu.ts index c0244cc3c1bf2..8e0a63f764654 100644 --- a/lib/browser/api/menu.ts +++ b/lib/browser/api/menu.ts @@ -93,7 +93,7 @@ Menu.prototype.popup = function (options = {}) { } } - this.popupAt(window as unknown as BaseWindow, x, y, positioningItem, sourceType, callback); + this.popupAt(window as unknown as BaseWindow, options.frame, x, y, positioningItem, sourceType, callback); return { browserWindow: window, x, y, position: positioningItem }; }; diff --git a/patches/chromium/.patches b/patches/chromium/.patches index c6e8b731e2be8..f48fa2a884c05 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -136,6 +136,7 @@ refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch feat_corner_smoothing_css_rule_and_blink_painting.patch build_add_public_config_simdutf_config.patch +fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch revert_code_health_clean_up_stale_macwebcontentsocclusion.patch ignore_parse_errors_for_resolveshortcutproperties.patch feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch diff --git a/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch b/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch new file mode 100644 index 0000000000000..6605f24d7b15f --- /dev/null +++ b/patches/chromium/fix_multiple_scopedpumpmessagesinprivatemodes_instances.patch @@ -0,0 +1,51 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Calvin Watford +Date: Wed, 5 Mar 2025 15:26:28 -0700 +Subject: fix: multiple ScopedPumpMessagesInPrivateModes instances + +Context: When swapping `Menu.popup` to use `ui::ShowContextMenu`, we +found that its use of `ScopedPumpMessagesInPrivateModes` may potentially +be used multiple times simultaneously. This class was designed to work +with only one global instance. + +This patch adds a global reference count to keep track of +`ScopedPumpMessagesInPrivateModes` instances and gate its +enable/disable behavior on this reference count. + +diff --git a/base/message_loop/message_pump_apple.mm b/base/message_loop/message_pump_apple.mm +index 52ed68ac3150bdeef3c5032f3f5f7df3d5aaac51..1658aece3e8fbcef89944a849e311f7949a68de9 100644 +--- a/base/message_loop/message_pump_apple.mm ++++ b/base/message_loop/message_pump_apple.mm +@@ -760,20 +760,29 @@ explicit OptionalAutoreleasePool(MessagePumpCFRunLoopBase* pump) { + + #else + ++static int g_private_mode_ref_count = 0; ++ + ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() { + DCHECK(g_app_pump); +- DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask()); + // Pumping events in private runloop modes is known to interact badly with + // app modal windows like NSAlert. + if (NSApp.modalWindow) { + return; + } +- g_app_pump->SetModeMask(kAllModesMask); ++ ++ g_private_mode_ref_count += 1; ++ if (g_private_mode_ref_count == 1) { ++ g_app_pump->SetModeMask(kAllModesMask); ++ } + } + + ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() { + DCHECK(g_app_pump); +- g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask); ++ DCHECK(g_private_mode_ref_count > 0); ++ g_private_mode_ref_count -= 1; ++ if (g_private_mode_ref_count == 0) { ++ g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask); ++ } + } + + int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() { diff --git a/shell/browser/api/electron_api_menu.cc b/shell/browser/api/electron_api_menu.cc index daef73495bf76..595a3513c499e 100644 --- a/shell/browser/api/electron_api_menu.cc +++ b/shell/browser/api/electron_api_menu.cc @@ -7,6 +7,7 @@ #include #include "shell/browser/api/electron_api_base_window.h" +#include "shell/browser/api/electron_api_web_frame_main.h" #include "shell/browser/api/ui_event.h" #include "shell/browser/javascript_environment.h" #include "shell/browser/native_window.h" @@ -16,6 +17,7 @@ #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/gurl_converter.h" #include "shell/common/gin_converters/image_converter.h" +#include "shell/common/gin_converters/optional_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/node_includes.h" diff --git a/shell/browser/api/electron_api_menu.h b/shell/browser/api/electron_api_menu.h index c861bd97a3e80..6b5ee0635da30 100644 --- a/shell/browser/api/electron_api_menu.h +++ b/shell/browser/api/electron_api_menu.h @@ -22,6 +22,7 @@ class Arguments; namespace electron::api { class BaseWindow; +class WebFrameMain; class Menu : public gin::Wrappable, public gin_helper::EventEmitterMixin, @@ -81,6 +82,7 @@ class Menu : public gin::Wrappable, void OnMenuWillShow(ui::SimpleMenuModel* source) override; virtual void PopupAt(BaseWindow* window, + std::optional frame, int x, int y, int positioning_item, diff --git a/shell/browser/api/electron_api_menu_mac.h b/shell/browser/api/electron_api_menu_mac.h index 679bff8784568..f4d92f0840693 100644 --- a/shell/browser/api/electron_api_menu_mac.h +++ b/shell/browser/api/electron_api_menu_mac.h @@ -13,6 +13,7 @@ namespace electron { class NativeWindow; +class WebFrameMain; namespace api { @@ -23,12 +24,14 @@ class MenuMac : public Menu { // Menu void PopupAt(BaseWindow* window, + std::optional frame, int x, int y, int positioning_item, ui::mojom::MenuSourceType source_type, base::OnceClosure callback) override; void PopupOnUI(const base::WeakPtr& native_window, + const base::WeakPtr& frame, int32_t window_id, int x, int y, diff --git a/shell/browser/api/electron_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm index 7ea23ffd91262..f5dffd4492e2b 100644 --- a/shell/browser/api/electron_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -11,11 +11,15 @@ #include "base/strings/sys_string_conversions.h" #include "base/task/current_thread.h" #include "base/task/sequenced_task_runner.h" +#include "content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h" // nogncheck +#include "content/browser/renderer_host/render_widget_host_view_mac.h" // nogncheck #include "content/public/browser/browser_task_traits.h" #include "shell/browser/api/electron_api_base_window.h" +#include "shell/browser/api/electron_api_web_frame_main.h" #include "shell/browser/native_window.h" #include "shell/common/keyboard_util.h" #include "shell/common/node_includes.h" +#include "ui/base/cocoa/menu_utils.h" namespace { @@ -48,6 +52,7 @@ MenuMac::~MenuMac() = default; void MenuMac::PopupAt(BaseWindow* window, + std::optional frame, int x, int y, int positioning_item, @@ -57,14 +62,19 @@ if (!native_window) return; + base::WeakPtr weak_frame; + if (frame && frame.value()) { + weak_frame = frame.value()->GetWeakPtr(); + } + // Make sure the Menu object would not be garbage-collected until the callback // has run. base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback)); - auto popup = - base::BindOnce(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(), - native_window->GetWeakPtr(), window->weak_map_id(), x, y, - positioning_item, std::move(callback_with_ref)); + auto popup = base::BindOnce(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(), + native_window->GetWeakPtr(), weak_frame, + window->weak_map_id(), x, y, positioning_item, + std::move(callback_with_ref)); base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, std::move(popup)); } @@ -95,6 +105,7 @@ } void MenuMac::PopupOnUI(const base::WeakPtr& native_window, + const base::WeakPtr& frame, int32_t window_id, int x, int y, @@ -145,18 +156,27 @@ position.x = position.x - [menu size].width; [popup_controllers_[window_id] setCloseCallback:std::move(close_callback)]; - // Make sure events can be pumped while the menu is up. - base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; - - // One of the events that could be pumped is |window.close()|. - // User-initiated event-tracking loops protect against this by - // setting flags in -[CrApplication sendEvent:], but since - // web-content menus are initiated by IPC message the setup has to - // be done manually. - base::mac::ScopedSendingEvent sendingEventScoper; - - // Don't emit unresponsive event when showing menu. - [menu popUpMenuPositioningItem:item atLocation:position inView:view]; + + if (frame && frame->render_frame_host()) { + auto* rfh = frame->render_frame_host()->GetOutermostMainFrameOrEmbedder(); + if (rfh && rfh->IsRenderFrameLive()) { + auto* rwhvm = + static_cast(rfh->GetView()); + RenderWidgetHostViewCocoa* cocoa_view = rwhvm->GetInProcessNSView(); + view = cocoa_view; + } + } + + NSEvent* dummy_event = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown + location:position + modifierFlags:0 + timestamp:0 + windowNumber:nswindow.windowNumber + context:nil + eventNumber:0 + clickCount:1 + pressure:0]; + ui::ShowContextMenu(menu, dummy_event, view, true); } void MenuMac::ClosePopupAt(int32_t window_id) { diff --git a/shell/browser/api/electron_api_menu_views.cc b/shell/browser/api/electron_api_menu_views.cc index dd5a543c965c0..67aa94e77032a 100644 --- a/shell/browser/api/electron_api_menu_views.cc +++ b/shell/browser/api/electron_api_menu_views.cc @@ -8,6 +8,7 @@ #include #include "shell/browser/api/electron_api_base_window.h" +#include "shell/browser/api/electron_api_web_frame_main.h" #include "shell/browser/native_window_views.h" #include "ui/display/screen.h" @@ -20,6 +21,7 @@ MenuViews::MenuViews(gin::Arguments* args) : Menu(args) {} MenuViews::~MenuViews() = default; void MenuViews::PopupAt(BaseWindow* window, + std::optional frame, int x, int y, int positioning_item, diff --git a/shell/browser/api/electron_api_menu_views.h b/shell/browser/api/electron_api_menu_views.h index 7759f07a898dd..490d4229050f3 100644 --- a/shell/browser/api/electron_api_menu_views.h +++ b/shell/browser/api/electron_api_menu_views.h @@ -22,6 +22,7 @@ class MenuViews : public Menu { protected: // Menu void PopupAt(BaseWindow* window, + std::optional frame, int x, int y, int positioning_item, diff --git a/shell/browser/api/electron_api_web_frame_main.h b/shell/browser/api/electron_api_web_frame_main.h index ca5cc07e7d1fa..f62d75e0a8f69 100644 --- a/shell/browser/api/electron_api_web_frame_main.h +++ b/shell/browser/api/electron_api_web_frame_main.h @@ -73,6 +73,10 @@ class WebFrameMain final : public gin::Wrappable, content::RenderFrameHost* render_frame_host() const; + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + // disable copy WebFrameMain(const WebFrameMain&) = delete; WebFrameMain& operator=(const WebFrameMain&) = delete; diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index f7338daa599e2..5d281261bb10d 100644 --- a/typings/internal-electron.d.ts +++ b/typings/internal-electron.d.ts @@ -166,7 +166,7 @@ declare namespace Electron { commandsMap: Record; groupsMap: Record; getItemCount(): number; - popupAt(window: BaseWindow, x: number, y: number, positioning: number, sourceType: Required['sourceType'], callback: () => void): void; + popupAt(window: BaseWindow, frame: WebFrameMain | undefined, x: number, y: number, positioning: number, sourceType: Required['sourceType'], callback: () => void): void; closePopupAt(id: number): void; setSublabel(index: number, label: string): void; setToolTip(index: number, tooltip: string): void; From 33eff16e9a5a39797aa40dc164ae29a1826ed373 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:01:48 -0500 Subject: [PATCH 094/339] perf: avoid unnecessary vector copy in `GetMimeTypeToExtensionIdMap()` (#46396) * perf: avoid making an unnecessary copy of the vector MimeTypesHandler::GetMIMETypeAllowlist() returns a const&, so we can iterate that directly instead of making a temporary copy of it. Co-authored-by: Charles Kerr * perf: move the call to ExtensionRegistry::Get() outside of the loop Also, keep the previous behavior of not calling it at all if there aren't any whitelisted extensions. Co-authored-by: Charles Kerr * perf: avoid redundant map lookup Co-authored-by: Charles Kerr * refactor: const correctness Co-authored-by: Charles Kerr * refactor: cleanup Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/plugins/plugin_utils.cc | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/shell/browser/plugins/plugin_utils.cc b/shell/browser/plugins/plugin_utils.cc index 1da80c073ded7..9c220eb888b23 100644 --- a/shell/browser/plugins/plugin_utils.cc +++ b/shell/browser/plugins/plugin_utils.cc @@ -33,30 +33,33 @@ std::string PluginUtils::GetExtensionIdForMimeType( base::flat_map PluginUtils::GetMimeTypeToExtensionIdMap( content::BrowserContext* browser_context) { - base::flat_map mime_type_to_extension_id_map; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - std::vector allowed_extension_ids = - MimeTypesHandler::GetMIMETypeAllowlist(); + const auto& allowed_extension_ids = MimeTypesHandler::GetMIMETypeAllowlist(); + if (allowed_extension_ids.empty()) + return {}; + + const extensions::ExtensionSet& enabled_extensions = + extensions::ExtensionRegistry::Get(browser_context)->enabled_extensions(); + + base::flat_map mime_type_to_extension_id_map; + // Go through the white-listed extensions and try to use them to intercept // the URL request. - for (const std::string& extension_id : allowed_extension_ids) { - const extensions::Extension* extension = - extensions::ExtensionRegistry::Get(browser_context) - ->enabled_extensions() - .GetByID(extension_id); - // The white-listed extension may not be installed, so we have to nullptr - // check |extension|. - if (!extension) { + for (const std::string& id : allowed_extension_ids) { + const extensions::Extension* extension = enabled_extensions.GetByID(id); + if (!extension) // extension might not be installed, so check for nullptr continue; - } - if (MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension)) { - for (const auto& supported_mime_type : handler->mime_type_set()) { - DCHECK(!mime_type_to_extension_id_map.contains(supported_mime_type)); - mime_type_to_extension_id_map[supported_mime_type] = extension_id; + if (const MimeTypesHandler* handler = + MimeTypesHandler::GetHandler(extension)) { + for (const std::string& mime_type : handler->mime_type_set()) { + const auto [_, inserted] = + mime_type_to_extension_id_map.insert_or_assign(mime_type, id); + DCHECK(inserted); } } } #endif + return mime_type_to_extension_id_map; } From 6126cc2bfeaa741f028b9e51696f337563e45b31 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:32:43 -0500 Subject: [PATCH 095/339] fix: `UtilityProcess.fork` crash before app ready (#46404) fix: UtilityProcess.fork crash before app ready Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/utility-process.md | 2 ++ .../api/electron_api_utility_process.cc | 9 ++++++ .../utility-process-app-ready/index.js | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 spec/fixtures/crash-cases/utility-process-app-ready/index.js diff --git a/docs/api/utility-process.md b/docs/api/utility-process.md index 0b500b58fa9fc..658c0c8804fba 100644 --- a/docs/api/utility-process.md +++ b/docs/api/utility-process.md @@ -44,6 +44,8 @@ Process: [Main](../glossary.md#main-process)
Returns [`UtilityProcess`](utility-process.md#class-utilityprocess) +**Note:** `utilityProcess.fork` can only be called after the `ready` event has been emitted on `App`. + ## Class: UtilityProcess > Instances of the `UtilityProcess` represent the Chromium spawned child process diff --git a/shell/browser/api/electron_api_utility_process.cc b/shell/browser/api/electron_api_utility_process.cc index 387b82c92fe65..573d38448e164 100644 --- a/shell/browser/api/electron_api_utility_process.cc +++ b/shell/browser/api/electron_api_utility_process.cc @@ -21,11 +21,13 @@ #include "gin/object_template_builder.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "shell/browser/api/message_port.h" +#include "shell/browser/browser.h" #include "shell/browser/javascript_environment.h" #include "shell/browser/net/system_network_context_manager.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" +#include "shell/common/gin_helper/error_thrower.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/node_includes.h" #include "shell/common/v8_util.h" @@ -412,6 +414,13 @@ raw_ptr UtilityProcessWrapper::FromProcessId( // static gin::Handle UtilityProcessWrapper::Create( gin::Arguments* args) { + if (!Browser::Get()->is_ready()) { + gin_helper::ErrorThrower(args->isolate()) + .ThrowTypeError( + "utilityProcess cannot be created before app is ready."); + return {}; + } + gin_helper::Dictionary dict; if (!args->GetNext(&dict)) { args->ThrowTypeError("Options must be an object."); diff --git a/spec/fixtures/crash-cases/utility-process-app-ready/index.js b/spec/fixtures/crash-cases/utility-process-app-ready/index.js new file mode 100644 index 0000000000000..590195c83331c --- /dev/null +++ b/spec/fixtures/crash-cases/utility-process-app-ready/index.js @@ -0,0 +1,31 @@ +const { app, BrowserWindow, utilityProcess } = require('electron'); + +const path = require('node:path'); + +function createWindow () { + const mainWindow = new BrowserWindow(); + mainWindow.loadFile('about:blank'); +} + +app.whenReady().then(() => { + createWindow(); + + app.on('activate', function () { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); + }); +}); + +app.on('window-all-closed', function () { + if (process.platform !== 'darwin') app.quit(); +}); + +try { + utilityProcess.fork(path.join(__dirname, 'utility.js')); +} catch (e) { + if (/utilityProcess cannot be created before app is ready/.test(e.message)) { + app.exit(0); + } else { + console.error(e); + app.exit(1); + } +} From 4db5a2a2b23488663569399d08b2c5f92b035bec Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:28:58 -0500 Subject: [PATCH 096/339] fix: rounded corners disappear momentarily on window close (#46407) fix: Explicitly set rounded corners in borderless mode on Windows 11 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: zoy --- shell/browser/native_window_views.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index d989e7d88df2b..0154e2ad7175d 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -381,8 +381,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, bool rounded_corner = true; options.Get(options::kRoundedCorners, &rounded_corner); - if (!rounded_corner) - SetRoundedCorners(false); + SetRoundedCorners(rounded_corner); } LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); From 58b2c2e651bb09f734efe790dea4495449642ed5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:58:33 -0500 Subject: [PATCH 097/339] fix: leaked `gfx::Canvas` in `AutofillPopupView::OnPaint()` (#46412) * perf: avoid redundant call to popup_bounds_in_view() Co-authored-by: Charles Kerr * refactor: use a std::optional<> for paint_canvas local Co-authored-by: Charles Kerr * fix: fix leaked gfx::Canvas in AutofillPopupView::OnPaint() Co-authored-by: Charles Kerr * refactor: remove redundant get() call when testing smart pointer for nonempty Co-authored-by: Charles Kerr * refactor: remove unnecessary draw_canvas variable Co-authored-by: Charles Kerr * refactor: rename bitmap to offscreen_bitmap for symmetry Co-authored-by: Charles Kerr * refactor: avoid another redundant call to popup_bounds_in_view() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/ui/views/autofill_popup_view.cc | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/shell/browser/ui/views/autofill_popup_view.cc b/shell/browser/ui/views/autofill_popup_view.cc index 7e7dd928bf5c0..289bba3f4c36e 100644 --- a/shell/browser/ui/views/autofill_popup_view.cc +++ b/shell/browser/ui/views/autofill_popup_view.cc @@ -5,6 +5,7 @@ #include "shell/browser/ui/views/autofill_popup_view.h" #include +#include #include #include "base/functional/bind.h" @@ -237,30 +238,33 @@ void AutofillPopupView::DoUpdateBoundsAndRedrawPopup() { void AutofillPopupView::OnPaint(gfx::Canvas* canvas) { if (!popup_ || static_cast(popup_->line_count()) != children().size()) return; - gfx::Canvas* draw_canvas = canvas; - SkBitmap bitmap; - std::unique_ptr paint_canvas; - if (view_proxy_.get()) { - bitmap.allocN32Pixels(popup_->popup_bounds_in_view().width(), - popup_->popup_bounds_in_view().height(), true); - paint_canvas = std::make_unique(bitmap); - draw_canvas = new gfx::Canvas(paint_canvas.get(), 1.0); + gfx::Rect offscreen_bounds; + SkBitmap offscreen_bitmap; + std::optional offscreen_paint_canvas; + std::optional offscreen_draw_canvas; + if (view_proxy_) { + offscreen_bounds = popup_->popup_bounds_in_view(); + offscreen_bitmap.allocN32Pixels(offscreen_bounds.width(), + offscreen_bounds.height(), true); + offscreen_paint_canvas.emplace(offscreen_bitmap); + offscreen_draw_canvas.emplace(&offscreen_paint_canvas.value(), 1.0); + canvas = &offscreen_draw_canvas.value(); } - draw_canvas->DrawColor( + canvas->DrawColor( GetColorProvider()->GetColor(ui::kColorResultsTableNormalBackground)); - OnPaintBorder(draw_canvas); + OnPaintBorder(canvas); for (int i = 0; i < popup_->line_count(); ++i) { gfx::Rect line_rect = popup_->GetRowBounds(i); - DrawAutofillEntry(draw_canvas, i, line_rect); + DrawAutofillEntry(canvas, i, line_rect); } - if (view_proxy_.get()) { - view_proxy_->SetBounds(popup_->popup_bounds_in_view()); - view_proxy_->SetBitmap(bitmap); + if (view_proxy_) { + view_proxy_->SetBounds(offscreen_bounds); + view_proxy_->SetBitmap(offscreen_bitmap); } } From 01ce103ae1a6c7bf5068c9eba4bd6f378b14d98d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:25:29 -0500 Subject: [PATCH 098/339] perf: have `ErrorThrower` lazily lookup the current isolate (#46415) perf: have ErrorThrower lazy-lookup the current isolate ErrorThrower's default constructor is marked as "should rarely if ever be used" because it's expensive to call. Unfortunately, nearly every instance of ErrorThrower comes as an argument in gin_helper's JS-->C++ function marshalling where a thrower is default-constructed and then populated in gin_helper::GetNextArgument() with an assignment operator to a temporary ErrorThrower constructed with the gin::Arguments' isolate. tldr: most of the time we use the slow constructor first, then throw that work away unused by overwriting with a fast-constructed one. This refactor avoids that cost by deferring the expensive work to `ErrorThrower::isolate()`, where it happens only as a fallback iff isolate_ hasn't been set. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/common/gin_helper/error_thrower.cc | 18 +++++++++--------- shell/common/gin_helper/error_thrower.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/shell/common/gin_helper/error_thrower.cc b/shell/common/gin_helper/error_thrower.cc index 0dbfce5cdf81b..842143cc1cb73 100644 --- a/shell/common/gin_helper/error_thrower.cc +++ b/shell/common/gin_helper/error_thrower.cc @@ -10,12 +10,11 @@ namespace gin_helper { -ErrorThrower::ErrorThrower(v8::Isolate* isolate) : isolate_(isolate) {} - -// This constructor should be rarely if ever used, since -// v8::Isolate::GetCurrent() uses atomic loads and is thus a bit -// costly to invoke -ErrorThrower::ErrorThrower() : isolate_(v8::Isolate::GetCurrent()) {} +v8::Isolate* ErrorThrower::isolate() const { + // Callers should prefer to specify the isolate in the constructor, + // since GetCurrent() uses atomic loads and is thus a bit costly to invoke + return isolate_ ? isolate_.get() : v8::Isolate::GetCurrent(); +} void ErrorThrower::ThrowError(const std::string_view err_msg) const { Throw(v8::Exception::Error, err_msg); @@ -39,9 +38,10 @@ void ErrorThrower::ThrowSyntaxError(const std::string_view err_msg) const { void ErrorThrower::Throw(ErrorGenerator gen, const std::string_view err_msg) const { - v8::Local exception = gen(gin::StringToV8(isolate_, err_msg), {}); - if (!isolate_->IsExecutionTerminating()) - isolate_->ThrowException(exception); + v8::Isolate* isolate = this->isolate(); + + if (!isolate->IsExecutionTerminating()) + isolate->ThrowException(gen(gin::StringToV8(isolate, err_msg), {})); } } // namespace gin_helper diff --git a/shell/common/gin_helper/error_thrower.h b/shell/common/gin_helper/error_thrower.h index bd5981b575857..4e3c90d50b005 100644 --- a/shell/common/gin_helper/error_thrower.h +++ b/shell/common/gin_helper/error_thrower.h @@ -14,8 +14,8 @@ namespace gin_helper { class ErrorThrower { public: - explicit ErrorThrower(v8::Isolate* isolate); - ErrorThrower(); + constexpr explicit ErrorThrower(v8::Isolate* isolate) : isolate_{isolate} {} + constexpr ErrorThrower() = default; ~ErrorThrower() = default; void ThrowError(std::string_view err_msg) const; @@ -24,14 +24,14 @@ class ErrorThrower { void ThrowReferenceError(std::string_view err_msg) const; void ThrowSyntaxError(std::string_view err_msg) const; - v8::Isolate* isolate() const { return isolate_; } + v8::Isolate* isolate() const; private: using ErrorGenerator = v8::Local (*)(v8::Local err_msg, v8::Local options); void Throw(ErrorGenerator gen, std::string_view err_msg) const; - raw_ptr isolate_; + raw_ptr isolate_ = {}; }; } // namespace gin_helper From 92b03cb91f6f3e3d85a45b4f355c38c0ab1ed415 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:31:54 -0400 Subject: [PATCH 099/339] build: make it clearer when cookie auth runs (#46422) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/set-chromium-cookie/action.yml | 14 ++++++++++++-- .github/workflows/linux-publish.yml | 1 + .github/workflows/macos-publish.yml | 1 + .github/workflows/windows-publish.yml | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/actions/set-chromium-cookie/action.yml b/.github/actions/set-chromium-cookie/action.yml index 4c37d14b7c7f2..2011655e29b59 100644 --- a/.github/actions/set-chromium-cookie/action.yml +++ b/.github/actions/set-chromium-cookie/action.yml @@ -4,9 +4,14 @@ runs: using: "composite" steps: - name: Set the git cookie from chromium.googlesource.com (Unix) - if: ${{ runner.os != 'Windows' && env.CHROMIUM_GIT_COOKIE }} + if: ${{ runner.os != 'Windows' }} shell: bash run: | + if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then + echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate." + exit 0 + fi + eval 'set +o history' 2>/dev/null || setopt HIST_IGNORE_SPACE 2>/dev/null touch ~/.gitcookies chmod 0600 ~/.gitcookies @@ -28,9 +33,14 @@ runs: echo $RESPONSE fi - name: Set the git cookie from chromium.googlesource.com (Windows) - if: ${{ runner.os == 'Windows' && env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + if: ${{ runner.os == 'Windows' }} shell: cmd run: | + if "%CHROMIUM_GIT_COOKIE_WINDOWS_STRING%"=="" ( + echo CHROMIUM_GIT_COOKIE_WINDOWS_STRING is not set - cannot authenticate. + exit /b 0 + ) + git config --global http.cookiefile "%USERPROFILE%\.gitcookies" powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies" diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index 9443d54f324e0..0d5a2a2349521 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -27,6 +27,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - name: Checkout Electron diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index d20b2d8d94a00..1620305893dfa 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -28,6 +28,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' steps: - name: Checkout Electron diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index 3b0f0ad3e5e9b..d4fd36139fd74 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -28,6 +28,7 @@ jobs: - /mnt/win-cache:/mnt/win-cache - /var/run/sas:/var/run/sas env: + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' From 7299dd1501aa309036ab7d6ed9b4d92ba673e02c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:53:43 -0500 Subject: [PATCH 100/339] perf: avoid redundant map lookup in UsbChooserContext::OnDeviceRemoved() (#46418) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/usb/usb_chooser_context.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/usb/usb_chooser_context.cc b/shell/browser/usb/usb_chooser_context.cc index 3b5d26b5f7296..1273b7dbd41e7 100644 --- a/shell/browser/usb/usb_chooser_context.cc +++ b/shell/browser/usb/usb_chooser_context.cc @@ -302,8 +302,8 @@ void UsbChooserContext::OnDeviceRemoved( } // Update the device list. - DCHECK(devices_.contains(device_info->guid)); - devices_.erase(device_info->guid); + const size_t n_erased = devices_.erase(device_info->guid); + DCHECK_EQ(n_erased, 1U); // Notify all device observers. for (auto& observer : device_observer_list_) From a0ac69088185b642f9f1ce7c652b7ab082af2970 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:58:08 -0500 Subject: [PATCH 101/339] perf: cache the return value of `IsX11()` (#46427) * perf: cache the return value of IsX11() Co-authored-by: Charles Kerr * fix: mark as nodiscard for those who call, but mark as maybe_unused for Windows Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 0154e2ad7175d..b1ec2b740bd86 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -163,10 +163,11 @@ gfx::Size WindowSizeToContentSizeBuggy(HWND hwnd, const gfx::Size& size) { #endif -[[maybe_unused]] bool IsX11() { - return ui::OzonePlatform::GetInstance() - ->GetPlatformProperties() - .electron_can_call_x11; +[[maybe_unused, nodiscard]] bool IsX11() { + static const bool is_x11 = ui::OzonePlatform::GetInstance() + ->GetPlatformProperties() + .electron_can_call_x11; + return is_x11; } class NativeWindowClientView : public views::ClientView { From 3acd5774da7da52e769ee431c421f2d8ce0ec6e8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:45:28 -0400 Subject: [PATCH 102/339] feat: support `system-context-menu` on Linux (#46399) feat: support system-context-menu on Linux Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/base-window.md | 6 ++- docs/api/browser-window.md | 6 ++- ...electron_desktop_window_tree_host_linux.cc | 41 +++++++++++++++++-- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 38a82de051b02..d23ced860c5b9 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -342,12 +342,12 @@ Emitted when the window has closed a sheet. Emitted when the native new tab button is clicked. -#### Event: 'system-context-menu' _Windows_ +#### Event: 'system-context-menu' _Windows_ _Linux_ Returns: * `event` Event -* `point` [Point](structures/point.md) - The screen coordinates the context menu was triggered at +* `point` [Point](structures/point.md) - The screen coordinates where the context menu was triggered. Emitted when the system context menu is triggered on the window, this is normally only triggered when the user right clicks on the non-client area @@ -356,6 +356,8 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). + ### Static Methods The `BaseWindow` class has the following static methods: diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 46f3d0289edf1..96ef6896ffbea 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -421,12 +421,12 @@ Emitted when the window has closed a sheet. Emitted when the native new tab button is clicked. -#### Event: 'system-context-menu' _Windows_ +#### Event: 'system-context-menu' _Windows_ _Linux_ Returns: * `event` Event -* `point` [Point](structures/point.md) - The screen coordinates the context menu was triggered at +* `point` [Point](structures/point.md) - The screen coordinates where the context menu was triggered. Emitted when the system context menu is triggered on the window, this is normally only triggered when the user right clicks on the non-client area @@ -435,6 +435,8 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). + ### Static Methods The `BrowserWindow` class has the following static methods: diff --git a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc index eb8d61f7f6afc..c58e7726e903f 100644 --- a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc +++ b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc @@ -17,6 +17,9 @@ #include "shell/browser/native_window_views.h" #include "shell/browser/ui/views/client_frame_view_linux.h" #include "third_party/skia/include/core/SkRegion.h" +#include "ui/aura/window_delegate.h" +#include "ui/base/hit_test.h" +#include "ui/display/screen.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/skia_conversions.h" #include "ui/linux/linux_ui.h" @@ -262,13 +265,45 @@ void ElectronDesktopWindowTreeHostLinux::DispatchEvent(ui::Event* event) { is_mousedown && (mouse_event->IsRightMouseButton() || (mouse_event->IsLeftMouseButton() && mouse_event->IsControlDown())); - if (is_system_menu_trigger) { - electron::api::WebContents::SetDisableDraggableRegions(true); + + if (!is_system_menu_trigger) { views::DesktopWindowTreeHostLinux::DispatchEvent(event); - electron::api::WebContents::SetDisableDraggableRegions(false); return; } + + // Determine the non-client area and dispatch 'system-context-menu'. + if (GetContentWindow() && GetContentWindow()->delegate()) { + ui::LocatedEvent* located_event = event->AsLocatedEvent(); + gfx::PointF location = located_event->location_f(); + gfx::PointF location_in_dip = + GetRootTransform().InverseMapPoint(location).value_or(location); + int hit_test_code = GetContentWindow()->delegate()->GetNonClientComponent( + gfx::ToRoundedPoint(location_in_dip)); + if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE) { + bool prevent_default = false; + native_window_view_->NotifyWindowSystemContextMenu( + located_event->x(), located_event->y(), &prevent_default); + + // If |prevent_default| is true, then the user might want to show a + // custom menu - proceed propagation and emit context-menu in the + // renderer. Otherwise, show the native system window controls menu. + if (prevent_default) { + electron::api::WebContents::SetDisableDraggableRegions(true); + views::DesktopWindowTreeHostLinux::DispatchEvent(event); + electron::api::WebContents::SetDisableDraggableRegions(false); + } else { + if (ui::OzonePlatform::GetInstance() + ->GetPlatformRuntimeProperties() + .supports_server_window_menus) { + views::DesktopWindowTreeHostLinux::ShowWindowControlsMenu( + display::Screen::GetScreen()->GetCursorScreenPoint()); + } + } + return; + } + } } + views::DesktopWindowTreeHostLinux::DispatchEvent(event); } From 30ccda8ba07a5cdb2e9a2a2ba4df54ef11d0d7f8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:26:32 -0700 Subject: [PATCH 103/339] fix: ensure maximize is emitted when reduce motion is enabled on macOS (#46466) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard --- .../ui/cocoa/electron_ns_window_delegate.mm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm index 3f7c689bfc8b0..04fa6781dd1fe 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -221,6 +221,12 @@ - (void)windowDidResize:(NSNotification*)notification { [super windowDidResize:notification]; shell_->NotifyWindowResize(); shell_->RedrawTrafficLights(); + // When reduce motion is enabled windowDidResize is only called once after + // a resize and windowDidEndLiveResize is not called. So we need to call + // handleZoomEnd here as well. + if (NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion) { + [self handleZoomEnd]; + } } - (void)windowWillMove:(NSNotification*)notification { @@ -276,9 +282,7 @@ - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame { return YES; } -- (void)windowDidEndLiveResize:(NSNotification*)notification { - resizingHorizontally_.reset(); - shell_->NotifyWindowResized(); +- (void)handleZoomEnd { if (is_zooming_) { if (shell_->IsMaximized()) shell_->NotifyWindowMaximize(); @@ -288,6 +292,12 @@ - (void)windowDidEndLiveResize:(NSNotification*)notification { } } +- (void)windowDidEndLiveResize:(NSNotification*)notification { + resizingHorizontally_.reset(); + shell_->NotifyWindowResized(); + [self handleZoomEnd]; +} + - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Store resizable mask so it can be restored after exiting fullscreen. is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable); From 8fec7adfa6763d7fb6ec966431786f0fc841e8dd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:05:13 -0500 Subject: [PATCH 104/339] fix: gin_helper::Promise in GPUInfoManager must be destroyed before destroying Node/V8 (#46471) * fix: gin_helper::Promise in GPUInfoManager must be destroyed before destroying Node/V8 Co-authored-by: Yang Liu * fix: use CleanedUpAtExit to control the lifetime of GPUInfoManager Co-authored-by: Yang Liu --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Yang Liu --- shell/browser/api/gpuinfo_manager.cc | 4 +++- shell/browser/api/gpuinfo_manager.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/shell/browser/api/gpuinfo_manager.cc b/shell/browser/api/gpuinfo_manager.cc index 4a2f4f30e4154..b68d7e27e62a9 100644 --- a/shell/browser/api/gpuinfo_manager.cc +++ b/shell/browser/api/gpuinfo_manager.cc @@ -17,7 +17,9 @@ namespace electron { GPUInfoManager* GPUInfoManager::GetInstance() { - return base::Singleton::get(); + // will be deleted by CleanedUpAtExit::DoCleanup + static GPUInfoManager* instance = new GPUInfoManager(); + return instance; } GPUInfoManager::GPUInfoManager() diff --git a/shell/browser/api/gpuinfo_manager.h b/shell/browser/api/gpuinfo_manager.h index 3ee39475101e7..b2f5cae518ea1 100644 --- a/shell/browser/api/gpuinfo_manager.h +++ b/shell/browser/api/gpuinfo_manager.h @@ -11,6 +11,7 @@ #include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck #include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/gpu_data_manager_observer.h" +#include "shell/common/gin_helper/cleaned_up_at_exit.h" namespace gin_helper { template @@ -20,7 +21,8 @@ class Promise; namespace electron { // GPUInfoManager is a singleton used to manage and fetch GPUInfo -class GPUInfoManager : private content::GpuDataManagerObserver { +class GPUInfoManager : private content::GpuDataManagerObserver, + public gin_helper::CleanedUpAtExit { public: static GPUInfoManager* GetInstance(); From 0cb42911ea799958c912cdf39b9700327af0486b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:23:54 -0500 Subject: [PATCH 105/339] fix: `NativeWindowViews::GetRestoredState()` can return wrong state when maximized (#46463) fix: NativeWindowViews::GetRestoredState() returning wrong state Introduced by the af58931 Chromium 131.0.6744.0 roll, specifically https://github.com/electron/electron/pull/43948/commits/9840662#diff-f9d7ef7 98406626 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index b1ec2b740bd86..e802bd6152ecb 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1851,7 +1851,7 @@ ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() { return ui::mojom::WindowShowState::kMaximized; } #else - return ui::mojom::WindowShowState::kMinimized; + return ui::mojom::WindowShowState::kMaximized; #endif } From e0a24f08a1d0e64f095920f285f39a9112e585e4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:30:41 -0500 Subject: [PATCH 106/339] perf: avoid redundant map lookup in `HidChooserContext::DeviceChanged()` (#46479) perf: avoid redundant map lookup in HidChooserContext::DeviceChanged() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/hid/hid_chooser_context.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/browser/hid/hid_chooser_context.cc b/shell/browser/hid/hid_chooser_context.cc index 6e288fa19e540..4869f56eb8def 100644 --- a/shell/browser/hid/hid_chooser_context.cc +++ b/shell/browser/hid/hid_chooser_context.cc @@ -274,10 +274,11 @@ void HidChooserContext::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) { void HidChooserContext::DeviceChanged(device::mojom::HidDeviceInfoPtr device) { DCHECK(device); - DCHECK(devices_.contains(device->guid)); // Update the device list. - devices_[device->guid] = device->Clone(); + auto& mapped = devices_[device->guid]; + DCHECK(!mapped.is_null()); + mapped = device->Clone(); // Notify all observers. for (auto& observer : device_observer_list_) From 99c3728a93ec0e9b857a1a492abd2746323032a2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:35:12 -0500 Subject: [PATCH 107/339] perf: avoid redundant call to virtual methods GetProcess() and GetID() (#46445) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_web_contents.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 005e5d91e5c6e..f1803661f9c48 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1788,19 +1788,18 @@ void WebContents::FrameDeleted(content::FrameTreeNodeId frame_tree_node_id) { } void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) { + const auto id = render_view_host->GetProcess()->GetID().GetUnsafeValue(); // This event is necessary for tracking any states with respect to // intermediate render view hosts aka speculative render view hosts. Currently // used by object-registry.js to ref count remote objects. - Emit("render-view-deleted", - render_view_host->GetProcess()->GetID().GetUnsafeValue()); + Emit("render-view-deleted", id); if (web_contents()->GetRenderViewHost() == render_view_host) { // When the RVH that has been deleted is the current RVH it means that the // the web contents are being closed. This is communicated by this event. // Currently tracked by guest-window-manager.ts to destroy the // BrowserWindow. - Emit("current-render-view-deleted", - render_view_host->GetProcess()->GetID().GetUnsafeValue()); + Emit("current-render-view-deleted", id); } } From 5a6ed32592a1e7b4f75436f26f55b83d9a4e04d9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 22:21:48 -0500 Subject: [PATCH 108/339] perf: prefer `absl::flat_hash_set` over `std::set` when sorted order is not needed (#46440) * perf: use an absl::flat_hash_set for UsbChooserContext::ephemeral_devices_ Co-authored-by: Charles Kerr * perf: use an absl::flat_hash_set for GlobalMenuBarRegistrarX11::live_windows_ Co-authored-by: Charles Kerr * perf: use an absl::flat_hash_set for NativeWindowViews::forwarding_windows_ Co-authored-by: Charles Kerr * perf: use an absl::flat_hash_set for OffScreenRenderWidgetHostView::guest_host_views_ perf: use an absl::flat_hash_set for OffScreenRenderWidgetHostView::proxy_views_ Co-authored-by: Charles Kerr * perf: use an absl::flat_hash_set for NativeWindow::injected_frames_ Co-authored-by: Charles Kerr * perf: use an absl::flat_hash_set for NativeWindow::background_throttling_sources_ Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.h | 5 +++-- shell/browser/native_window_views.h | 4 ++-- shell/browser/native_window_views_win.cc | 1 - shell/browser/osr/osr_render_widget_host_view.h | 6 +++--- shell/browser/ui/views/global_menu_bar_registrar_x11.h | 5 ++--- shell/browser/usb/usb_chooser_context.h | 4 ++-- shell/renderer/electron_sandboxed_renderer_client.h | 4 ++-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 4b151c90715ea..b3fee341e6740 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include "content/public/browser/web_contents_user_data.h" #include "extensions/browser/app_window/size_constraints.h" #include "shell/browser/native_window_observer.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "ui/views/widget/widget_delegate.h" class SkRegion; @@ -515,7 +515,8 @@ class NativeWindow : public base::SupportsUserData, // Observers of this window. base::ObserverList observers_; - std::set background_throttling_sources_; + absl::flat_hash_set + background_throttling_sources_; // Accessible title. std::u16string accessible_title_; diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 74abb577268f4..518842aa90a3b 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -9,11 +9,11 @@ #include #include -#include #include #include "base/memory/raw_ptr.h" #include "shell/browser/ui/views/root_view.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "ui/base/ozone_buildflags.h" #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #include "ui/views/widget/widget_observer.h" @@ -283,7 +283,7 @@ class NativeWindowViews : public NativeWindow, base::win::ScopedGDIObject app_icon_; // The set of windows currently forwarding mouse messages. - static std::set forwarding_windows_; + static inline absl::flat_hash_set forwarding_windows_; static HHOOK mouse_hook_; bool forwarding_mouse_messages_ = false; HWND legacy_window_ = nullptr; diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 04016b304c88c..64961174313e8 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -224,7 +224,6 @@ bool IsScreenReaderActive() { } // namespace -std::set NativeWindowViews::forwarding_windows_; HHOOK NativeWindowViews::mouse_hook_ = nullptr; bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { diff --git a/shell/browser/osr/osr_render_widget_host_view.h b/shell/browser/osr/osr_render_widget_host_view.h index 952ed7496d09f..2bd201bcdda72 100644 --- a/shell/browser/osr/osr_render_widget_host_view.h +++ b/shell/browser/osr/osr_render_widget_host_view.h @@ -6,7 +6,6 @@ #define ELECTRON_SHELL_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ #include -#include #include #include @@ -28,6 +27,7 @@ #include "shell/browser/osr/osr_host_display_client.h" #include "shell/browser/osr/osr_video_consumer.h" #include "shell/browser/osr/osr_view_proxy.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "third_party/blink/public/mojom/widget/record_content_to_visible_time_request.mojom-forward.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/ime/text_input_client.h" @@ -277,8 +277,8 @@ class OffScreenRenderWidgetHostView raw_ptr parent_host_view_ = nullptr; raw_ptr popup_host_view_ = nullptr; raw_ptr child_host_view_ = nullptr; - std::set guest_host_views_; - std::set proxy_views_; + absl::flat_hash_set guest_host_views_; + absl::flat_hash_set proxy_views_; const bool transparent_; const bool offscreen_use_shared_texture_; diff --git a/shell/browser/ui/views/global_menu_bar_registrar_x11.h b/shell/browser/ui/views/global_menu_bar_registrar_x11.h index 5e4ac03c70301..655b3b0057ac2 100644 --- a/shell/browser/ui/views/global_menu_bar_registrar_x11.h +++ b/shell/browser/ui/views/global_menu_bar_registrar_x11.h @@ -7,10 +7,9 @@ #include -#include - #include "base/memory/raw_ptr.h" #include "base/memory/singleton.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "ui/base/glib/scoped_gsignal.h" #include "ui/gfx/x/xproto.h" @@ -52,7 +51,7 @@ class GlobalMenuBarRegistrarX11 { // x11::Window which want to be registered, but haven't yet been because // we're waiting for the proxy to become available. - std::set live_windows_; + absl::flat_hash_set live_windows_; ScopedGSignal signal_; }; diff --git a/shell/browser/usb/usb_chooser_context.h b/shell/browser/usb/usb_chooser_context.h index 6ad48d30968f9..d62c9ad5edfb1 100644 --- a/shell/browser/usb/usb_chooser_context.h +++ b/shell/browser/usb/usb_chooser_context.h @@ -6,7 +6,6 @@ #define ELECTRON_SHELL_BROWSER_USB_USB_CHOOSER_CONTEXT_H_ #include -#include #include #include @@ -20,6 +19,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/usb_manager.mojom.h" #include "services/device/public/mojom/usb_manager_client.mojom.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" #include "url/origin.h" namespace mojo { @@ -108,7 +108,7 @@ class UsbChooserContext : public KeyedService, base::queue pending_get_devices_requests_; - std::map> ephemeral_devices_; + std::map> ephemeral_devices_; std::map devices_; // Connection to |device_manager_instance_|. diff --git a/shell/renderer/electron_sandboxed_renderer_client.h b/shell/renderer/electron_sandboxed_renderer_client.h index d162579b61cd4..3199083b367fd 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.h +++ b/shell/renderer/electron_sandboxed_renderer_client.h @@ -5,9 +5,9 @@ #define ELECTRON_SHELL_RENDERER_ELECTRON_SANDBOXED_RENDERER_CLIENT_H_ #include -#include #include "shell/renderer/renderer_client_base.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_set.h" namespace base { class ProcessMetrics; @@ -64,7 +64,7 @@ class ElectronSandboxedRendererClient : public RendererClientBase { // Getting main script context from web frame would lazily initializes // its script context. Doing so in a web page without scripts would trigger // assertion, so we have to keep a book of injected web frames. - std::set injected_frames_; + absl::flat_hash_set injected_frames_; }; } // namespace electron From 3d8df7684bf085010d19894473603bf8e934c85c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 22:22:05 -0500 Subject: [PATCH 109/339] fix: zlib pointer alignment (#46460) fix: fix zlib pointer alignment Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/node/.patches | 1 + patches/node/zlib_fix_pointer_alignment.patch | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 patches/node/zlib_fix_pointer_alignment.patch diff --git a/patches/node/.patches b/patches/node/.patches index ea57fbb74b3db..90ab26d2adeb1 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -48,3 +48,4 @@ feat_add_oom_error_callback_in_node_isolatesettings.patch fix_-wnonnull_warning.patch refactor_attach_cppgc_heap_on_v8_isolate_creation.patch fix_ensure_traverseparent_bails_on_resource_path_exit.patch +zlib_fix_pointer_alignment.patch diff --git a/patches/node/zlib_fix_pointer_alignment.patch b/patches/node/zlib_fix_pointer_alignment.patch new file mode 100644 index 0000000000000..47c51a49e62ab --- /dev/null +++ b/patches/node/zlib_fix_pointer_alignment.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeroen Hofstee +Date: Tue, 1 Apr 2025 20:09:31 +0000 +Subject: zlib: fix pointer alignment + +The function AllocForBrotli prefixes the allocated memory with its +size, and returns a pointer to the region after it. This pointer can +however no longer be suitably aligned. Correct this by allocating +the maximum of the the size of the size_t and the max alignment. + +On Arm 32bits the size_t is 4 bytes long, but the alignment is 8 for +some NEON instructions. When Brotli is compiled with optimizations +enabled newer GCC versions will use the NEON instructions and trigger +a bus error killing node. + +see https://github.com/google/brotli/issues/1159 + +diff --git a/src/node_zlib.cc b/src/node_zlib.cc +index 90307cd4984ae5aa55386f2980ad9cd540322dfd..6f12b5034d1a98da50c064cf2cfdf12fc88137eb 100644 +--- a/src/node_zlib.cc ++++ b/src/node_zlib.cc +@@ -493,7 +493,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { + } + + static void* AllocForBrotli(void* data, size_t size) { +- size += sizeof(size_t); ++ constexpr size_t offset = std::max(sizeof(size_t), alignof(max_align_t)); ++ size += offset; + CompressionStream* ctx = static_cast(data); + char* memory = UncheckedMalloc(size); + if (memory == nullptr) [[unlikely]] { +@@ -502,7 +503,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { + *reinterpret_cast(memory) = size; + ctx->unreported_allocations_.fetch_add(size, + std::memory_order_relaxed); +- return memory + sizeof(size_t); ++ return memory + offset; + } + + static void FreeForZlib(void* data, void* pointer) { +@@ -510,7 +511,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { + return; + } + CompressionStream* ctx = static_cast(data); +- char* real_pointer = static_cast(pointer) - sizeof(size_t); ++ constexpr size_t offset = std::max(sizeof(size_t), alignof(max_align_t)); ++ char* real_pointer = static_cast(pointer) - offset; + size_t real_size = *reinterpret_cast(real_pointer); + ctx->unreported_allocations_.fetch_sub(real_size, + std::memory_order_relaxed); From 10fb533bc3e2f84bdd097ffe5516ea377261f25f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 22:11:20 -0700 Subject: [PATCH 110/339] fix: don't copy 'package.json's out of ASAR file (#46477) * fix: don't copy 'package.json's out of ASAR file New Node.js module resolution system reads `package.json` from imported modules by reading from the file natively in C++ without calling into `fs.readFileSync`. The ASAR FS wrapper code had copied files out into a temporary folder as a workaround, but it is inefficient and does not cover all module resolution mechanisms in Node.js. In this change we expose `overrideReadFileSync` method on the `modules` binding in Node.js, and use this override to call into ASAR-supporting `fs.readFileSync`. Co-authored-by: Fedor Indutny * chore: remove erroneous patch --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Fedor Indutny Co-authored-by: Keeley Hammond Co-authored-by: Keeley Hammond --- lib/node/asar-fs-wrapper.ts | 42 +++--- patches/node/.patches | 1 + ...se_readfilesync_override_for_modules.patch | 123 ++++++++++++++++++ 3 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 patches/node/fix_expose_readfilesync_override_for_modules.patch diff --git a/lib/node/asar-fs-wrapper.ts b/lib/node/asar-fs-wrapper.ts index e4650036bfdcc..6ecaa0ac785b5 100644 --- a/lib/node/asar-fs-wrapper.ts +++ b/lib/node/asar-fs-wrapper.ts @@ -667,10 +667,10 @@ export const wrapFsWithAsar = (fs: Record) => { return p(pathArgument, options); }; - const { readFileSync } = fs; - fs.readFileSync = function (pathArgument: string, options: any) { - const pathInfo = splitPath(pathArgument); - if (!pathInfo.isAsar) return readFileSync.apply(this, arguments); + function readFileFromArchiveSync ( + pathInfo: { asarPath: string; filePath: string }, + options: any + ): ReturnType { const { asarPath, filePath } = pathInfo; const archive = getOrCreateArchive(asarPath); @@ -704,6 +704,14 @@ export const wrapFsWithAsar = (fs: Record) => { fs.readSync(fd, buffer, 0, info.size, info.offset); validateBufferIntegrity(buffer, info.integrity); return (encoding) ? buffer.toString(encoding) : buffer; + } + + const { readFileSync } = fs; + fs.readFileSync = function (pathArgument: string, options: any) { + const pathInfo = splitPath(pathArgument); + if (!pathInfo.isAsar) return readFileSync.apply(this, arguments); + + return readFileFromArchiveSync(pathInfo, options); }; type ReaddirOptions = { encoding: BufferEncoding | null; withFileTypes?: false, recursive?: false } | undefined | null; @@ -980,25 +988,19 @@ export const wrapFsWithAsar = (fs: Record) => { }; const modBinding = internalBinding('modules'); - const { readPackageJSON } = modBinding; - internalBinding('modules').readPackageJSON = ( - jsonPath: string, - isESM: boolean, - base: undefined | string, - specifier: undefined | string - ) => { + modBinding.overrideReadFileSync((jsonPath: string): Buffer | false | undefined => { const pathInfo = splitPath(jsonPath); - if (!pathInfo.isAsar) return readPackageJSON(jsonPath, isESM, base, specifier); - const { asarPath, filePath } = pathInfo; - const archive = getOrCreateArchive(asarPath); - if (!archive) return undefined; + // Fallback to Node.js internal implementation + if (!pathInfo.isAsar) return undefined; - const realPath = archive.copyFileOut(filePath); - if (!realPath) return undefined; - - return readPackageJSON(realPath, isESM, base, specifier); - }; + try { + return readFileFromArchiveSync(pathInfo, undefined); + } catch { + // Not found + return false; + } + }); const { internalModuleStat } = binding; internalBinding('fs').internalModuleStat = (receiver: unknown, pathArgument: string) => { diff --git a/patches/node/.patches b/patches/node/.patches index 90ab26d2adeb1..7603d4f935433 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -48,4 +48,5 @@ feat_add_oom_error_callback_in_node_isolatesettings.patch fix_-wnonnull_warning.patch refactor_attach_cppgc_heap_on_v8_isolate_creation.patch fix_ensure_traverseparent_bails_on_resource_path_exit.patch +fix_expose_readfilesync_override_for_modules.patch zlib_fix_pointer_alignment.patch diff --git a/patches/node/fix_expose_readfilesync_override_for_modules.patch b/patches/node/fix_expose_readfilesync_override_for_modules.patch new file mode 100644 index 0000000000000..d30767517d58a --- /dev/null +++ b/patches/node/fix_expose_readfilesync_override_for_modules.patch @@ -0,0 +1,123 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Fedor Indutny +Date: Mon, 31 Mar 2025 11:21:29 -0700 +Subject: fix: expose ReadFileSync override for modules + +To avoid copying out `package.json` files out of the ASAR file we need +an API override to replace the native `ReadFileSync` in the `modules` +binding. + +diff --git a/src/env_properties.h b/src/env_properties.h +index 9f89823170782242093bc5ee0df6a2a2ef5b919f..b9374ee1acceb3d0aab51c6c5ae6a79be1cc71a9 100644 +--- a/src/env_properties.h ++++ b/src/env_properties.h +@@ -478,6 +478,7 @@ + V(maybe_cache_generated_source_map, v8::Function) \ + V(messaging_deserialize_create_object, v8::Function) \ + V(message_port, v8::Object) \ ++ V(modules_read_file_sync, v8::Function) \ + V(builtin_module_require, v8::Function) \ + V(performance_entry_callback, v8::Function) \ + V(prepare_stack_trace_callback, v8::Function) \ +diff --git a/src/node_modules.cc b/src/node_modules.cc +index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4088e05ac 100644 +--- a/src/node_modules.cc ++++ b/src/node_modules.cc +@@ -21,6 +21,7 @@ namespace modules { + + using v8::Array; + using v8::Context; ++using v8::Function; + using v8::FunctionCallbackInfo; + using v8::HandleScope; + using v8::Isolate; +@@ -88,6 +89,7 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { + + const BindingData::PackageConfig* BindingData::GetPackageJSON( + Realm* realm, std::string_view path, ErrorContext* error_context) { ++ auto isolate = realm->isolate(); + auto binding_data = realm->GetBindingData(); + + auto cache_entry = binding_data->package_configs_.find(path.data()); +@@ -97,8 +99,36 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( + + PackageConfig package_config{}; + package_config.file_path = path; ++ ++ Local modules_read_file_sync = realm->modules_read_file_sync(); ++ ++ int read_err; + // No need to exclude BOM since simdjson will skip it. +- if (ReadFileSync(&package_config.raw_json, path.data()) < 0) { ++ if (modules_read_file_sync.IsEmpty()) { ++ read_err = ReadFileSync(&package_config.raw_json, path.data()); ++ } else { ++ Local args[] = { ++ v8::String::NewFromUtf8(isolate, path.data()).ToLocalChecked(), ++ }; ++ Local result = modules_read_file_sync->Call( ++ realm->context(), ++ Undefined(isolate), ++ arraysize(args), ++ args).ToLocalChecked(); ++ ++ if (result->IsUndefined()) { ++ // Fallback ++ read_err = ReadFileSync(&package_config.raw_json, path.data()); ++ } else if (result->IsFalse()) { ++ // Not found ++ read_err = -1; ++ } else { ++ BufferValue data(isolate, result); ++ package_config.raw_json = data.ToString(); ++ read_err = 0; ++ } ++ } ++ if (read_err < 0) { + return nullptr; + } + // In some systems, std::string is annotated to generate an +@@ -248,6 +278,12 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( + return &cached.first->second; + } + ++void BindingData::OverrideReadFileSync(const FunctionCallbackInfo& args) { ++ Realm* realm = Realm::GetCurrent(args); ++ CHECK(args[0]->IsFunction()); ++ realm->set_modules_read_file_sync(args[0].As()); ++} ++ + void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { + CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier] + CHECK(args[0]->IsString()); // path +@@ -556,6 +592,8 @@ void GetCompileCacheDir(const FunctionCallbackInfo& args) { + void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, + Local target) { + Isolate* isolate = isolate_data->isolate(); ++ SetMethod(isolate, target, "overrideReadFileSync", OverrideReadFileSync); ++ + SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON); + SetMethod(isolate, + target, +@@ -595,6 +633,8 @@ void BindingData::CreatePerContextProperties(Local target, + + void BindingData::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { ++ registry->Register(OverrideReadFileSync); ++ + registry->Register(ReadPackageJSON); + registry->Register(GetNearestParentPackageJSONType); + registry->Register(GetNearestParentPackageJSON); +diff --git a/src/node_modules.h b/src/node_modules.h +index 17909b2270454b3275c7bf2e50d4b9b35673ecc8..3d5b0e3ac65524adfe221bfd6f85360dee1f0bee 100644 +--- a/src/node_modules.h ++++ b/src/node_modules.h +@@ -54,6 +54,8 @@ class BindingData : public SnapshotableObject { + SET_SELF_SIZE(BindingData) + SET_MEMORY_INFO_NAME(BindingData) + ++ static void OverrideReadFileSync( ++ const v8::FunctionCallbackInfo& args); + static void ReadPackageJSON(const v8::FunctionCallbackInfo& args); + static void GetNearestParentPackageJSON( + const v8::FunctionCallbackInfo& args); From 4e3baa4d8e78571883efd287d7d959c133c97308 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 4 Apr 2025 08:02:14 -0500 Subject: [PATCH 111/339] fix!: hard crash on invalid command line switches (36-x-y) (#46446) fix: hard crash on invalid command line switches (#46004) * fix: hard crash on invalid command line switch * Update docs/api/command-line.md * chore: feedback from review * docs: Add breaking change note --------- Co-authored-by: Shelley Vohr Co-authored-by: Niklas Wenzel --- docs/api/command-line.md | 54 ++++++++++++++++--- docs/breaking-changes.md | 15 ++++++ shell/common/api/electron_api_command_line.cc | 29 ++++++---- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/docs/api/command-line.md b/docs/api/command-line.md index 63046d734624d..424c4e9d5921d 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -20,45 +20,87 @@ document. #### `commandLine.appendSwitch(switch[, value])` -* `switch` string - A command-line switch, without the leading `--` -* `value` string (optional) - A value for the given switch +* `switch` string - A command-line switch, without the leading `--`. +* `value` string (optional) - A value for the given switch. Append a switch (with optional `value`) to Chromium's command line. **Note:** This will not affect `process.argv`. The intended usage of this function is to control Chromium's behavior. +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +``` + #### `commandLine.appendArgument(value)` -* `value` string - The argument to append to the command line +* `value` string - The argument to append to the command line. Append an argument to Chromium's command line. The argument will be quoted correctly. Switches will precede arguments regardless of appending order. If you're appending an argument like `--switch=value`, consider using `appendSwitch('switch', 'value')` instead. +```js +const { app } = require('electron') + +app.commandLine.appendArgument('--enable-experimental-web-platform-features') +``` + **Note:** This will not affect `process.argv`. The intended usage of this function is to control Chromium's behavior. #### `commandLine.hasSwitch(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Returns `boolean` - Whether the command-line switch is present. +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +const hasPort = app.commandLine.hasSwitch('remote-debugging-port') +console.log(hasPort) // true +``` + #### `commandLine.getSwitchValue(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Returns `string` - The command-line switch value. +This function is meant to obtain Chromium command line switches. It is not +meant to be used for application-specific command line arguments. For the +latter, please use `process.argv`. + +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +const portValue = app.commandLine.getSwitchValue('remote-debugging-port') +console.log(portValue) // '8315' +``` + **Note:** When the switch is not present or has no value, it returns empty string. #### `commandLine.removeSwitch(switch)` -* `switch` string - A command-line switch +* `switch` string - A command-line switch. Removes the specified switch from Chromium's command line. +```js +const { app } = require('electron') + +app.commandLine.appendSwitch('remote-debugging-port', '8315') +console.log(app.commandLine.hasSwitch('remote-debugging-port')) // true + +app.commandLine.removeSwitch('remote-debugging-port') +console.log(app.commandLine.hasSwitch('remote-debugging-port')) // false +``` + **Note:** This will not affect `process.argv`. The intended usage of this function is to control Chromium's behavior. diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index b717ca9d1e00c..9d26d0b458365 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -12,6 +12,21 @@ This document uses the following convention to categorize breaking changes: * **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release. * **Removed:** An API or feature was removed, and is no longer supported by Electron. +## Planned Breaking API Changes (37.0) + +### Behavior Changed: `BrowserWindow.IsVisibleOnAllWorkspaces()` on Linux + +`BrowserWindow.IsVisibleOnAllWorkspaces()` will now return false on Linux if the +window is not currently visible. + +### Behavior Changes: `app.commandLine` + +`app.commandLine` will convert upper-cases switches and arguments to lowercase. + +`app.commandLine` was only meant to handle chromium switches (which aren't case-sensitive) and switches passed via `app.commandLine` will not be passed down to any of the child processes. + +If you were using `app.commandLine` to control the behavior of the main process, you should do this via `process.argv`. + ## Planned Breaking API Changes (36.0) ### Removed:`isDefault` and `status` properties on `PrinterInfo` diff --git a/shell/common/api/electron_api_command_line.cc b/shell/common/api/electron_api_command_line.cc index 786296900a067..f1bacd59afb4d 100644 --- a/shell/common/api/electron_api_command_line.cc +++ b/shell/common/api/electron_api_command_line.cc @@ -4,50 +4,59 @@ #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/strings/string_util.h" #include "services/network/public/cpp/network_switches.h" #include "shell/common/gin_converters/base_converter.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_includes.h" +#include "third_party/abseil-cpp/absl/strings/ascii.h" namespace { +bool HasSwitch(const std::string& switch_string) { + auto switch_str = base::ToLowerASCII(switch_string); -bool HasSwitch(const std::string& name) { - return base::CommandLine::ForCurrentProcess()->HasSwitch(name); + auto* command_line = base::CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(switch_str); } -base::CommandLine::StringType GetSwitchValue(const std::string& name) { - return base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(name); +base::CommandLine::StringType GetSwitchValue(gin_helper::ErrorThrower thrower, + const std::string& switch_string) { + auto switch_str = base::ToLowerASCII(switch_string); + + auto* command_line = base::CommandLine::ForCurrentProcess(); + return command_line->GetSwitchValueNative(switch_str); } void AppendSwitch(const std::string& switch_string, gin_helper::Arguments* args) { + auto switch_str = base::ToLowerASCII(switch_string); auto* command_line = base::CommandLine::ForCurrentProcess(); - if (base::EndsWith(switch_string, "-path", base::CompareCase::INSENSITIVE_ASCII) || switch_string == network::switches::kLogNetLog) { base::FilePath path; args->GetNext(&path); - command_line->AppendSwitchPath(switch_string, path); + command_line->AppendSwitchPath(switch_str, path); return; } base::CommandLine::StringType value; if (args->GetNext(&value)) - command_line->AppendSwitchNative(switch_string, value); + command_line->AppendSwitchNative(switch_str, value); else - command_line->AppendSwitch(switch_string); + command_line->AppendSwitch(switch_str); } void RemoveSwitch(const std::string& switch_string) { + auto switch_str = base::ToLowerASCII(switch_string); + auto* command_line = base::CommandLine::ForCurrentProcess(); - command_line->RemoveSwitch(switch_string); + command_line->RemoveSwitch(switch_str); } void AppendArg(const std::string& arg) { auto* command_line = base::CommandLine::ForCurrentProcess(); - command_line->AppendArg(arg); } From c92412761d2195114040a4823ea640383099444a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 08:58:47 -0500 Subject: [PATCH 112/339] docs: Add C++/Win32 tutorial (#46488) * docs: Add C++/Win32 tutorial Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-win32.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-win32.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-win32.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-win32.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-win32.md Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * docs: make linter happy Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Felix Rieseberg Co-authored-by: Charles Kerr --- .../native-code-and-electron-cpp-win32.md | 1346 +++++++++++++++++ 1 file changed, 1346 insertions(+) create mode 100644 docs/tutorial/native-code-and-electron-cpp-win32.md diff --git a/docs/tutorial/native-code-and-electron-cpp-win32.md b/docs/tutorial/native-code-and-electron-cpp-win32.md new file mode 100644 index 0000000000000..3d7fab89b932b --- /dev/null +++ b/docs/tutorial/native-code-and-electron-cpp-win32.md @@ -0,0 +1,1346 @@ +# Native Code and Electron: C++ (Windows) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for Windows using C++ and the [Win32 API](https://learn.microsoft.com/en-us/windows/win32/). To illustrate how you can embed native Win32 code in your Electron app, we'll be building a basic native Windows GUI (using the Windows Common Controls) that communicates with Electron's JavaScript. + +Specifically, we'll be integrating with two commonly used native Windows libraries: + +* `comctl32.lib`, which contains common controls and user interface components. It provides various UI elements like buttons, scrollbars, toolbars, status bars, progress bars, and tree views. As far as GUI development on Windows goes, this library is very low-level and basic - more modern frameworks like WinUI or WPF are advanced and alternatives but require a lot more C++ and Windows version considerations than are useful for this tutorial. This way, we can avoid the many perils of building native interfaces for multiple Windows versions! +* `shcore.lib`, a library that provides high-DPI awareness functionality and other Shell-related features around managing displays and UI elements. + +This tutorial will be most useful to those who already have some familiarity with native C++ GUI development on Windows. You should have experience with basic window classes and procedures, like `WNDCLASSEXW` and `WindowProc` functions. You should also be familiar with the Windows message loop, which is the heart of any native application - our code will be using `GetMessage`, `TranslateMessage`, and `DispatchMessage` to handle messages. Lastly, we'll be using (but not explaining) standard Win32 controls like `WC_EDITW` or `WC_BUTTONW`. + +> [!NOTE] +> If you're not familiar with C++ GUI development on Windows, we recommend Microsoft's excellent documentation and guides, particular for beginners. "[Get Started with Win32 and C++](https://learn.microsoft.com/en-us/windows/win32/learnwin32/learn-to-program-for-windows)" is a great introduction. + +## Requirements + +Just like our [general introduction to Native Code and Electron](./native-code-and-electron.md), this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code. Since this tutorial discusses writing native code that interacts with Windows, we recommend that you follow this tutorial on Windows with both Visual Studio and the "Desktop development with C++ workload" installed. For details, see the [Visual Studio Installation instructions](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio). + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +my-native-win32-addon/ +├── binding.gyp +├── include/ +│ └── cpp_code.h +├── js/ +│ └── index.js +├── package.json +└── src/ + ├── cpp_addon.cc + └── cpp_code.cc +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "cpp-win32", + "version": "1.0.0", + "description": "A demo module that exposes C++ code to Electron", + "main": "js/index.js", + "author": "Your Name", + "scripts": { + "clean": "rm -rf build_swift && rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +For a Windows-specific addon, we need to modify our `binding.gyp` file to include Windows libraries and set appropriate compiler flags. In short, we need to do the following three things: + +1. We need to ensure our addon is only compiled on Windows, since we'll be writing platform-specific code. +2. We need to include the Windows-specific libraries. In our tutorial, we'll be targeting `comctl32.lib` and `shcore.lib`. +3. We need to configure the compiler and define C++ macros. + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "cpp_addon", + "conditions": [ + ['OS=="win"', { + "sources": [ + "src/cpp_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " +#include + +namespace cpp_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); + +} // namespace cpp_code +``` + +This header: + +* Includes the basic `hello_world` function from the general tutorial +* Adds a `hello_gui` function to create a Win32 GUI +* Defines callback types for Todo operations (add). To keep this tutorial somewhat brief, we'll only be implementing one callback. +* Provides setter functions for these callbacks + +## 4) Implementing Win32 GUI Code + +Now, let's implement our Win32 GUI in `src/cpp_code.cc`. This is a larger file, so we'll review it in sections. First, let's include necessary headers and define basic structures. + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(linker, "\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +struct TodoItem +{ + GUID id; + std::wstring text; + int64_t date; + + std::string toJson() const + { + OLECHAR *guidString; + StringFromCLSID(id, &guidString); + std::wstring widGuid(guidString); + CoTaskMemFree(guidString); + + // Convert wide string to narrow for JSON + std::string guidStr(widGuid.begin(), widGuid.end()); + std::string textStr(text.begin(), text.end()); + + return "{" + "\"id\":\"" + guidStr + "\"," + "\"text\":\"" + textStr + "\"," + "\"date\":" + std::to_string(date) + + "}"; + } +}; + +namespace cpp_code +{ + // More code to follow later... +} +``` + +In this section: + +* We include necessary Win32 headers +* We set up pragma comments to link against required libraries +* We define callback variables for Todo operations +* We create a `TodoItem` struct with a method to convert to JSON + +Next, let's implement the basic functions and helper methods: + +```cpp title='src/cpp_code.cc' +namespace cpp_code +{ + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + // Window procedure function that handles window messages + // hwnd: Handle to the window + // uMsg: Message code + // wParam: Additional message-specific information + // lParam: Additional message-specific information + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + // Helper function to scale a value based on DPI + int Scale(int value, UINT dpi) + { + return MulDiv(value, dpi, 96); // 96 is the default DPI + } + + // Helper function to convert SYSTEMTIME to milliseconds since epoch + int64_t SystemTimeToMillis(const SYSTEMTIME &st) + { + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (uli.QuadPart - 116444736000000000ULL) / 10000; + } + + // More code to follow later... +} +``` + +In this section, we've added a function that allows us to set the callback for an added todo item. We also added two helper functions that we need when working with JavaScript: One to scale our UI elements depending on the display's DPI - and another one to convert a Windows `SYSTEMTIME` to milliseconds since epoch, which is how JavaScript keeps track of time. + +Now, let's get to the part you probably came to this tutorial for - creating a GUI thread and drawing native pixels on screen. We'll do that by adding a `void hello_gui()` function to our `cpp_code` namespace. There are a few considerations we need to make: + +* We need to create a new thread for the GUI to avoid blocking the Node.js event loop. The Windows message loop that processes GUI events runs in an infinite loop, which would prevent Node.js from processing other events if run on the main thread. By running the GUI on a separate thread, we allow both the native Windows interface and Node.js to remain responsive. This separation also helps prevent potential deadlocks that could occur if GUI operations needed to wait for JavaScript callbacks. You don't need to do that for simpler Windows API interactions - but since you need to check the message loop, you do need to setup your own thread for GUI. +* Then, within our thread, we need to run a message loop to handle any Windows messages. +* We need to setup DPI awareness for proper display scaling. +* We need to register a window class, create a window, and add various UI controls. + +In the code below, we haven't added any actual controls yet. We're doing that on purpose to look at our added code in smaller portions here. + +```cpp title='src/cpp_code.cc' +void hello_gui() { + // Launch GUI in a separate thread + std::thread guiThread([]() { + // Enable Per-Monitor DPI awareness + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + // Initialize Common Controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&icex); + + // Register window class + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"TodoApp"; + RegisterClassExW(&wc); + + // Get the DPI for the monitor + UINT dpi = GetDpiForSystem(); + + // Create window + HWND hwnd = CreateWindowExW( + 0, L"TodoApp", L"Todo List", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + Scale(500, dpi), Scale(500, dpi), + nullptr, nullptr, + GetModuleHandle(nullptr), nullptr + ); + + if (hwnd == nullptr) { + return; + } + + // Controls go here! The window is currently empty, + // we'll add controls in the next step. + + ShowWindow(hwnd, SW_SHOW); + + // Message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Clean up + DeleteObject(hFont); + }); + + // Detach the thread so it runs independently + guiThread.detach(); +} +``` + +Now that we have a thread, a window, and a message loop, we can add some controls. Nothing we're doing here is unique to writing Windows C++ for Electron - you can simply copy & paste the code below into the `Controls go here!` section inside our `hello_gui()` function. + +We're specifically adding buttons, a date picker, and a list. + +```cpp title='src/cpp_code.cc' +void hello_gui() { + // ... + // All the code above "Controls go here!" + + // Create the modern font with DPI-aware size + HFONT hFont = CreateFontW( + -Scale(14, dpi), // Height (scaled) + 0, // Width + 0, // Escapement + 0, // Orientation + FW_NORMAL, // Weight + FALSE, // Italic + FALSE, // Underline + FALSE, // StrikeOut + DEFAULT_CHARSET, // CharSet + OUT_DEFAULT_PRECIS, // OutPrecision + CLIP_DEFAULT_PRECIS, // ClipPrecision + CLEARTYPE_QUALITY, // Quality + DEFAULT_PITCH | FF_DONTCARE, // Pitch and Family + L"Segoe UI" // Font face name + ); + + // Create input controls with scaled positions and sizes + HWND hEdit = CreateWindowExW(0, WC_EDITW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + Scale(10, dpi), Scale(10, dpi), + Scale(250, dpi), Scale(25, dpi), + hwnd, (HMENU)1, GetModuleHandle(nullptr), nullptr); + SendMessageW(hEdit, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Create date picker + HWND hDatePicker = CreateWindowExW(0, DATETIMEPICK_CLASSW, L"", + WS_CHILD | WS_VISIBLE | DTS_SHORTDATECENTURYFORMAT, + Scale(270, dpi), Scale(10, dpi), + Scale(100, dpi), Scale(25, dpi), + hwnd, (HMENU)4, GetModuleHandle(nullptr), nullptr); + SendMessageW(hDatePicker, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hButton = CreateWindowExW(0, WC_BUTTONW, L"Add", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + Scale(380, dpi), Scale(10, dpi), + Scale(50, dpi), Scale(25, dpi), + hwnd, (HMENU)2, GetModuleHandle(nullptr), nullptr); + SendMessageW(hButton, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hListBox = CreateWindowExW(0, WC_LISTBOXW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY, + Scale(10, dpi), Scale(45, dpi), + Scale(460, dpi), Scale(400, dpi), + hwnd, (HMENU)3, GetModuleHandle(nullptr), nullptr); + SendMessageW(hListBox, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Store menu handle in window's user data + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)hContextMenu); + + // All the code below "Controls go here!" + // ... +} +``` + +Now that we have a user interface that allows users to add todos, we need to store them - and add a helper function that'll potentially call our JavaScript callback. Right below the `void hello_gui() { ... }` function, we'll add the following: + +```cpp title='src/cpp_code.cc' + // Global vector to store todos + static std::vector g_todos; + + void NotifyCallback(const TodoCallback &callback, const std::string &json) + { + if (callback) + { + callback(json); + // Process pending messages + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +``` + +We'll also need a function that turns a todo into something we can display. We don't need anything fancy - given the name of the todo and a `SYSTEMTIME` timestamp, we'll return a simple string. Add it right below the function above: + +```cpp title='src/cpp_code.cc' + std::wstring FormatTodoDisplay(const std::wstring &text, const SYSTEMTIME &st) + { + wchar_t dateStr[64]; + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, dateStr, 64); + return text + L" - " + dateStr; + } +``` + +When a user adds a todo, we want to reset the controls back to an empty state. To do so, add a helper function below the code we just added: + +```cpp title='src/cpp_code.cc' + void ResetControls(HWND hwnd) + { + HWND hEdit = GetDlgItem(hwnd, 1); + HWND hDatePicker = GetDlgItem(hwnd, 4); + HWND hAddButton = GetDlgItem(hwnd, 2); + + // Clear text + SetWindowTextW(hEdit, L""); + + // Reset date to current + SYSTEMTIME currentTime; + GetLocalTime(¤tTime); + DateTime_SetSystemtime(hDatePicker, GDT_VALID, ¤tTime); + } +``` + +Then, we'll need to implement the window procedure to handle Windows messages. Like a lot of our code here, there is very little specific to Electron in this code - so as a Win32 C++ developer, you'll recognize this function. The only thing that is unique is that we want to potentially notify the JavaScript callback about an added todo. We've previously implemented the `NotifyCallback()` function, which we will be using here. Add this code right below the function above: + +```cpp title='src/cpp_code.cc' + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_COMMAND: + { + HWND hListBox = GetDlgItem(hwnd, 3); + int cmd = LOWORD(wParam); + + switch (cmd) + { + case 2: // Add button + { + wchar_t buffer[256]; + GetDlgItemTextW(hwnd, 1, buffer, 256); + + if (wcslen(buffer) > 0) + { + SYSTEMTIME st; + HWND hDatePicker = GetDlgItem(hwnd, 4); + DateTime_GetSystemtime(hDatePicker, &st); + + TodoItem todo; + CoCreateGuid(&todo.id); + todo.text = buffer; + todo.date = SystemTimeToMillis(st); + + g_todos.push_back(todo); + + std::wstring displayText = FormatTodoDisplay(buffer, st); + SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + ResetControls(hwnd); + NotifyCallback(g_todoAddedCallback, todo.toJson()); + } + break; + } + } + break; + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } +``` + +We now have successfully implemented the Win32 C++ code. Most of this should look and feel to you like code you'd write with or without Electron. In the next step, we'll be building the bridge between C++ and JavaScript. Here's the complete implementation: + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "comctl32.lib") +#pragma comment(linker, "\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; +static TodoCallback g_todoUpdatedCallback; +static TodoCallback g_todoDeletedCallback; + +struct TodoItem +{ + GUID id; + std::wstring text; + int64_t date; + + std::string toJson() const + { + OLECHAR *guidString; + StringFromCLSID(id, &guidString); + std::wstring widGuid(guidString); + CoTaskMemFree(guidString); + + // Convert wide string to narrow for JSON + std::string guidStr(widGuid.begin(), widGuid.end()); + std::string textStr(text.begin(), text.end()); + + return "{" + "\"id\":\"" + guidStr + "\"," + "\"text\":\"" + textStr + "\"," + "\"date\":" + std::to_string(date) + + "}"; + } +}; + +namespace cpp_code +{ + + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } + + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + // Helper function to scale a value based on DPI + int Scale(int value, UINT dpi) + { + return MulDiv(value, dpi, 96); // 96 is the default DPI + } + + // Helper function to convert SYSTEMTIME to milliseconds since epoch + int64_t SystemTimeToMillis(const SYSTEMTIME &st) + { + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (uli.QuadPart - 116444736000000000ULL) / 10000; + } + + void ResetControls(HWND hwnd) + { + HWND hEdit = GetDlgItem(hwnd, 1); + HWND hDatePicker = GetDlgItem(hwnd, 4); + HWND hAddButton = GetDlgItem(hwnd, 2); + + // Clear text + SetWindowTextW(hEdit, L""); + + // Reset date to current + SYSTEMTIME currentTime; + GetLocalTime(¤tTime); + DateTime_SetSystemtime(hDatePicker, GDT_VALID, ¤tTime); + } + + void hello_gui() { + // Launch GUI in a separate thread + std::thread guiThread([]() { + // Enable Per-Monitor DPI awareness + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + // Initialize Common Controls + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&icex); + + // Register window class + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"TodoApp"; + RegisterClassExW(&wc); + + // Get the DPI for the monitor + UINT dpi = GetDpiForSystem(); + + // Create window + HWND hwnd = CreateWindowExW( + 0, L"TodoApp", L"Todo List", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + Scale(500, dpi), Scale(500, dpi), + nullptr, nullptr, + GetModuleHandle(nullptr), nullptr + ); + + if (hwnd == nullptr) { + return; + } + + // Create the modern font with DPI-aware size + HFONT hFont = CreateFontW( + -Scale(14, dpi), // Height (scaled) + 0, // Width + 0, // Escapement + 0, // Orientation + FW_NORMAL, // Weight + FALSE, // Italic + FALSE, // Underline + FALSE, // StrikeOut + DEFAULT_CHARSET, // CharSet + OUT_DEFAULT_PRECIS, // OutPrecision + CLIP_DEFAULT_PRECIS, // ClipPrecision + CLEARTYPE_QUALITY, // Quality + DEFAULT_PITCH | FF_DONTCARE, // Pitch and Family + L"Segoe UI" // Font face name + ); + + // Create input controls with scaled positions and sizes + HWND hEdit = CreateWindowExW(0, WC_EDITW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + Scale(10, dpi), Scale(10, dpi), + Scale(250, dpi), Scale(25, dpi), + hwnd, (HMENU)1, GetModuleHandle(nullptr), nullptr); + SendMessageW(hEdit, WM_SETFONT, (WPARAM)hFont, TRUE); + + // Create date picker + HWND hDatePicker = CreateWindowExW(0, DATETIMEPICK_CLASSW, L"", + WS_CHILD | WS_VISIBLE | DTS_SHORTDATECENTURYFORMAT, + Scale(270, dpi), Scale(10, dpi), + Scale(100, dpi), Scale(25, dpi), + hwnd, (HMENU)4, GetModuleHandle(nullptr), nullptr); + SendMessageW(hDatePicker, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hButton = CreateWindowExW(0, WC_BUTTONW, L"Add", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + Scale(380, dpi), Scale(10, dpi), + Scale(50, dpi), Scale(25, dpi), + hwnd, (HMENU)2, GetModuleHandle(nullptr), nullptr); + SendMessageW(hButton, WM_SETFONT, (WPARAM)hFont, TRUE); + + HWND hListBox = CreateWindowExW(0, WC_LISTBOXW, L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY, + Scale(10, dpi), Scale(45, dpi), + Scale(460, dpi), Scale(400, dpi), + hwnd, (HMENU)3, GetModuleHandle(nullptr), nullptr); + SendMessageW(hListBox, WM_SETFONT, (WPARAM)hFont, TRUE); + + ShowWindow(hwnd, SW_SHOW); + + // Message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Clean up + DeleteObject(hFont); + }); + + // Detach the thread so it runs independently + guiThread.detach(); + } + + // Global vector to store todos + static std::vector g_todos; + + void NotifyCallback(const TodoCallback &callback, const std::string &json) + { + if (callback) + { + callback(json); + // Process pending messages + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + std::wstring FormatTodoDisplay(const std::wstring &text, const SYSTEMTIME &st) + { + wchar_t dateStr[64]; + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, nullptr, dateStr, 64); + return text + L" - " + dateStr; + } + + LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_COMMAND: + { + HWND hListBox = GetDlgItem(hwnd, 3); + int cmd = LOWORD(wParam); + + switch (cmd) + { + case 2: // Add button + { + wchar_t buffer[256]; + GetDlgItemTextW(hwnd, 1, buffer, 256); + + if (wcslen(buffer) > 0) + { + SYSTEMTIME st; + HWND hDatePicker = GetDlgItem(hwnd, 4); + DateTime_GetSystemtime(hDatePicker, &st); + + TodoItem todo; + CoCreateGuid(&todo.id); + todo.text = buffer; + todo.date = SystemTimeToMillis(st); + + g_todos.push_back(todo); + + std::wstring displayText = FormatTodoDisplay(buffer, st); + SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)displayText.c_str()); + + ResetControls(hwnd); + NotifyCallback(g_todoAddedCallback, todo.toJson()); + } + break; + } + } + break; + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + +} // namespace cpp_code +``` + +## 5) Creating the Node.js Addon Bridge + +Now let's implement the bridge between our C++ code and Node.js in `src/cpp_addon.cc`. Let's start by creating a basic skeleton for our addon: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + // We'll add code here later + return exports; +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This is the minimal structure required for a Node.js addon using `node-addon-api`. The `Init` function is called when the addon is loaded, and the `NODE_API_MODULE` macro registers our initializer. + +### Create a Class to Wrap Our C++ Code + +Let's create a class that will wrap our C++ code and expose it to JavaScript: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + // We'll add methods here later + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppWin32Addon", func); + return exports; + } + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + // Constructor logic will go here + } + +private: + // Will add private members and methods later +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This creates a class that inherits from `Napi::ObjectWrap`, which allows us to wrap our C++ object for use in JavaScript. The `Init` function sets up the class and exports it to JavaScript. + +### Implement Basic Functionality - HelloWorld + +Now let's add our first method, the `HelloWorld` function: + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + }); + + // ... rest of Init function + } + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + // Constructor logic will go here + } + +private: + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } +}; + +// ... rest of the file +``` + +This adds the `HelloWorld` method to our class and registers it with `DefineClass`. The method validates inputs, calls our C++ function, and returns the result to JavaScript. + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + }); + + // ... rest of Init function + } + + // ... constructor + +private: + // ... HelloWorld method + + void HelloGui(const Napi::CallbackInfo& info) { + cpp_code::hello_gui(); + } +}; + +// ... rest of the file +``` + +This simple method calls our `hello_gui` function from the C++ code, which launches the Win32 GUI window in a separate thread. + +### Setting Up the Event System + +Now comes the complex part - setting up the event system so our C++ code can call back to JavaScript. We need to: + +1. Add private members to store callbacks +2. Create a threadsafe function for cross-thread communication +3. Add an `On` method to register JavaScript callbacks +4. Set up C++ callbacks that will trigger the JavaScript callbacks + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + // ... previous public methods + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + // ... existing private methods +}; + +// ... rest of the file +``` + +Now, let's enhance our constructor to initialize these members: + +```cpp title='src/cpp_addon.cc' +// ... previous code + +class CppAddon : public Napi::ObjectWrap { +public: + // CallbackData struct to pass data between threads + struct CallbackData { + std::string eventType; + std::string payload; + CppAddon* addon; + }; + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + // We'll add threadsafe function setup here in the next step + } + + // Add destructor to clean up + ~CppAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + + // ... rest of the class +}; + +// ... rest of the file +``` + +Now let's add the threadsafe function setup to our constructor: + +```cpp title='src/cpp_addon.cc' +// ... existing constructor code +CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // We'll add callback setup in the next step +} +``` + +This creates a threadsafe function that allows our C++ code to call JavaScript from any thread. When called, it retrieves the appropriate JavaScript callback and invokes it with the provided payload. + +Now let's add the callbacks setup: + +```cpp title='src/cpp_addon.cc' +// ... existing constructor code after threadsafe function setup + +// Set up the callbacks here +auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; +}; + +cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); +``` + +This creates a function that generates callbacks for each event type. The callbacks capture the event type and, when called, create a `CallbackData` object and pass it to our threadsafe function. + +Finally, let's add the `On` method to allow JavaScript to register callback functions: + +```cpp title='src/cpp_addon.cc' +// ... in the class definition, add On to DefineClass +static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + // ... rest of Init function +} + +// ... and add the implementation in the private section +Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +This allows JavaScript to register callbacks for specific event types. + +### Putting the bridge together + +Now we have all the pieces in place. + +Here's the complete implementation: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "CppWin32Addon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppWin32Addon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + CppAddon* addon; + }; + + CppAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + } + + ~CppAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo& info) { + cpp_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +Let's finish things off by adding a JavaScript wrapper in `js/index.js`. As we could all see, C++ requires a lot of boilerplate code that might be easier or faster to write in JavaScript - and you will find that many production applications end up transforming data or requests in JavaScript before invoking native code. We, for instance, turn our timestamp into a proper JavaScript date. + +```cpp title='js/index.js' +const EventEmitter = require('events') + +class CppWin32Addon extends EventEmitter { + constructor() { + super() + + if (process.platform !== 'win32') { + throw new Error('This module is only available on Windows') + } + + const native = require('bindings')('cpp_addon') + this.addon = new native.CppWin32Addon(); + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.#parse(payload)) + }); + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.#parse(payload)) + }); + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.#parse(payload)) + }); + } + + helloWorld(input = "") { + return this.addon.helloWorld(input) + } + + helloGui() { + this.addon.helloGui() + } + + #parse(payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'win32') { + module.exports = new CppWin32Addon() +} else { + module.exports = {} +} +``` + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```psh +npm run build +``` + +## Conclusion + +You've now built a complete native Node.js addon for Windows using C++ and the Win32 API. Some of things we've done here are: + +1. Creating a native Windows GUI from C++ +2. Implementing a Todo list application with Add, Edit, and Delete functionality +3. Bidirectional communication between C++ and JavaScript +4. Using Win32 controls and Windows-specific features +5. Safely calling back into JavaScript from C++ threads + +This provides a foundation for building more complex Windows-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native code. + +For more information on working with Win32 API, refer to the [Microsoft C++, C, and Assembler documentation](https://learn.microsoft.com/en-us/cpp/?view=msvc-170) and the [Windows API reference](https://learn.microsoft.com/en-us/windows/win32/api/). From 7be433c0b70262efbf0e5acbd85036c6bac01324 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:11:05 -0500 Subject: [PATCH 113/339] fix: destroy parent port backend when JS env exits (#46496) * fix: destroy parent port backend when JS env exits Co-authored-by: Shelley Vohr * fix: close parent port before destroying Co-authored-by: deepak1556 --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr Co-authored-by: deepak1556 --- shell/services/node/node_service.cc | 2 ++ shell/services/node/parent_port.cc | 4 ++-- shell/services/node/parent_port.h | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/shell/services/node/node_service.cc b/shell/services/node/node_service.cc index 52a7a7e7bca11..dd2c5743a98a1 100644 --- a/shell/services/node/node_service.cc +++ b/shell/services/node/node_service.cc @@ -96,6 +96,7 @@ NodeService::NodeService( NodeService::~NodeService() { if (!node_env_stopped_) { node_env_->set_trace_sync_io(false); + ParentPort::GetInstance()->Close(); js_env_->DestroyMicrotasksRunner(); node::Stop(node_env_.get(), node::StopFlags::kDoNotTerminateIsolate); } @@ -147,6 +148,7 @@ void NodeService::Initialize( node_env_.get(), [this](node::Environment* env, int exit_code) { // Destroy node platform. env->set_trace_sync_io(false); + ParentPort::GetInstance()->Close(); js_env_->DestroyMicrotasksRunner(); node::Stop(env, node::StopFlags::kDoNotTerminateIsolate); node_env_stopped_ = true; diff --git a/shell/services/node/parent_port.cc b/shell/services/node/parent_port.cc index 012a588474bdd..9b1393a007e12 100644 --- a/shell/services/node/parent_port.cc +++ b/shell/services/node/parent_port.cc @@ -23,8 +23,8 @@ namespace electron { gin::WrapperInfo ParentPort::kWrapperInfo = {gin::kEmbedderNativeGin}; ParentPort* ParentPort::GetInstance() { - static base::NoDestructor instance; - return instance.get(); + static ParentPort* instance = new ParentPort(); + return instance; } ParentPort::ParentPort() = default; diff --git a/shell/services/node/parent_port.h b/shell/services/node/parent_port.h index b39d825c75215..fd950a34ea44b 100644 --- a/shell/services/node/parent_port.h +++ b/shell/services/node/parent_port.h @@ -10,6 +10,7 @@ #include "gin/wrappable.h" #include "mojo/public/cpp/bindings/connector.h" #include "mojo/public/cpp/bindings/message.h" +#include "shell/common/gin_helper/cleaned_up_at_exit.h" #include "third_party/blink/public/common/messaging/message_port_descriptor.h" namespace v8 { @@ -31,6 +32,7 @@ namespace electron { // for the lifetime of a Utility Process which // also means that GC lifecycle is ignored by this class. class ParentPort final : public gin::Wrappable, + public gin_helper::CleanedUpAtExit, private mojo::MessageReceiver { public: static ParentPort* GetInstance(); @@ -49,9 +51,10 @@ class ParentPort final : public gin::Wrappable, v8::Isolate* isolate) override; const char* GetTypeName() override; + void Close(); + private: void PostMessage(v8::Local message_value); - void Close(); void Start(); void Pause(); From 42514326cac2a59eeb70eaef2a999df78da37956 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 10:44:18 -0500 Subject: [PATCH 114/339] refactor: remove unused `electron::api::App::FileIconCallback` (#46510) refactor: remove electron::api::App::FileIconCallback last use removed in 2018 by 3f15f516 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_app.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/shell/browser/api/electron_api_app.h b/shell/browser/api/electron_api_app.h index 6a208950bd222..cc079ef5bebfc 100644 --- a/shell/browser/api/electron_api_app.h +++ b/shell/browser/api/electron_api_app.h @@ -34,10 +34,6 @@ namespace base { class FilePath; } -namespace gfx { -class Image; -} - namespace gin { template class Handle; @@ -65,9 +61,6 @@ class App final : public ElectronBrowserClient::Delegate, private content::GpuDataManagerObserver, private content::BrowserChildProcessObserver { public: - using FileIconCallback = - base::RepeatingCallback, const gfx::Image&)>; - static gin::Handle Create(v8::Isolate* isolate); static App* Get(); From 31e3c848435b09f3640c995a19a762754822a03d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 10:45:02 -0500 Subject: [PATCH 115/339] refactor: instantiate `navigation_entries` local variable on the stack (#46504) * refactor: instantiate navigation_entries on the stack instead of the heap Co-authored-by: Charles Kerr * refactor: reserve the full size of navigation_entries Co-authored-by: Charles Kerr * refactor: use emplace_back to simplify the code a little Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_web_contents.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index f1803661f9c48..abaf906582310 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2518,8 +2518,9 @@ void WebContents::RestoreHistory( return; } - auto navigation_entries = std::make_unique< - std::vector>>(); + auto navigation_entries = + std::vector>{}; + navigation_entries.reserve(entries.size()); blink::UserAgentOverride ua_override; ua_override.ua_string_override = GetUserAgent(); @@ -2539,14 +2540,13 @@ void WebContents::RestoreHistory( nav_entry->SetIsOverridingUserAgent( !ua_override.ua_string_override.empty()); - navigation_entries->push_back( - std::unique_ptr(nav_entry)); + navigation_entries.emplace_back(nav_entry); } - if (!navigation_entries->empty()) { + if (!navigation_entries.empty()) { web_contents()->SetUserAgentOverride(ua_override, false); web_contents()->GetController().Restore( - index, content::RestoreType::kRestored, navigation_entries.get()); + index, content::RestoreType::kRestored, &navigation_entries); web_contents()->GetController().LoadIfNecessary(); } } From c3b5e5527f3ff42ac813b82df73c188ff9b1077e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:11:44 +0200 Subject: [PATCH 116/339] docs: note that `titleBarOverlay.symbolColor` is supported on Linux (#46536) docs: note that titleBarOverlay.symbolColor is supported on Linux this is supported via OpaqueFrameView Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- docs/api/structures/base-window-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/structures/base-window-options.md b/docs/api/structures/base-window-options.md index 3876d69225278..84169a049ad94 100644 --- a/docs/api/structures/base-window-options.md +++ b/docs/api/structures/base-window-options.md @@ -93,7 +93,7 @@ **Note:** This option is currently experimental. * `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`. * `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color. - * `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color. + * `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color. * `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height. * `trafficLightPosition` [Point](point.md) (optional) _macOS_ - Set a custom position for the traffic light buttons in frameless windows. From 4392cb94342d51a893edb93c6ad0984973995637 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:16:34 -0500 Subject: [PATCH 117/339] perf: on Windows, make `Archive::HeaderIntegrity()` faster (#46537) * perf: do not clone the map each time we call Archive::HeaderIntegrity() Co-authored-by: Charles Kerr * perf: use absl::flat_hash_map for the integrity cache Co-authored-by: Charles Kerr * perf: do not clone the JSON payload string Co-authored-by: Charles Kerr * perf: preallocate capacity for the integrity cache Co-authored-by: Charles Kerr * perf: use move variant of insert_or_assign() Co-authored-by: Charles Kerr * refactor: simplify integrity cache building remove unnecessary std::optional<> Co-authored-by: Charles Kerr * refactor: use base::FindOrNull() Co-authored-by: Charles Kerr * refactor: remove unused #includes Co-authored-by: Charles Kerr * refactor: make variable types explicit Co-authored-by: Charles Kerr * fix: make res_size unsigned Co-authored-by: Charles Kerr * refactor: put GetIntegrityConfigCache() in an unnamed namespace refator: put LoadIntegrityConfig() in an unnamed namespace Co-authored-by: Charles Kerr * fix: oops, missing rel_path_utf8 key Co-authored-by: Charles Kerr * fix: oops, fix Wunreachable-code-return Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/common/asar/archive_win.cc | 66 +++++++++++++------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/shell/common/asar/archive_win.cc b/shell/common/asar/archive_win.cc index d1deb6e346b90..861c2fec086e7 100644 --- a/shell/common/asar/archive_win.cc +++ b/shell/common/asar/archive_win.cc @@ -6,9 +6,10 @@ #include "shell/common/asar/archive.h" #include -#include +#include #include "base/base_paths.h" +#include "base/containers/map_util.h" #include "base/json/json_reader.h" #include "base/logging.h" #include "base/no_destructor.h" @@ -17,6 +18,7 @@ #include "base/strings/string_util_win.h" #include "base/strings/utf_string_conversions.h" #include "shell/common/asar/asar_util.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_map.h" namespace asar { @@ -37,19 +39,10 @@ std::optional Archive::RelativePath() const { return relative_path; } -std::optional> -LoadIntegrityConfigCache() { - static base::NoDestructor< - std::optional>> - integrity_config_cache; +namespace { - // Skip loading if cache is already loaded - if (integrity_config_cache->has_value()) { - return *integrity_config_cache; - } - - // Init cache - *integrity_config_cache = std::unordered_map(); +auto LoadIntegrityConfig() { + absl::flat_hash_map cache; // Load integrity config from exe resource HMODULE module_handle = ::GetModuleHandle(NULL); @@ -65,8 +58,8 @@ LoadIntegrityConfigCache() { PLOG(FATAL) << "LoadResource failed."; } - auto* res_data = static_cast(::LockResource(rcData)); - int res_size = SizeofResource(module_handle, resource); + const auto* res_data = static_cast(::LockResource(rcData)); + const auto res_size = SizeofResource(module_handle, resource); if (!res_data) { PLOG(FATAL) << "Failed to integrity config from exe resource."; @@ -77,9 +70,8 @@ LoadIntegrityConfigCache() { } // Parse integrity config payload - std::string integrity_config_payload = std::string(res_data, res_size); std::optional root = - base::JSONReader::Read(integrity_config_payload); + base::JSONReader::Read(std::string_view{res_data, res_size}); if (!root.has_value()) { LOG(FATAL) << "Invalid integrity config: NOT a valid JSON."; @@ -91,6 +83,7 @@ LoadIntegrityConfigCache() { } // Parse each individual file integrity config + cache.reserve(file_configs->size()); for (size_t i = 0; i < file_configs->size(); i++) { // Skip invalid file configs const base::Value::Dict* ele_dict = (*file_configs)[i].GetIfDict(); @@ -122,37 +115,30 @@ LoadIntegrityConfigCache() { header_integrity.algorithm = HashAlgorithm::kSHA256; header_integrity.hash = base::ToLowerASCII(*value); - integrity_config_cache->value()[base::ToLowerASCII(*file)] = - std::move(header_integrity); + cache.insert_or_assign(base::ToLowerASCII(*file), + std::move(header_integrity)); } - return *integrity_config_cache; + return cache; } -std::optional Archive::HeaderIntegrity() const { - std::optional relative_path = RelativePath(); - // Callers should have already asserted this - CHECK(relative_path.has_value()); +const auto& GetIntegrityConfigCache() { + static const auto cache = base::NoDestructor(LoadIntegrityConfig()); + return *cache; +} - // Load integrity config from exe resource - std::optional> - integrity_config = LoadIntegrityConfigCache(); - if (!integrity_config.has_value()) { - LOG(WARNING) << "Failed to integrity config from exe resource."; - return std::nullopt; - } +} // namespace - // Convert Window rel path to UTF8 lower case - std::string rel_path_utf8 = base::WideToUTF8(relative_path.value().value()); - rel_path_utf8 = base::ToLowerASCII(rel_path_utf8); +std::optional Archive::HeaderIntegrity() const { + const std::optional relative_path = RelativePath(); + CHECK(relative_path); - // Find file integrity config - auto iter = integrity_config.value().find(rel_path_utf8); - if (iter == integrity_config.value().end()) { - LOG(FATAL) << "Failed to find file integrity info for " << rel_path_utf8; - } + const auto key = base::ToLowerASCII(base::WideToUTF8(relative_path->value())); + + if (const auto* payload = base::FindOrNull(GetIntegrityConfigCache(), key)) + return *payload; - return iter->second; + LOG(FATAL) << "Failed to find file integrity info for " << key; } } // namespace asar From 64c9afcf770a0fa360f44099f0ce9bdd8fb5a904 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:16:56 -0500 Subject: [PATCH 118/339] refactor: use `base::flat_set` in `WebContents::DidUpdateFaviconUrl()` (#46530) * refactor: add gin::Converter::ToV8() Co-authored-by: Charles Kerr * feat: add ToV8(const base::flat_set&) Co-authored-by: Charles Kerr * perf: use a flat_set in WebContents::TitleWasSet() Co-authored-by: Charles Kerr * refactor: add gin::Converter::ToV8() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../browser/api/electron_api_web_contents.cc | 12 ++++---- shell/common/gin_converters/base_converter.h | 9 ++++++ shell/common/gin_converters/std_converter.h | 28 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index abaf906582310..349eb5d1f7ab0 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -16,6 +16,7 @@ #include "base/base64.h" #include "base/containers/fixed_flat_map.h" +#include "base/containers/flat_set.h" #include "base/containers/id_map.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" @@ -2119,13 +2120,12 @@ void WebContents::TitleWasSet(content::NavigationEntry* entry) { void WebContents::DidUpdateFaviconURL( content::RenderFrameHost* render_frame_host, const std::vector& urls) { - std::set unique_urls; + base::flat_set unique_urls; + unique_urls.reserve(std::size(urls)); for (const auto& iter : urls) { - if (iter->icon_type != blink::mojom::FaviconIconType::kFavicon) - continue; - const GURL& url = iter->icon_url; - if (url.is_valid()) - unique_urls.insert(url); + if (iter->icon_type == blink::mojom::FaviconIconType::kFavicon && + iter->icon_url.is_valid()) + unique_urls.insert(iter->icon_url); } Emit("page-favicon-updated", unique_urls); } diff --git a/shell/common/gin_converters/base_converter.h b/shell/common/gin_converters/base_converter.h index d48da0508931a..56c420d77d817 100644 --- a/shell/common/gin_converters/base_converter.h +++ b/shell/common/gin_converters/base_converter.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_BASE_CONVERTER_H_ #define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_BASE_CONVERTER_H_ +#include "base/containers/flat_set.h" #include "base/process/kill.h" #include "gin/converter.h" #include "shell/common/gin_converters/std_converter.h" @@ -41,6 +42,14 @@ struct Converter { } }; +template +struct Converter> { + static v8::Local ToV8(v8::Isolate* isolate, + const base::flat_set& set) { + return Converter>::ToV8(isolate, std::span{set}); + } +}; + } // namespace gin #endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_BASE_CONVERTER_H_ diff --git a/shell/common/gin_converters/std_converter.h b/shell/common/gin_converters/std_converter.h index 3eaa7eecdbca4..4ea06b487de7a 100644 --- a/shell/common/gin_converters/std_converter.h +++ b/shell/common/gin_converters/std_converter.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,33 @@ v8::Local ConvertToV8(v8::Isolate* isolate, T&& input) { isolate, std::forward(input)); } +template +struct Converter> { + static v8::Local ToV8(v8::Isolate* isolate, + const std::span& span) { + int idx = 0; + auto context = isolate->GetCurrentContext(); + auto result = v8::Array::New(isolate, static_cast(span.size())); + for (const auto& val : span) { + v8::MaybeLocal maybe = Converter::ToV8(isolate, val); + v8::Local element; + if (!maybe.ToLocal(&element)) + return {}; + if (!result->Set(context, idx++, element).FromMaybe(false)) + NOTREACHED() << "CreateDataProperty should always succeed here."; + } + return result; + } +}; + +template +struct Converter> { + static v8::Local ToV8(v8::Isolate* isolate, + const std::array& array) { + return Converter>::ToV8(isolate, std::span{array}); + } +}; + #if !BUILDFLAG(IS_LINUX) template <> struct Converter { // NOLINT(runtime/int) From ec34d8ee49d32fb7eefeaf51fc9ac471213b6f9c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:40:10 -0500 Subject: [PATCH 119/339] perf: avoid redundant virtual method call in `NativeWindowViews::SetEnabledInternal()` (#46527) perf: avoid redundant virtual method call in NativeWindowViews::SetEnabledInternal() Why waste time make lot call when few call do trick? Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index e802bd6152ecb..5b6fda2a6d417 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -623,11 +623,8 @@ bool NativeWindowViews::ShouldBeEnabled() const { } void NativeWindowViews::SetEnabledInternal(bool enable) { - if (enable && IsEnabled()) { + if (enable == IsEnabled()) return; - } else if (!enable && !IsEnabled()) { - return; - } #if BUILDFLAG(IS_WIN) ::EnableWindow(GetAcceleratedWidget(), enable); From 5942ac580bbe9647b345977aac686cbf0bc34437 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:42:11 -0500 Subject: [PATCH 120/339] revert: allow NSMenuItems to be disabled (#46523) Revert "fix: allow NSMenuItems to be disabled (#46307)" This reverts commit ac616ef41d9379ead79130d6da071cad220b21d2. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Hailey --- .../browser/ui/cocoa/electron_menu_controller.mm | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/shell/browser/ui/cocoa/electron_menu_controller.mm b/shell/browser/ui/cocoa/electron_menu_controller.mm index 2c5eae1499b16..d23e007dd0bad 100644 --- a/shell/browser/ui/cocoa/electron_menu_controller.mm +++ b/shell/browser/ui/cocoa/electron_menu_controller.mm @@ -320,10 +320,6 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:label action:@selector(itemSelected:) keyEquivalent:@""]; - if (model->IsEnabledAt(index)) - [item setEnabled:YES]; - else - [item setEnabled:NO]; // If the menu item has an icon, set it. ui::ImageModel icon = model->GetIconAt(index); @@ -353,6 +349,11 @@ - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index [item setSubmenu:[self createShareMenuForItem:sharing_item]]; } else if (type == electron::ElectronMenuModel::TYPE_SUBMENU && model->IsVisibleAt(index)) { + // We need to specifically check that the submenu top-level item has been + // enabled as it's not validated by validateUserInterfaceItem + if (!model->IsEnabledAt(index)) + [item setEnabled:NO]; + // Recursively build a submenu from the sub-model at this index. [item setTarget:nil]; [item setAction:nil]; @@ -492,10 +493,8 @@ - (void)performShare:(NSMenuItem*)sender { } - (NSMenu*)menu { - if (menu_) { - [menu_ setAutoenablesItems:NO]; + if (menu_) return menu_; - } if (model_ && model_->sharing_item()) { NSMenu* menu = [self createShareMenuForItem:*model_->sharing_item()]; @@ -505,8 +504,6 @@ - (NSMenu*)menu { if (model_) [self populateWithModel:model_.get()]; } - - [menu_ setAutoenablesItems:NO]; [menu_ setDelegate:self]; return menu_; } From 2dd4b77ae06ef20843817636a761c43ad40ac445 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:09:35 -0500 Subject: [PATCH 121/339] chore: bump chromium to 136.0.7095.0 (36-x-y) (#46184) * chore: bump chromium in DEPS to 136.0.7081.1 * chore: bump chromium in DEPS to 136.0.7083.1 * chore: bump chromium in DEPS to 136.0.7085.1 * chore: bump chromium in DEPS to 136.0.7087.1 * chore: bump chromium in DEPS to 136.0.7089.0 * chore: bump chromium in DEPS to 136.0.7091.0 * chore: bump chromium in DEPS to 136.0.7092.0 * chore: bump chromium in DEPS to 136.0.7093.1 * chore: bump chromium in DEPS to 136.0.7095.1 * chore: bump chromium in DEPS to 136.0.7097.1 * chore: bump chromium in DEPS to 136.0.7099.1 * chore: bump chromium in DEPS to 136.0.7101.0 * chore: bump chromium in DEPS to 136.0.7103.0 * chore: bump chromium in DEPS to 136.0.7103.15 * chore: bump chromium in DEPS to 136.0.7103.17 * chore: bump chromium to 136.0.7095.0 (main) (#46118) * chore: bump chromium in DEPS to 136.0.7076.0 * chore: bump chromium in DEPS to 136.0.7077.0 * 6368856: Migrate absl variant.h and utility.h in content (part 2/2) | https://chromium-review.googlesource.com/c/chromium/src/+/6368856 * 6356528: Clean up LegacyRenderWidgetHostHWND code | https://chromium-review.googlesource.com/c/chromium/src/+/6356528 * chore: export patches * 6339113: [Viewport Segments] Add CDP commands to override Viewport Segments without overriding other device properties. | https://chromium-review.googlesource.com/c/chromium/src/+/6339113 * 6352169: [DevTools][MultiInstance] Support new tab in another window on Android | https://chromium-review.googlesource.com/c/chromium/src/+/6352169 * 6368856: Migrate absl variant.h and utility.h in content (part 2/2) | https://chromium-review.googlesource.com/c/chromium/src/+/6368856 * 6360858:Clickiness: Wire response from URLLoader to DB, add e2e tests| https://chromium-review.googlesource.com/c/chromium/src/+/6360858 * chore: bump chromium in DEPS to 136.0.7079.0 * chore: export patches * chore: bump chromium in DEPS to 136.0.7081.0 * chore: export patches * chore: bump chromium in DEPS to 136.0.7083.0 * 6361987: Remove double-declaration with gfx::NativeView and gfx::NativeWindow | https://chromium-review.googlesource.com/c/chromium/src/+/6361987 * chore: export patches * chore: bump chromium in DEPS to 136.0.7087.0 * chore: export patches * fix: include node patch for missing AtomicsWaitEvent https://chromium-review.googlesource.com/c/chromium/src/+/6385540 * build: add depot_tools python to path * fix: cppgc init and unregistering v8 isolate https://chromium-review.googlesource.com/c/v8/v8/+/6333562 CppGc is now initialized earlier so Node can skip reinitializing it. Additionally, gin::IsolateHandle was attempting to destruct an already destructed v8::Isolate upon electron::JavaScriptEnvironment destruction. By removing the call to NodePlatform::UnregisterIsolate, this fixes the crash on app shutdown. * fix: unregister isolate after destruction See code comment. * chore: bump chromium in DEPS to 136.0.7095.0 * chore: sync patches * fix: add script_parsing::ContentScriptType parameter https://chromium-review.googlesource.com/c/chromium/src/+/6298395 * fix: migrate content::BrowserAccessibilityState methods https://chromium-review.googlesource.com/c/chromium/src/+/6401437 https://chromium-review.googlesource.com/c/chromium/src/+/6383275 * feat: enableHappyEyeballs option for host resolver https://chromium-review.googlesource.com/c/chromium/src/+/6332599 * fix: add new cookie exclusion reason https://chromium-review.googlesource.com/c/chromium/src/+/6343479 * fix: add new url loader method https://chromium-review.googlesource.com/c/chromium/src/+/6337340 * fix: add new cppgc header file for electron_node headers https://chromium-review.googlesource.com/c/v8/v8/+/6348644 * fix: disable CREL on Linux ARM64 https://chromium-review.googlesource.com/q/I3a62f02f564f07be63173b0773b4ecaffbe939b9 * fixup! fix: add new cppgc header file for electron_node headers https://chromium-review.googlesource.com/c/v8/v8/+/6348644 * chore: update corner smoothing patch * fixup! chore: update corner smoothing patch * chore: disable NAN weak tests These two tests are incompatible with a V8 change that disallows running JS code from a weak finalizer callback. Ref: https://chromium-review.googlesource.com/c/v8/v8/+/4733273 * test: fix task starvation in node test A V8 change makes these contexts get collected in a task that is posted and run asynchronously. The tests were synchronously GC'ing in an infinite loop, preventing the task loop from running the task that would GC these contexts. This change should be upstreamed in some way. Ref: https://chromium-review.googlesource.com/c/v8/v8/+/4733273 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: alice Co-authored-by: Samuel Maddock Co-authored-by: John Kleinschmidt Co-authored-by: clavin (cherry picked from commit 9c019b6147e88dc14959cdb8e555de0fe52f51a4) * Remove file-wide unsafe buffer suppression from content/ [3 of N] https://chromium-review.googlesource.com/c/chromium/src/+/6341711 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .../actions/install-build-tools/action.yml | 1 + DEPS | 2 +- docs/api/app.md | 6 + electron_unsafe_buffers_paths.txt | 1 + patches/boringssl/expose_ripemd160.patch | 2 +- ...xpose_several_extra_cipher_functions.patch | 2 +- ...ack_ssl_error_zero_return_explicitly.patch | 6 +- patches/chromium/.patches | 2 +- .../add_didinstallconditionalfeatures.patch | 10 +- ...lectron_deps_to_license_credits_file.patch | 4 +- ...pedcliboardwriter_writeunsaferawdata.patch | 6 +- ...adjust_accessibility_ui_for_electron.patch | 4 +- ..._scheduler_throttling_per_renderview.patch | 8 +- ..._windows_to_have_different_web_prefs.patch | 14 +- patches/chromium/blink_local_frame.patch | 2 +- patches/chromium/boringssl_build_gn.patch | 2 +- ...ld_allow_electron_to_use_exec_script.patch | 4 +- ..._depend_on_packed_resource_integrity.patch | 16 +- patches/chromium/build_gn.patch | 2 +- .../build_libc_as_static_library.patch | 6 +- patches/chromium/can_create_window.patch | 36 +- ..._introduce_blocking_api_for_electron.patch | 6 +- ...fy_chromium_handling_of_mouse_events.patch | 18 +- .../chromium/chore_partial_revert_of.patch | 4 +- ...rofile_methods_in_chrome_browser_pdf.patch | 2 +- ...screationoverridden_with_full_params.patch | 22 +- ...e_browser_v8_snapshot_file_name_fuse.patch | 31 +- .../disable_compositor_recycling.patch | 4 +- patches/chromium/disable_hidden.patch | 4 +- .../chromium/enable_reset_aspect_ratio.patch | 6 +- ...locator_for_usage_outside_of_the_gin.patch | 6 +- ...xpose_setuseragent_on_networkcontext.patch | 6 +- .../extend_apply_webpreferences.patch | 6 +- ...dd_set_theme_source_to_allow_apps_to.patch | 8 +- ...e_launch_options_for_service_process.patch | 14 +- ...moothing_css_rule_and_blink_painting.patch | 188 ++-- ...g_symbol_color_in_framecaptionbutton.patch | 14 +- ...screen_rendering_with_viz_compositor.patch | 18 +- ...g_exit_code_on_service_process_crash.patch | 6 +- ...etdefersloading_on_webdocumentloader.patch | 6 +- ..._raw_response_headers_from_urlloader.patch | 26 +- ...ivate_background_material_on_windows.patch | 6 +- ...ables_headless_mode_on_native_widget.patch | 4 +- .../fix_aspect_ratio_with_max_size.patch | 4 +- ...ding_non-standard_schemes_in_iframes.patch | 6 +- ..._background_throttling_in_compositor.patch | 6 +- ...ingshelper_behind_branding_buildflag.patch | 14 +- ...king_and_message_bubbling_on_windows.patch | 30 +- ...board_hides_on_input_blur_in_webview.patch | 12 +- ..._properly_honor_printing_page_ranges.patch | 4 +- ...x_remove_caption-removing_style_call.patch | 4 +- ...original_resize_performance_on_macos.patch | 4 +- ...from_localframe_requestexecutescript.patch | 10 +- ...t_menu_item_when_opened_via_keyboard.patch | 6 +- ...s_into_account_when_showing_a_window.patch | 58 - .../fix_win32_synchronous_spellcheck.patch | 4 +- patches/chromium/frame_host_manager.patch | 4 +- .../gin_enable_disable_v8_platform.patch | 6 +- .../chromium/gritsettings_resource_ids.patch | 4 +- ...sync_with_host_os_mac_on_linux_in_ci.patch | 2 +- patches/chromium/ignore_rc_check.patch | 4 +- ...tform_electron_can_call_x11_property.patch | 4 +- .../load_v8_snapshot_in_browser_process.patch | 4 +- ..._avoid_private_macos_api_usage.patch.patch | 96 +- ...emote_certificate_verification_logic.patch | 6 +- .../chromium/notification_provenance.patch | 8 +- ...eated_to_allow_for_browser_initiated.patch | 4 +- patches/chromium/picture-in-picture.patch | 2 +- ...utofill_colors_to_the_color_pipeline.patch | 6 +- patches/chromium/printing.patch | 12 +- ...r_changes_to_the_webcontentsobserver.patch | 14 +- ...efactor_unfilter_unresponsive_events.patch | 4 +- .../render_widget_host_view_base.patch | 2 +- .../render_widget_host_view_mac.patch | 10 +- patches/chromium/resource_file_conflict.patch | 6 +- ...ean_up_stale_macwebcontentsocclusion.patch | 4 +- ...revert_enable_crel_for_arm32_targets.patch | 23 + ...ssivethrottlingwithwebsocket_feature.patch | 2 +- patches/chromium/scroll_bounce_flag.patch | 4 +- .../support_mixed_sandbox_with_zygote.patch | 4 +- patches/chromium/web_contents.patch | 6 +- patches/chromium/webview_fullscreen.patch | 10 +- .../worker_context_will_destroy.patch | 16 +- ...feat_add_hook_to_notify_script_ready.patch | 12 +- ...i_to_allow_electron_to_set_dock_side.patch | 4 +- patches/node/.patches | 5 +- patches/node/build_add_gn_build_files.patch | 12 +- ...cli_move_--trace-atomics-wait_to_eol.patch | 995 ++++++++++++++++++ .../node/fix_cppgc_initializing_twice.patch | 26 + ...starvation_in_inspector_context_test.patch | 68 ++ patches/v8/.patches | 1 - ...8_object_setinternalfieldfornodecore.patch | 2 +- ...ated_attachcppheap_and_detachcppheap.patch | 137 --- script/nan-spec-runner.js | 5 +- shell/app/electron_main_delegate.cc | 5 +- shell/app/electron_main_delegate.h | 2 +- shell/browser/api/electron_api_app.cc | 18 +- shell/browser/api/electron_api_cookies.cc | 5 +- .../extensions/api/scripting/scripting_api.cc | 13 +- shell/browser/javascript_environment.cc | 33 +- shell/browser/javascript_environment.h | 4 +- shell/browser/mac/electron_application.mm | 8 +- shell/browser/native_window.h | 2 +- shell/browser/native_window_mac.mm | 8 +- shell/browser/native_window_views_win.cc | 4 +- .../net/system_network_context_manager.cc | 1 + .../browser/net/url_loader_network_observer.h | 4 + .../browser/osr/osr_render_widget_host_view.h | 3 +- .../browser/osr/osr_web_contents_view_mac.mm | 6 +- shell/browser/ui/devtools_manager_delegate.cc | 3 +- shell/browser/ui/devtools_manager_delegate.h | 3 +- shell/browser/ui/webui/accessibility_ui.cc | 12 +- shell/common/api/electron_api_url_loader.h | 4 + 113 files changed, 1647 insertions(+), 728 deletions(-) delete mode 100644 patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch create mode 100644 patches/chromium/revert_enable_crel_for_arm32_targets.patch create mode 100644 patches/node/cli_move_--trace-atomics-wait_to_eol.patch create mode 100644 patches/node/fix_cppgc_initializing_twice.patch create mode 100644 patches/node/fix_task_starvation_in_inspector_context_test.patch delete mode 100644 patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index d405dfa1cd212..33cb28cfc8aa5 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -20,3 +20,4 @@ runs: cp "C:\Python311\python.exe" "C:\Python311\python3.exe" fi echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH + echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH diff --git a/DEPS b/DEPS index 0316ae3e71b79..bd8ba726c01c1 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7067.0', + '136.0.7095.0', 'node_version': 'v22.14.0', 'nan_version': diff --git a/docs/api/app.md b/docs/api/app.md index 988edf22c7ca1..7bc011cf3d54e 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1110,6 +1110,11 @@ indicates success while any other value indicates failure according to Chromium resolver will attempt to use the system's DNS settings to do DNS lookups itself. Enabled by default on macOS, disabled by default on Windows and Linux. + * `enableHappyEyeballs` boolean (optional) - Whether the + [Happy Eyeballs V3][happy-eyeballs-v3] algorithm should be used in creating + network connections. When enabled, hostnames resolving to multiple IP + addresses will be attempted in parallel to have a chance at establishing a + connection more quickly. * `secureDnsMode` string (optional) - Can be 'off', 'automatic' or 'secure'. Configures the DNS-over-HTTP mode. When 'off', no DoH lookups will be performed. When 'automatic', DoH lookups will be performed first if DoH is @@ -1579,6 +1584,7 @@ A `boolean` property that returns `true` if the app is packaged, `false` otherw [Squirrel-Windows]: https://github.com/Squirrel/Squirrel.Windows [JumpListBeginListMSDN]: https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist [about-panel-options]: https://developer.apple.com/reference/appkit/nsapplication/1428479-orderfrontstandardaboutpanelwith?language=objc +[happy-eyeballs-v3]: https://datatracker.ietf.org/doc/draft-pauly-happy-happyeyeballs-v3/ ### `app.name` diff --git a/electron_unsafe_buffers_paths.txt b/electron_unsafe_buffers_paths.txt index 14389345918d6..62ed395531a0b 100644 --- a/electron_unsafe_buffers_paths.txt +++ b/electron_unsafe_buffers_paths.txt @@ -21,6 +21,7 @@ -base/ -chrome/ -components/ +-content/browser/indexed_db -device/ -extensions/ -google_apis/ diff --git a/patches/boringssl/expose_ripemd160.patch b/patches/boringssl/expose_ripemd160.patch index 024a131fab244..ad7ed38e1e075 100644 --- a/patches/boringssl/expose_ripemd160.patch +++ b/patches/boringssl/expose_ripemd160.patch @@ -82,7 +82,7 @@ index e04b80cd6a1a215fc87f8fd8d750c3d258c3974f..8fdf1c624794f568bfc77b7b6b0c510b void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher, const char *name, diff --git a/include/openssl/digest.h b/include/openssl/digest.h -index 5ddc2d3b4cfb8a87eb22fb707230f56dcb7ccb3e..dea3c5b3adf49e1b4aab197e822744c80964afac 100644 +index b36c5885a31ff3242ac3104e6d875a008c7c39e4..8fe32508a00263295313e77992e2c208459ca42c 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -48,6 +48,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_blake2b256(void); diff --git a/patches/boringssl/feat_expose_several_extra_cipher_functions.patch b/patches/boringssl/feat_expose_several_extra_cipher_functions.patch index 0b37e96bb0bbd..b4fdeb6d33b59 100644 --- a/patches/boringssl/feat_expose_several_extra_cipher_functions.patch +++ b/patches/boringssl/feat_expose_several_extra_cipher_functions.patch @@ -118,7 +118,7 @@ index 8fdf1c624794f568bfc77b7b6b0c510b23905a4d..2e40c031e8c681fe921331b26dbf63f4 callback(EVP_des_ede3_cbc(), "des-ede3-cbc", NULL, arg); callback(EVP_rc2_cbc(), "rc2-cbc", NULL, arg); diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h -index 6bb135801326bc1cbe2b93f02e561e38c90abeca..06bcba4451456c72b168266859482343af76a931 100644 +index 13e68ad20ac08a462bb577d7f99e2c6f167579fa..4960d0eeb8f31bec4347ed2a1b63beba530de700 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h @@ -448,6 +448,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_des_ede3_ecb(void); diff --git a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch index 9b355b6f09620..4fab9c81ec1d6 100644 --- a/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch +++ b/patches/boringssl/revert_track_ssl_error_zero_return_explicitly.patch @@ -20,10 +20,10 @@ index 2cdcbc346175eeee69402ecee7f169e61c655199..f7226fe711e4214b216ea2c5173a0212 case ssl_open_record_error: diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index 10b062abf8304df32652c57f377d57209bb47ed1..4eefe928daaf959d0cb1f0820e01ee05754bb4d5 100644 +index d228b1c3b517e21fb8022c1927b0bc522f46bf78..4a94410d0ea4daabde1397c78c5e5de113398ddf 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -1206,7 +1206,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { +@@ -1205,7 +1205,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) { } if (ret_code == 0) { @@ -32,7 +32,7 @@ index 10b062abf8304df32652c57f377d57209bb47ed1..4eefe928daaf959d0cb1f0820e01ee05 return SSL_ERROR_ZERO_RETURN; } // An EOF was observed which violates the protocol, and the underlying -@@ -2573,13 +2573,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { +@@ -2572,13 +2572,7 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } diff --git a/patches/chromium/.patches b/patches/chromium/.patches index f48fa2a884c05..d982f77f50033 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -144,4 +144,4 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch fix_linter_error.patch -fix_take_snapped_status_into_account_when_showing_a_window.patch +revert_enable_crel_for_arm32_targets.patch diff --git a/patches/chromium/add_didinstallconditionalfeatures.patch b/patches/chromium/add_didinstallconditionalfeatures.patch index 34880274e34a3..fe920c58a4766 100644 --- a/patches/chromium/add_didinstallconditionalfeatures.patch +++ b/patches/chromium/add_didinstallconditionalfeatures.patch @@ -23,7 +23,7 @@ index 44da0544b778d6ff4c14b6f4e8463cb8260d2f0d..8ae8939af4141a684b7a6d50a43e1abb int32_t world_id) {} virtual void DidClearWindowObject() {} diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 0fb92081a1bbfb14c0ddd74dfe91f94bb2be1d2a..886f677564084d8b6af15b03403cfa56aeddd3c9 100644 +index 4185aa3e07df8f2a3061d18e87f39cd5d79baead..81261373a194210d97c723ed525cb75a2bbeafad 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -4802,6 +4802,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, @@ -40,7 +40,7 @@ index 0fb92081a1bbfb14c0ddd74dfe91f94bb2be1d2a..886f677564084d8b6af15b03403cfa56 int world_id) { for (auto& observer : observers_) diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h -index bf073c9180d47243f09fdd052a43dfe7e34aa4aa..d1642b675e474568c302553a288253b639ed1efd 100644 +index a072319de5d16d8004cb33792b2275320627fe6a..f52aa0470ca53c9997961c398d2c79d25967562e 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h @@ -653,6 +653,8 @@ class CONTENT_EXPORT RenderFrameImpl @@ -53,7 +53,7 @@ index bf073c9180d47243f09fdd052a43dfe7e34aa4aa..d1642b675e474568c302553a288253b6 int world_id) override; void DidChangeScrollOffset() override; diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h -index 038c13c1e697d4f21160e0329f659b609802ecc1..60fd9aa69a86c32abf79ef8737e810b0017e1083 100644 +index fade7618e12889da5a1c3cbe465dec0869634407..53aaf91fe5e68465c9ec8b181e33b5ee95a5a5aa 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h @@ -664,6 +664,9 @@ class BLINK_EXPORT WebLocalFrameClient { @@ -123,10 +123,10 @@ index 4c7375a27a22f04694e14fecc17d633734d4cccc..ea2a32b151aaf07b5704e463d01714eb int32_t world_id) override; diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h -index 8ca461c8fc3f2e508be5783406b570062ccf5de0..3c67d214ecd2b3b2f3e8955836e8ef7c0f0a525d 100644 +index 286d95397da7b90ba28b4c4faaae26d6ab71a496..23fd67571152897b3bc93fde8e98e1d554cce275 100644 --- a/third_party/blink/renderer/core/loader/empty_clients.h +++ b/third_party/blink/renderer/core/loader/empty_clients.h -@@ -416,6 +416,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { +@@ -417,6 +417,8 @@ class CORE_EXPORT EmptyLocalFrameClient : public LocalFrameClient { void DidCreateScriptContext(v8::Local, int32_t world_id) override {} diff --git a/patches/chromium/add_electron_deps_to_license_credits_file.patch b/patches/chromium/add_electron_deps_to_license_credits_file.patch index cce3480fa518f..82c66e747e0ec 100644 --- a/patches/chromium/add_electron_deps_to_license_credits_file.patch +++ b/patches/chromium/add_electron_deps_to_license_credits_file.patch @@ -7,10 +7,10 @@ Ensure that licenses for the dependencies introduced by Electron are included in `LICENSES.chromium.html` diff --git a/tools/licenses/licenses.py b/tools/licenses/licenses.py -index 12ad657f5df3ff4af2bdbd8b9fb7959131db2970..f2b03596dd5ff96236272b6348336515df85eef0 100755 +index 7b948007755d306babea73498c17031c437ae6dc..adcfad8931c314f86d26347ccda438af452b8167 100755 --- a/tools/licenses/licenses.py +++ b/tools/licenses/licenses.py -@@ -336,6 +336,31 @@ SPECIAL_CASES = { +@@ -337,6 +337,31 @@ SPECIAL_CASES = { "License": "Apache 2.0", "License File": ["//third_party/dawn/third_party/khronos/LICENSE"], }, diff --git a/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch b/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch index bfb86df138f29..8d721cdf6f968 100644 --- a/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch +++ b/patches/chromium/add_ui_scopedcliboardwriter_writeunsaferawdata.patch @@ -8,10 +8,10 @@ was removed as part of the Raw Clipboard API scrubbing. https://bugs.chromium.org/p/chromium/issues/detail?id=1217643 diff --git a/ui/base/clipboard/scoped_clipboard_writer.cc b/ui/base/clipboard/scoped_clipboard_writer.cc -index fd162a607424bd4d0b324ccc7270522eb3b6a991..df1d5564a52ef67b447bbc222ceb1f864eb3dd81 100644 +index 8064a2c27d747d4862503526496a65987df7dc35..c92596713c6d3884be3f63bf78d9117b7ad3ec90 100644 --- a/ui/base/clipboard/scoped_clipboard_writer.cc +++ b/ui/base/clipboard/scoped_clipboard_writer.cc -@@ -230,6 +230,16 @@ void ScopedClipboardWriter::WriteData(std::u16string_view format, +@@ -237,6 +237,16 @@ void ScopedClipboardWriter::WriteData(std::u16string_view format, } } @@ -29,7 +29,7 @@ index fd162a607424bd4d0b324ccc7270522eb3b6a991..df1d5564a52ef67b447bbc222ceb1f86 objects_.clear(); raw_objects_.clear(); diff --git a/ui/base/clipboard/scoped_clipboard_writer.h b/ui/base/clipboard/scoped_clipboard_writer.h -index d8fd611e88c22f2c1319da769dcd14cc218f9c90..f17304e1e9fd202dc4e1b7724031dc4c333a4baf 100644 +index 939a99b2a086d5373f82fe96da73dabe02f6f9d8..fccc200b1b11076c8fcffde071a53598ffba9a12 100644 --- a/ui/base/clipboard/scoped_clipboard_writer.h +++ b/ui/base/clipboard/scoped_clipboard_writer.h @@ -87,6 +87,10 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { diff --git a/patches/chromium/adjust_accessibility_ui_for_electron.patch b/patches/chromium/adjust_accessibility_ui_for_electron.patch index 85f71b85b7724..0eab8c33d29a0 100644 --- a/patches/chromium/adjust_accessibility_ui_for_electron.patch +++ b/patches/chromium/adjust_accessibility_ui_for_electron.patch @@ -10,7 +10,7 @@ usage of BrowserList and Browser as we subclass related methods and use our WindowList. diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc -index 230c9cb619498f315fc0913da54837b725fc9024..0fa1120ab952864e53085e7746608bb491ec14b2 100644 +index 628fe5557f963c52cbe0c5b24c6e06aaa81f9677..868f5eb60c35dfaa0a4e33e7f175743848e0cc2e 100644 --- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc +++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc @@ -48,6 +48,7 @@ @@ -38,7 +38,7 @@ index 230c9cb619498f315fc0913da54837b725fc9024..0fa1120ab952864e53085e7746608bb4 + PrefService* pref = static_cast(current_context)->prefs(); ui::AXMode mode = content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode(); - bool is_native_enabled = content::BrowserAccessibilityState::GetInstance() + bool is_a11y_allowed = content::BrowserAccessibilityState::GetInstance() @@ -221,7 +222,7 @@ void HandleAccessibilityRequestCallback( data.Set(kPDFPrinting, pdf_printing ? kOn : kOff); diff --git a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch index 377c31242d6be..510696788770e 100644 --- a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch +++ b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch @@ -51,7 +51,7 @@ index 56e52f079d1ad7c7a22764b976f0c8b2cc48dff2..1231fe522ad103e94d3c30ad7d5e5d23 void SendRendererPreferencesToRenderer( const blink::RendererPreferences& preferences); diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 19d2df47b0c929558ac8cfc208742dfef463a68b..7e9f2e71f08c1324a805462064d4fa485041c19f 100644 +index d719b546b8c3c59003698b26dead065da7d76341..95a52f1cc2024e4a9cd694429d5304a5860a1c1e 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -579,8 +579,8 @@ void RenderWidgetHostViewAura::ShowImpl(PageVisibilityState page_visibility) { @@ -116,10 +116,10 @@ index b1689844282d6917b9750fbc6a875848ddf84b70..f1cc159b7c3448a33a6d9e213f8fbd3b // Visibility ----------------------------------------------------------- diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index 3de7e34536eceb241de943908875a9c3e5e99f7e..10c3795839969bde6a747da67d5cc7caa44a7179 100644 +index c1af09d0a80ffba9b1265e59cf3d066ad2a5bfa0..fbfc0af8a6f29da9ee2d5fd6ae5a02958a7e3a81 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -2461,6 +2461,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( +@@ -2465,6 +2465,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( TRACE_EVENT2("navigation", "WebViewImpl::SetPageLifecycleStateInternal", "old_state", old_state, "new_state", new_state); @@ -130,7 +130,7 @@ index 3de7e34536eceb241de943908875a9c3e5e99f7e..10c3795839969bde6a747da67d5cc7ca bool storing_in_bfcache = new_state->is_in_back_forward_cache && !old_state->is_in_back_forward_cache; bool restoring_from_bfcache = !new_state->is_in_back_forward_cache && -@@ -3987,10 +3991,23 @@ PageScheduler* WebViewImpl::Scheduler() const { +@@ -3988,10 +3992,23 @@ PageScheduler* WebViewImpl::Scheduler() const { return GetPage()->GetPageScheduler(); } diff --git a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch index 2b6beb632e5b1..0fa0efdf1160d 100644 --- a/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch +++ b/patches/chromium/allow_in-process_windows_to_have_different_web_prefs.patch @@ -8,7 +8,7 @@ WebPreferences of in-process child windows, rather than relying on process-level command line switches, as before. diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc -index 2494aeb1cd363c07e1b662743b1453da646fe17d..927dce4724263b45ff27b046ef024ae2a04706a9 100644 +index 2612e54a2b0d3c71bd8efe9340bc58c795500140..e590387effc958ba7215e75ea1e2a8173f624d02 100644 --- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc +++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc @@ -148,6 +148,19 @@ bool StructTraitsaccelerated_video_decode_enabled = data.accelerated_video_decode_enabled(); diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h -index b7b817c01fcec0bf60bd88544ec66b53248a1ed8..82a03a14e19840924c90cb768a3bbd715fdf82d4 100644 +index 85f312a9ea1eca0ff7ba4c679fabb3156aabbc0b..a9dc007a93003610d68e3118b4a47a86d4e16f6e 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences.h +++ b/third_party/blink/public/common/web_preferences/web_preferences.h @@ -9,6 +9,7 @@ @@ -43,9 +43,9 @@ index b7b817c01fcec0bf60bd88544ec66b53248a1ed8..82a03a14e19840924c90cb768a3bbd71 #include "build/build_config.h" #include "net/nqe/effective_connection_type.h" #include "third_party/blink/public/common/common_export.h" -@@ -447,6 +448,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { - // when feature DynamicSafeAreaInsets is enabled. - bool dynamic_safe_area_insets_enabled = false; +@@ -451,6 +452,20 @@ struct BLINK_COMMON_EXPORT WebPreferences { + // WebView and by `kWebPayments` feature flag everywhere. + bool payment_request_enabled = false; + // Begin Electron-specific WebPreferences. + bool context_isolation = false; @@ -65,7 +65,7 @@ index b7b817c01fcec0bf60bd88544ec66b53248a1ed8..82a03a14e19840924c90cb768a3bbd71 // chrome, except for the cases where it would require lots of extra work for // the embedder to use the same default value. diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h -index d686fc396ce4d53c209be5cbb4d4ddfecc4cca1b..cd1110adc4131404ba32595ea5533b220554b0c4 100644 +index 9b7bc55a0e61de85688b2370012e14f8b47c43ab..63b035046ac3d565e5ee1f3a3c559da88d81f059 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h +++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h @@ -8,6 +8,7 @@ @@ -130,7 +130,7 @@ index d686fc396ce4d53c209be5cbb4d4ddfecc4cca1b..cd1110adc4131404ba32595ea5533b22 return r.cookie_enabled; } diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom -index 6678fc10d72e6c49ba729d5f44626f9a29934a3b..57f8c71bd0e3b1e09510184888636004a949c192 100644 +index e7c3bc263844c0ddeb9011440e2c0fd8f848ca27..142dae3834fe96f120727ef9ffe1250607bcadec 100644 --- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom +++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom @@ -8,9 +8,11 @@ import "third_party/blink/public/mojom/css/preferred_color_scheme.mojom"; diff --git a/patches/chromium/blink_local_frame.patch b/patches/chromium/blink_local_frame.patch index edfeb9615f758..da9ec05ec8e12 100644 --- a/patches/chromium/blink_local_frame.patch +++ b/patches/chromium/blink_local_frame.patch @@ -49,7 +49,7 @@ index 9dc450bc20744463c8898bc822a558be38486493..576421cb9600625ad8b9eda25cb99954 // its owning reference back to our owning LocalFrame. client_->Detached(type); diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index ba6aac34b4838efa86c01926759871a31a3b2257..99ae1320be10ac19d204edfe1c1cf4f52c62eb76 100644 +index 1a032c3a8eec55e98da745e7f82b311b1e0bd8ba..19efdf1f30eb5409f9d8a64f008891a2f4bda47c 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc @@ -748,10 +748,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { diff --git a/patches/chromium/boringssl_build_gn.patch b/patches/chromium/boringssl_build_gn.patch index 66ecb2fd08813..995fb72833e68 100644 --- a/patches/chromium/boringssl_build_gn.patch +++ b/patches/chromium/boringssl_build_gn.patch @@ -6,7 +6,7 @@ Subject: boringssl BUILD.gn Build BoringSSL with some extra functions that nodejs needs. diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn -index 12b2fb63dd3ff8c3d29d915a7d0f84a9d26c6e0a..c9fd2c069473b1c21ca8610afb246337c412e0be 100644 +index b962901a7db8e6e50155dabbb1371124b12f9b26..b01e197ae2e411a376ffa28b9058d62a16abf23b 100644 --- a/third_party/boringssl/BUILD.gn +++ b/third_party/boringssl/BUILD.gn @@ -48,6 +48,21 @@ all_sources = bcm_internal_headers + bcm_sources + crypto_internal_headers + diff --git a/patches/chromium/build_allow_electron_to_use_exec_script.patch b/patches/chromium/build_allow_electron_to_use_exec_script.patch index 0b9bf55adc86e..31c3790c85250 100644 --- a/patches/chromium/build_allow_electron_to_use_exec_script.patch +++ b/patches/chromium/build_allow_electron_to_use_exec_script.patch @@ -6,10 +6,10 @@ Subject: build: allow electron to use exec_script This is similar to the //build usecase so we're OK adding ourselves here diff --git a/.gn b/.gn -index 3f6571828197301361ebde2e19e8e3138597c276..9effa81a564c3d2afae3eb2bb7438635e45f124a 100644 +index 54d2631ec203207f44038a36439613709fec1669..c8b8f604f9e9d960e8144a93d958ab125aae96c5 100644 --- a/.gn +++ b/.gn -@@ -172,4 +172,26 @@ exec_script_allowlist = +@@ -173,4 +173,26 @@ exec_script_allowlist = "//tools/grit/grit_rule.gni", "//tools/gritsettings/BUILD.gn", diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index 3592273e6afc1..d424e470323e0 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -11,10 +11,10 @@ if we ever align our .pak file generation with Chrome we can remove this patch. diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index b38442f018b218944c7b85c9f8bd8b8eb6137b9e..dd15f6cf5dc40f2d54134c833d35508f2e22967b 100644 +index 95c73dcb082999d0a85d82f1fe330d27c88055ca..64cd976eba849435779b280b2941f9150b5d1d36 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -199,11 +199,16 @@ if (!is_android && !is_mac) { +@@ -200,11 +200,16 @@ if (!is_android && !is_mac) { "common/crash_keys.h", ] @@ -33,10 +33,10 @@ index b38442f018b218944c7b85c9f8bd8b8eb6137b9e..dd15f6cf5dc40f2d54134c833d35508f "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 0ad542897fa8e45003a7945d9393f84844f993d3..0fb8770c97c2a3c2ffebb558cd81081821747b2a 100644 +index fd02559a88b339ebd70e08d11d294975f1ef3148..c304e3d04364b071465a513a7953b3d1f0d3a38e 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4567,7 +4567,7 @@ static_library("browser") { +@@ -4591,7 +4591,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index 0ad542897fa8e45003a7945d9393f84844f993d3..0fb8770c97c2a3c2ffebb558cd810818 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index 1cc7b3905eae8d1c3025ae3454482ea8418a5217..bf71a7514f337cc6447e27a8ebd3a2c299840121 100644 +index 48488b2b31ff39e3fe822aa388e1c89578eb2575..9ba26245243d37683f3161c89411cacdcfaeceb7 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7031,9 +7031,12 @@ test("unit_tests") { +@@ -7086,9 +7086,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index 1cc7b3905eae8d1c3025ae3454482ea8418a5217..bf71a7514f337cc6447e27a8ebd3a2c2 "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -7996,6 +7999,10 @@ test("unit_tests") { +@@ -8054,6 +8057,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index 1cc7b3905eae8d1c3025ae3454482ea8418a5217..bf71a7514f337cc6447e27a8ebd3a2c2 sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8051,7 +8058,6 @@ test("unit_tests") { +@@ -8109,7 +8116,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/build_gn.patch b/patches/chromium/build_gn.patch index a8999358f9f38..1ca25368c1572 100644 --- a/patches/chromium/build_gn.patch +++ b/patches/chromium/build_gn.patch @@ -7,7 +7,7 @@ These are variables we add to the root BUILDCONFIG so that they're available everywhere, without having to import("//electron/.../flags.gni"). diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn -index bdf8b13631a3cdf698078f70c435e9316cf0bfc3..53df468e94bdd2cdf049604e29e55863efdd3a8d 100644 +index a9d2caeb1c92e102daa9a8bd5bdc5a55af418cf4..efb5bf91722d4b18ae704ebf7355a13f253bef20 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -123,6 +123,9 @@ if (current_os == "") { diff --git a/patches/chromium/build_libc_as_static_library.patch b/patches/chromium/build_libc_as_static_library.patch index fbcf8c5e08ba0..f22d990128a0e 100644 --- a/patches/chromium/build_libc_as_static_library.patch +++ b/patches/chromium/build_libc_as_static_library.patch @@ -7,10 +7,10 @@ Build libc++ as static library to compile and pass nan tests diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn -index 3f437d2b9c193285bdc200c2781b13de75ba993d..d9493066ddc7d7198acc76f345c726be021d8fce 100644 +index fd41146966f8cd559e99eb58c0831acdce9fbc6a..acc60f67732179ac6ed7161478686b6e3778731c 100644 --- a/buildtools/third_party/libc++/BUILD.gn +++ b/buildtools/third_party/libc++/BUILD.gn -@@ -204,7 +204,11 @@ libcxx_modules("std_wctype_h") { +@@ -265,7 +265,11 @@ libcxx_modules("std_wctype_h") { if (libcxx_is_shared) { _libcxx_target_type = "shared_library" } else { @@ -23,7 +23,7 @@ index 3f437d2b9c193285bdc200c2781b13de75ba993d..d9493066ddc7d7198acc76f345c726be } target(_libcxx_target_type, "libc++") { -@@ -213,6 +217,7 @@ target(_libcxx_target_type, "libc++") { +@@ -274,6 +278,7 @@ target(_libcxx_target_type, "libc++") { # need to explicitly depend on libc++. visibility = [ "//build/config:common_deps", diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index b5cb0276c27fd..9efd3bd665c18 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 447c0ae162f45eb157008e62cbc756d6f23e4920..27db5dbfd05106788feda9daab9d1aaaf1ae920f 100644 +index 9e95917b0808ddcb6e0b1359fb5a86a516e4a071..ea6ba910234659d1213b1f3f624da0f49b851725 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9635,6 +9635,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9638,6 +9638,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,10 +21,10 @@ index 447c0ae162f45eb157008e62cbc756d6f23e4920..27db5dbfd05106788feda9daab9d1aaa &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index c53ab4b96a16b7326dfde14452b20d2170aecf04..e500fe77f55836b3e5832536f98cf9581984b8c1 100644 +index 9c0ef713978f40d8ddda6772004fc81f37eafb6f..d7bf74c206cedc628d0b92fff25c29393f6c9d95 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5061,6 +5061,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5087,6 +5087,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( SetPartitionedPopinOpenerOnNewWindowIfNeeded(new_contents_impl, params, opener); @@ -37,7 +37,7 @@ index c53ab4b96a16b7326dfde14452b20d2170aecf04..e500fe77f55836b3e5832536f98cf958 // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses -@@ -5102,12 +5108,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5128,12 +5134,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( AddWebContentsDestructionObserver(new_contents_impl); } @@ -51,10 +51,10 @@ index c53ab4b96a16b7326dfde14452b20d2170aecf04..e500fe77f55836b3e5832536f98cf958 new_contents_impl, opener, params.target_url, params.referrer.To(), params.disposition, diff --git a/content/common/frame.mojom b/content/common/frame.mojom -index 8f8f79733c956fed2469e51993bad29689c11d8a..17e953f46d479f431fa06d28857901cb844ff4ea 100644 +index 55bb4ae3bab4cdf20b3e1dde9450a5c0e4e62b37..fe444c7fa140166a1b65c7a8a2676e2de7c4e0fc 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom -@@ -642,6 +642,10 @@ struct CreateNewWindowParams { +@@ -646,6 +646,10 @@ struct CreateNewWindowParams { pending_associated_remote widget; pending_associated_receiver frame_widget_host; pending_associated_remote frame_widget; @@ -66,10 +66,10 @@ index 8f8f79733c956fed2469e51993bad29689c11d8a..17e953f46d479f431fa06d28857901cb // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index f5b028759f86f382230867e6abf72a82051c02c3..f1110385258f057be3b456198b46e4d2c54ca718 100644 +index c5b548159e64ef3b8d27e1d7e8147289064f6e8a..1c4dd9d0c16751f34d59088e68e23317b8d25437 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc -@@ -815,6 +815,8 @@ bool ContentBrowserClient::CanCreateWindow( +@@ -820,6 +820,8 @@ bool ContentBrowserClient::CanCreateWindow( const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -79,7 +79,7 @@ index f5b028759f86f382230867e6abf72a82051c02c3..f1110385258f057be3b456198b46e4d2 bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 1c7e6dc3b867c0e598f8517591ffb9aa8007bfea..7459b083156d1f6bc01198690c4c9ec02d88d862 100644 +index 921a6a8f7ecbdd2e3fa8a9888e4a34f1b0cf7e09..7cfc3331a004fd52d9863a097271f4d892480933 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -198,6 +198,7 @@ class NetworkService; @@ -90,7 +90,7 @@ index 1c7e6dc3b867c0e598f8517591ffb9aa8007bfea..7459b083156d1f6bc01198690c4c9ec0 } // namespace network namespace sandbox { -@@ -1368,6 +1369,8 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -1372,6 +1373,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -100,7 +100,7 @@ index 1c7e6dc3b867c0e598f8517591ffb9aa8007bfea..7459b083156d1f6bc01198690c4c9ec0 bool opener_suppressed, bool* no_javascript_access); diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc -index ac4deeb6acebf79987591756320e2406d74d7a1e..f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8 100644 +index b390356721fa226c348923f33601c4a1a2d9702d..97a3ea6f292563a41fd41f812ac72526a96d8471 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -32,6 +32,17 @@ namespace content { @@ -122,7 +122,7 @@ index ac4deeb6acebf79987591756320e2406d74d7a1e..f688e6d1e6c11f0dd80f498e6361fa82 WebContents* source, const OpenURLParams& params, diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h -index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1c6b6a128 100644 +index da319cb20733150366d85bee95609f0f2d9def7f..8a18958035cc1dd26be558349f64f7727570c4ba 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h @@ -18,6 +18,7 @@ @@ -133,7 +133,7 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 #include "content/public/browser/eye_dropper.h" #include "content/public/browser/fullscreen_types.h" #include "content/public/browser/invalidate_type.h" -@@ -375,6 +376,13 @@ class CONTENT_EXPORT WebContentsDelegate { +@@ -376,6 +377,13 @@ class CONTENT_EXPORT WebContentsDelegate { const StoragePartitionConfig& partition_config, SessionStorageNamespace* session_storage_namespace); @@ -148,10 +148,10 @@ index ac2e7cdceb13ce07966a908fab3ff8feff969484..96cb58b1a88499cf8f78d748dc5a1cc1 // typically happens when popups are created. virtual void WebContentsCreated(WebContents* source_contents, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 0e2524de1159d7e2628c66d188002c6a417bfa52..0fb92081a1bbfb14c0ddd74dfe91f94bb2be1d2a 100644 +index 0755a49ee7558ee0c5e76f602c514bb8a7c3a019..4185aa3e07df8f2a3061d18e87f39cd5d79baead 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -6924,6 +6924,10 @@ WebView* RenderFrameImpl::CreateNewWindow( +@@ -6927,6 +6927,10 @@ WebView* RenderFrameImpl::CreateNewWindow( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); @@ -210,10 +210,10 @@ index 82e9d3dfb5f7da76d89fe15ae61d379fa46e177d..fd035512099a54dff6cc951a2226c23a } // namespace blink diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc -index 70d107d7c99056e790d75755855b804ed1961a90..a0ed0e7cd27532dcf2c327874ae4573c70290ecd 100644 +index f4a1ed78679c5efaea6494a4aa8b22fff31cba1b..afa637807b0a5bdef126031f219115ecee8d478d 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc -@@ -2270,6 +2270,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, +@@ -2272,6 +2272,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, WebWindowFeatures window_features = GetWindowFeaturesFromString(features, entered_window); diff --git a/patches/chromium/chore_introduce_blocking_api_for_electron.patch b/patches/chromium/chore_introduce_blocking_api_for_electron.patch index adb711334e88f..b2d277ea348e6 100644 --- a/patches/chromium/chore_introduce_blocking_api_for_electron.patch +++ b/patches/chromium/chore_introduce_blocking_api_for_electron.patch @@ -7,7 +7,7 @@ This patch comes after Chromium removed the ScopedAllowIO API in favor of explicitly adding ScopedAllowBlocking calls as friends. diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h -index 075e8173bcf1cfe420b87afe216bb06459a0d8bc..a300ed1e8c226f43fbfe6d82f72f9fb741fa342e 100644 +index d97240a7b0dcf63737808850b6ef83852155b794..4a58b1f3e8c2bee32084be82977239e911598077 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -132,6 +132,7 @@ class KeyStorageLinux; @@ -28,7 +28,7 @@ index 075e8173bcf1cfe420b87afe216bb06459a0d8bc..a300ed1e8c226f43fbfe6d82f72f9fb7 namespace enterprise_connectors { class LinuxKeyRotationCommand; } // namespace enterprise_connectors -@@ -574,6 +578,7 @@ class BASE_EXPORT ScopedAllowBlocking { +@@ -575,6 +579,7 @@ class BASE_EXPORT ScopedAllowBlocking { friend class ::DesktopNotificationBalloon; friend class ::FirefoxProfileLock; friend class ::GaiaConfig; @@ -36,7 +36,7 @@ index 075e8173bcf1cfe420b87afe216bb06459a0d8bc..a300ed1e8c226f43fbfe6d82f72f9fb7 friend class ::ProfileImpl; friend class ::ScopedAllowBlockingForProfile; friend class ::StartupTabProviderImpl; -@@ -613,6 +618,7 @@ class BASE_EXPORT ScopedAllowBlocking { +@@ -614,6 +619,7 @@ class BASE_EXPORT ScopedAllowBlocking { friend class crypto::ScopedAllowBlockingForNSS; // http://crbug.com/59847 friend class drive::FakeDriveService; friend class extensions::DesktopAndroidExtensionSystem; diff --git a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch index 16f983f707470..7c3a128ce7dc5 100644 --- a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch +++ b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch @@ -34,10 +34,10 @@ index 39b5a8fdd165efd74b00256552b51b5413107958..bfc4ef4f50efff4a77f2aef64335bb7e class ScrollEvent; 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 55c426aee12da4d4d1f62dc7d489133e8d21dc49..40c88bf14d2d4fe841e29b32414361688ae438e5 100644 +index 19826b92b39c812a3170bfa470a08192b2a712bc..d97262cc28622de335bfe5a82c26779e9dccd586 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 -@@ -1362,6 +1362,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( +@@ -1387,6 +1387,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( window()->SetProperty(aura::client::kHeadlessBoundsKey, bounds); } @@ -49,10 +49,10 @@ index 55c426aee12da4d4d1f62dc7d489133e8d21dc49..40c88bf14d2d4fe841e29b3241436168 DesktopWindowTreeHostWin::GetSingletonDesktopNativeCursorManager() { return new DesktopNativeCursorManagerWin(); 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 4865d79c95c34d8cead96d3bb8063a0e2bd6076b..ebfa09ed15dca98b75a013e3dcbb566ce84d7cb7 100644 +index 2ee5e4b4673f4f18880dddecc48118c89823fd3f..37109b8d3d439073b5c9e2ea3597c36f32de5704 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 -@@ -267,6 +267,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, +@@ -268,6 +268,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, void HandleWindowSizeUnchanged() override; void HandleWindowScaleFactorChanged(float window_scale_factor) override; void HandleHeadlessWindowBoundsChanged(const gfx::Rect& bounds) override; @@ -61,10 +61,10 @@ index 4865d79c95c34d8cead96d3bb8063a0e2bd6076b..ebfa09ed15dca98b75a013e3dcbb566c // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 3a60e310d1c4048f0e37e085c97b8dfc093aefda..8fe48c9bef144218e34434d563883b15733d03bc 100644 +index 1cf98eadc8cbc1ada481c709a873dc1dd443de66..cec234006cbcacff953ce9ff4175006b057aa341 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3156,15 +3156,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3177,15 +3177,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } // We must let Windows handle the caption buttons if it's drawing them, or // they won't work. @@ -86,7 +86,7 @@ index 3a60e310d1c4048f0e37e085c97b8dfc093aefda..8fe48c9bef144218e34434d563883b15 return 0; } } -@@ -3187,6 +3191,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3208,6 +3212,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, // handle alt-space, or in the frame itself. is_right_mouse_pressed_on_caption_ = false; ReleaseCapture(); @@ -94,7 +94,7 @@ index 3a60e310d1c4048f0e37e085c97b8dfc093aefda..8fe48c9bef144218e34434d563883b15 // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() // expect screen coordinates. POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); -@@ -3194,7 +3199,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3215,7 +3220,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, w_param = static_cast(SendMessage( hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); if (w_param == HTCAPTION || w_param == HTSYSMENU) { @@ -114,7 +114,7 @@ index 3a60e310d1c4048f0e37e085c97b8dfc093aefda..8fe48c9bef144218e34434d563883b15 } } else if (message == WM_NCLBUTTONDOWN && diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h -index de8fd5657e6885f74a5970bdd49647a6f1616387..4af87792edc7a147468077b834582510550e35e6 100644 +index 83c26bf2b4bc11c0e3d839093eea56eed1bf581b..075e456e851bbde2b0174ca0cc34428cc32b6966 100644 --- a/ui/views/win/hwnd_message_handler_delegate.h +++ b/ui/views/win/hwnd_message_handler_delegate.h @@ -255,6 +255,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index 801c594f350aa..af244fb88baf1 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,10 +14,10 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 6010d9f9fc9bfeffb3e5a64de7352b52a202cbf7..4325cdfd256ae7a1008e073d42da995b82df5bba 100644 +index 51355738262d80afaf1f319b5d90c8a74d435ffd..2a17aa6a3f687d60a7ca0e839e59f637819a9376 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4980,7 +4980,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5006,7 +5006,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( : IsGuest(); // While some guest types do not have a guest SiteInstance, the ones that // don't all override WebContents creation above. diff --git a/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch b/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch index 46f3ddb405d00..4a93ef5d28865 100644 --- a/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch +++ b/patches/chromium/chore_patch_out_profile_methods_in_chrome_browser_pdf.patch @@ -9,7 +9,7 @@ Electron does not support Profiles, so this Profile::FromBrowserContext() call is not needed and will not link. This change patches it out. diff --git a/chrome/browser/pdf/chrome_pdf_stream_delegate.cc b/chrome/browser/pdf/chrome_pdf_stream_delegate.cc -index 9774ed3e55c0d78f21bb0cb0b2607cc07eaf78e0..f58c3946130fa469f2775d8bae773e5bbfc71a96 100644 +index e3b9f14a4cf2167064ce6716053e663adffa1542..65f13a4607c8145858fd47d81cb9960c44272d79 100644 --- a/chrome/browser/pdf/chrome_pdf_stream_delegate.cc +++ b/chrome/browser/pdf/chrome_pdf_stream_delegate.cc @@ -45,6 +45,7 @@ namespace { diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index c390777f3f0d4..6dbe4f0b55e6a 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -80,10 +80,10 @@ index 4fd8dff1089cd6afa6a66dc185734d7671657281..0a1f4268ea771a3d5d4a2668928c6e5d content::WebContents* source, const content::OpenURLParams& params, diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc -index 8232c0081baaeb3df9f2f6224f8fe31a2dcb1d5b..081c01bc00dfdef66c23a9ef640ed159f4f7557b 100644 +index 9ccf763fcbc717480c4dfce735f273dfdbf5cd4a..381cda59465f34a46f55d03ef6ccd53fadec6e6e 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc -@@ -2264,12 +2264,11 @@ bool Browser::IsWebContentsCreationOverridden( +@@ -2267,12 +2267,11 @@ bool Browser::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -99,10 +99,10 @@ index 8232c0081baaeb3df9f2f6224f8fe31a2dcb1d5b..081c01bc00dfdef66c23a9ef640ed159 WebContents* Browser::CreateCustomWebContents( diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h -index 7a61c64db93db7f3b31b8d39897257a6315383c2..7db684020fbaf7061ed61a8b1cefba8c757f2fe5 100644 +index 288d4f6d7dfdcf7e518f78990f061f4215f1b975..3d8899e313906a0d3a273d43cab872243fa6faea 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h -@@ -981,8 +981,7 @@ class Browser : public TabStripModelObserver, +@@ -983,8 +983,7 @@ class Browser : public TabStripModelObserver, content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -218,10 +218,10 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 952fd20f71560acd89c74f08e9d8cdbf34fb5a1a..59b05937ceb1b81b69d913f587150021b9031106 100644 +index 30593ec9ecd2b135a6054ed50e95c849c09b47bd..b6870d74a761a847e9952d409b37ddb778790b08 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4943,8 +4943,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4969,8 +4969,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( // TODO(crbug.com/40202416): Support a way for MPArch guests to support this. if (delegate_ && delegate_->IsWebContentsCreationOverridden( source_site_instance, params.window_container_type, @@ -232,7 +232,7 @@ index 952fd20f71560acd89c74f08e9d8cdbf34fb5a1a..59b05937ceb1b81b69d913f587150021 static_cast(delegate_->CreateCustomWebContents( opener, source_site_instance, is_new_browsing_instance, diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc -index f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8..dc47ebfd61f16901b7f8e0e4c30c79eaf5d0710e 100644 +index 97a3ea6f292563a41fd41f812ac72526a96d8471..b299dd5659100d317a3574e902bf2c29c5defd2c 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -159,8 +159,7 @@ bool WebContentsDelegate::IsWebContentsCreationOverridden( @@ -246,10 +246,10 @@ index f688e6d1e6c11f0dd80f498e6361fa822e8c2eb8..dc47ebfd61f16901b7f8e0e4c30c79ea } diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h -index 96cb58b1a88499cf8f78d748dc5a1cc1c6b6a128..8ff56b56da764c8af14c40babc529ee4cb089c7e 100644 +index 8a18958035cc1dd26be558349f64f7727570c4ba..7d9c9b06bcc57ef5eb0a2ca74ee20632a1393f9e 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h -@@ -354,8 +354,7 @@ class CONTENT_EXPORT WebContentsDelegate { +@@ -355,8 +355,7 @@ class CONTENT_EXPORT WebContentsDelegate { SiteInstance* source_site_instance, mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -324,7 +324,7 @@ index e39031afd8fff7cb6e278555cc58a48d86407d65..f67f6a5603c1fa9e66ccdde9b601df9a content::RenderFrameHost* opener, content::SiteInstance* source_site_instance, diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc -index 16babdc7affdaef1af24850cf35494b8b4fe1479..1e77b091d94457c892788f7a441a83e7a96a49cc 100644 +index 60de0d74ee40fedcbae96e5049e21dc238bf33bf..c568d4d08f772e1d381820bed826a0b64a631449 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc @@ -452,8 +452,7 @@ bool MimeHandlerViewGuest::IsWebContentsCreationOverridden( @@ -380,7 +380,7 @@ index 1012a909ef1fcae51c218ae519fe7e0db65ab087..127b1ae940bc9313aecb635e2b01bb6f int opener_render_process_id, int opener_render_frame_id, diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc -index 62be493c74423875af45a92c98b2ffceb928189f..ec82d8e47bf0a33f832b8ca242f395fe59a1bbee 100644 +index 7ca1e83ba1fd2dc5ea7c7ce644c3b7c54b9999f9..c1639653714d6973bcb5a0b37cb7028db8406742 100644 --- a/headless/lib/browser/headless_web_contents_impl.cc +++ b/headless/lib/browser/headless_web_contents_impl.cc @@ -206,8 +206,7 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate { diff --git a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch index 553772fa6b448..3b9e069bb1261 100644 --- a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch +++ b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch @@ -7,10 +7,10 @@ By default, chromium sets up one v8 snapshot to be used in all v8 contexts. This to have a dedicated browser process v8 snapshot defined by the file `browser_v8_context_snapshot.bin`. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abcfc3c1a61 100644 +index 2474e8c608956bcf50e0c7204d728854fe498d31..32f3a69ea003d7c6a1f6a15622ff2acb9b5a063b 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -271,8 +271,13 @@ void AsanProcessInfoCB(const char*, bool*) { +@@ -272,8 +272,13 @@ void AsanProcessInfoCB(const char*, bool*) { } #endif // defined(ADDRESS_SANITIZER) @@ -25,7 +25,7 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) base::FileDescriptorStore& file_descriptor_store = base::FileDescriptorStore::GetInstance(); -@@ -301,11 +306,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, +@@ -302,11 +307,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, #endif // V8_USE_EXTERNAL_STARTUP_DATA @@ -40,7 +40,7 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc #endif // V8_USE_EXTERNAL_STARTUP_DATA } -@@ -978,7 +984,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { +@@ -979,7 +985,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { return TerminateForFatalInitializationError(); #endif // BUILDFLAG(IS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) @@ -50,19 +50,18 @@ index 054f38d683280638c7ac618d2ff8f7aef1a0def0..6e9d8d9a043cf8b67e26f70b3a904abc blink::TrialTokenValidator::SetOriginTrialPolicyGetter( base::BindRepeating([]() -> blink::OriginTrialPolicy* { diff --git a/content/public/app/content_main_delegate.cc b/content/public/app/content_main_delegate.cc -index 8194fc8b036482eedb162ff92bb82165cdf3c8d0..f038620516d7783170bc82b3b14dde4e01f3975d 100644 +index 8b02f553e2fc29da88c3e14c05a7ee82210eab51..14f2e66d5ecda6e860724a3ab946eaaffba33d6d 100644 --- a/content/public/app/content_main_delegate.cc +++ b/content/public/app/content_main_delegate.cc -@@ -4,6 +4,8 @@ - +@@ -5,6 +5,7 @@ #include "content/public/app/content_main_delegate.h" + #include +#include -+ + #include "base/check.h" #include "base/notreached.h" - #include "build/build_config.h" -@@ -72,6 +74,10 @@ std::optional ContentMainDelegate::PostEarlyInitialization( +@@ -74,6 +75,10 @@ std::optional ContentMainDelegate::PostEarlyInitialization( return std::nullopt; } @@ -74,13 +73,13 @@ index 8194fc8b036482eedb162ff92bb82165cdf3c8d0..f038620516d7783170bc82b3b14dde4e return new ContentClient(); } diff --git a/content/public/app/content_main_delegate.h b/content/public/app/content_main_delegate.h -index 801bfd401ea4a8e72417d88efaa718cc6fb60883..663fec68d0c2855cdf83bb259b85c22910a67464 100644 +index db611d99a6c0f18f39967b38791822fda7d175b5..cc150475de655d5ef20a107ae3ef80c08af8c7fb 100644 --- a/content/public/app/content_main_delegate.h +++ b/content/public/app/content_main_delegate.h -@@ -8,6 +8,7 @@ - #include +@@ -9,6 +9,7 @@ #include #include + #include +#include #include @@ -95,10 +94,10 @@ index 801bfd401ea4a8e72417d88efaa718cc6fb60883..663fec68d0c2855cdf83bb259b85c229 friend class ContentClientCreator; friend class ContentClientInitializer; diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index 578e200a88d89b356e991b3317ff1e71f25ff75e..ae49b7b5e830a7127812219df1c8888b7ba4b348 100644 +index 11cafc3e1588cce52b76cc2f09f66b3e451fb087..e07bdaeccecc8015462e35d5cf4606335e2e962c 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -672,8 +672,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, +@@ -660,8 +660,7 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** snapshot_data_out, #if defined(V8_USE_EXTERNAL_STARTUP_DATA) @@ -108,7 +107,7 @@ index 578e200a88d89b356e991b3317ff1e71f25ff75e..ae49b7b5e830a7127812219df1c8888b if (g_mapped_snapshot) { // TODO(crbug.com/40558459): Confirm not loading different type of snapshot // files in a process. -@@ -682,10 +681,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { +@@ -670,10 +669,17 @@ void V8Initializer::LoadV8Snapshot(V8SnapshotFileType snapshot_file_type) { base::MemoryMappedFile::Region file_region; base::File file = diff --git a/patches/chromium/disable_compositor_recycling.patch b/patches/chromium/disable_compositor_recycling.patch index afb2922976a33..6178e0cd02e5d 100644 --- a/patches/chromium/disable_compositor_recycling.patch +++ b/patches/chromium/disable_compositor_recycling.patch @@ -6,10 +6,10 @@ Subject: fix: disabling compositor recycling Compositor recycling is useful for Chrome because there can be many tabs and spinning up a compositor for each one would be costly. In practice, Chrome uses the parent compositor code path of browser_compositor_view_mac.mm; the NSView of each tab is detached when it's hidden and attached when it's shown. For Electron, there is no parent compositor, so we're forced into the "own compositor" code path, which seems to be non-optimal and pretty ruthless in terms of the release of resources. Electron has no real concept of multiple tabs per window, so it should be okay to disable this ruthless recycling altogether in Electron. diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index 71db145609652dbbe733dbc96d2630b686f6c8c9..6aa0fd5422ee0057acc3e5c423ca2847a4ebb17e 100644 +index 1a726fb4a9e8173dc7da6901eb1632c7f326358e..ae8a4117db2f6bac1d62981e7c0e35cf1eee68da 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm -@@ -560,7 +560,11 @@ +@@ -561,7 +561,11 @@ return; host()->WasHidden(); diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index f62bee289327c..13007af26e7fe 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -6,7 +6,7 @@ Subject: disable_hidden.patch Electron uses this to disable background throttling for hidden windows. diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 22ffafd8f5e0f49d5c9df7ecd3f61dad3dfd2814..a409c64768968c81be3b6c7e7646f8df26e2afe6 100644 +index 573269ba54150d5350e5c3589217c5e7f41d560d..20fcda4eb20459b69247003c51c2a3ed37c7b1e8 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -832,6 +832,10 @@ void RenderWidgetHostImpl::WasHidden() { @@ -35,7 +35,7 @@ index c201cff9e5c3b286389a5eb74e1a9ebd86edfef9..949f6a7867758e35c24897add9a4d47a // |routing_id| must not be MSG_ROUTING_NONE. // If this object outlives |delegate|, DetachDelegate() must be called when diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 70f368ab0c59e7f495cb13094160d72f601e0938..19d2df47b0c929558ac8cfc208742dfef463a68b 100644 +index 5867fc3e77326991f30d835d08d3cfafe2b6687c..d719b546b8c3c59003698b26dead065da7d76341 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -643,7 +643,7 @@ void RenderWidgetHostViewAura::HideImpl() { diff --git a/patches/chromium/enable_reset_aspect_ratio.patch b/patches/chromium/enable_reset_aspect_ratio.patch index 0a0ec7c802f06..409ced0aa6c13 100644 --- a/patches/chromium/enable_reset_aspect_ratio.patch +++ b/patches/chromium/enable_reset_aspect_ratio.patch @@ -6,7 +6,7 @@ Subject: feat: enable setting aspect ratio to 0 Make SetAspectRatio accept 0 as valid input, which would reset to null. 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 24f63e82a1a170b392bdc8e868729ddd5f9238fa..55c426aee12da4d4d1f62dc7d489133e8d21dc49 100644 +index 65e4957aa08923200923f91cadcf47eb403bb8a8..19826b92b39c812a3170bfa470a08192b2a712bc 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 @@ -637,7 +637,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { @@ -19,10 +19,10 @@ index 24f63e82a1a170b392bdc8e868729ddd5f9238fa..55c426aee12da4d4d1f62dc7d489133e excluded_margin); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 8d5002fab43ccfcaccdb044fc9b2a95748e71b75..adade58a533c373087d8c51a5744e8f118ba6e9d 100644 +index a0e6a63f7b2da396ba5d300a9eb6ab94825f677a..539f5920661010139dc69afb3ff7dd4c9bae762a 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -997,8 +997,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, +@@ -1008,8 +1008,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, void HWNDMessageHandler::SetAspectRatio(float aspect_ratio, const gfx::Size& excluded_margin) { diff --git a/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch b/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch index d0e1c7f421fc1..9d478cd3aeddc 100644 --- a/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch +++ b/patches/chromium/export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch @@ -9,7 +9,7 @@ correctly tagged with MAP_JIT we need to use gins page allocator instead of the default V8 allocator. This probably can't be usptreamed. diff --git a/gin/public/v8_platform.h b/gin/public/v8_platform.h -index c34f34146d0aab681318b64ac33081bdc21ce7d6..11cdf6c34d8f79fcf293f59785e586dc8363b983 100644 +index 9dac402705385087ced2df2db757a07246984a94..cb49b4f085026658e920699ed285d524119d6aad 100644 --- a/gin/public/v8_platform.h +++ b/gin/public/v8_platform.h @@ -32,6 +32,7 @@ class GIN_EXPORT V8Platform : public v8::Platform { @@ -21,10 +21,10 @@ index c34f34146d0aab681318b64ac33081bdc21ce7d6..11cdf6c34d8f79fcf293f59785e586dc ThreadIsolatedAllocator* GetThreadIsolatedAllocator() override; #endif diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc -index fa799a4cc32950ce03c154bc7791341d96a17f67..b8ec81b79b6129a7e26a2ffecf535fdedea8ed6c 100644 +index 698b29e9c31a2695cac30bf85c97a216ff8a6257..dbc38d0d87803496ce122da62e639f2a0334e0f6 100644 --- a/gin/v8_platform.cc +++ b/gin/v8_platform.cc -@@ -204,6 +204,10 @@ ThreadIsolatedAllocator* V8Platform::GetThreadIsolatedAllocator() { +@@ -205,6 +205,10 @@ ThreadIsolatedAllocator* V8Platform::GetThreadIsolatedAllocator() { } #endif // PA_BUILDFLAG(ENABLE_THREAD_ISOLATION) diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index 0b079b34898dc..64a0872ed743c 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,7 +33,7 @@ index 0ab8187b0db8ae6db46d81738f653a2bc4c566f6..de3d55e85c22317f7f9375eb94d0d5d4 } // namespace net diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index b1df0a09a9bfd226ffe7b37144e6599b099e619e..f8c7bc591552fea25b4a8e0edd7a3823b00e6b06 100644 +index ee56aab6831be256390b88e65a2a949c591bbd31..60c6977d2ffd0da0286e25e5645c11c19d6ab376 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -1814,6 +1814,13 @@ void NetworkContext::SetNetworkConditions( @@ -63,10 +63,10 @@ index 930e0bd987c48d111b2c8d71147c09e4418bda6c..9373a53c5cac879c689fcea77f1dbbb3 void SetEnableReferrers(bool enable_referrers) override; #if BUILDFLAG(IS_CT_SUPPORTED) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index cf4d2dcf86b6536c37d46875f74517e478b34928..b19976926137cae56094ec6e292a5014c2fd546b 100644 +index 6d79f4a4cb1a40be907ef52e35f0e5a0acab31fb..95ff46a7ca2654166d18027b986f6ec6c2a3d361 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -1267,6 +1267,9 @@ interface NetworkContext { +@@ -1275,6 +1275,9 @@ interface NetworkContext { SetNetworkConditions(mojo_base.mojom.UnguessableToken throttling_profile_id, NetworkConditions? conditions); diff --git a/patches/chromium/extend_apply_webpreferences.patch b/patches/chromium/extend_apply_webpreferences.patch index 75db37c172f62..278fb134b48f2 100644 --- a/patches/chromium/extend_apply_webpreferences.patch +++ b/patches/chromium/extend_apply_webpreferences.patch @@ -12,10 +12,10 @@ Ideally we could add an embedder observer pattern here but that can be done in future work. diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index 10c3795839969bde6a747da67d5cc7caa44a7179..9810991e0a5d8b931a70e056b6651b8e5fdb9881 100644 +index fbfc0af8a6f29da9ee2d5fd6ae5a02958a7e3a81..cf80d64963a2a25afbaa6060b4dfda425638858b 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -169,6 +169,7 @@ +@@ -170,6 +170,7 @@ #include "third_party/blink/renderer/core/view_transition/view_transition_supplement.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" #include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h" @@ -23,7 +23,7 @@ index 10c3795839969bde6a747da67d5cc7caa44a7179..9810991e0a5d8b931a70e056b6651b8e #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" -@@ -1859,6 +1860,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, +@@ -1860,6 +1861,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, #if BUILDFLAG(IS_MAC) web_view_impl->SetMaximumLegibleScale( prefs.default_maximum_page_scale_factor); diff --git a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch index f6f5a58425702..5ca5f6536b336 100644 --- a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch +++ b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch @@ -13,7 +13,7 @@ uses internally for things like menus and devtools. We can remove this patch once it has in some shape been upstreamed. diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc -index 1ebd8809abb3208d402b6bd728114f7fea3ac52c..a564078fd9fe0c605a0dcb9eb21beabda389771f 100644 +index 3f8801c6042ef2b20635838e4d4c572d89b94601..055b15ff72844cd54c60cc92042a5465ce2870ad 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc @@ -210,6 +210,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors, @@ -26,7 +26,7 @@ index 1ebd8809abb3208d402b6bd728114f7fea3ac52c..a564078fd9fe0c605a0dcb9eb21beabd } diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h -index 2e657f24bb625c7a7af14686553aebdc06ad8eda..6f2384338ac4a48a78bc8aac8b4bb9d330d686b3 100644 +index 97cceb20da0fccf0603cf2d4b1c8985314b1e850..0632d7fdc975dfdc3cc24bd8e641a2680fabc45d 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h @@ -458,6 +458,23 @@ class COMPONENT_EXPORT(NATIVE_THEME) NativeTheme { @@ -62,10 +62,10 @@ index 2e657f24bb625c7a7af14686553aebdc06ad8eda..6f2384338ac4a48a78bc8aac8b4bb9d3 SEQUENCE_CHECKER(sequence_checker_); }; diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index ff43747a45b5f508f45afb1e6304bda22ae46fbc..955c3734591f608f32b40927e53db1bf453ce907 100644 +index dcf2d14d92f8b5a5dd7bf22f8ce00f030824c989..04d0f5d9416eaba569f49a25e7e8c1a89e50ecfe 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc -@@ -695,6 +695,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { +@@ -696,6 +696,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { if (InForcedColorsMode() && !IsForcedDarkMode()) { return false; } diff --git a/patches/chromium/feat_configure_launch_options_for_service_process.patch b/patches/chromium/feat_configure_launch_options_for_service_process.patch index 2ce4043d9ee56..bafd2c57636be 100644 --- a/patches/chromium/feat_configure_launch_options_for_service_process.patch +++ b/patches/chromium/feat_configure_launch_options_for_service_process.patch @@ -19,7 +19,7 @@ to STDOUT_FILENO/STD_OUTPUT_HANDLE and STDERR_FILENO/STD_ERROR_HANDLE allowing t parent process to read from the pipe. diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h -index 6a3645f020d7348f758ebcc93c49ddcbee709937..55994606517a4f52475610db2da26b5560865fab 100644 +index 32b0b480d2195e5b166a473c2b58c960a0b5d052..cbf593e5405b384dbbdf763ebc4319cf25fc5c4a 100644 --- a/content/browser/child_process_launcher.h +++ b/content/browser/child_process_launcher.h @@ -33,6 +33,7 @@ @@ -43,7 +43,7 @@ index 6a3645f020d7348f758ebcc93c49ddcbee709937..55994606517a4f52475610db2da26b55 // in the child process. If a FilePath is provided, the file will be opened // and the descriptor cached for future process launches. If a ScopedFD is @@ -211,6 +215,15 @@ struct ChildProcessLauncherFileData { - std::map> + std::map> files_to_preload; #endif + @@ -187,10 +187,10 @@ index 96c9563aac5847e742de5d9c9236f78bcb6cfd9c..73c9d585579ad5bdc407687b8becd0b7 host->GetChildProcess()->BindServiceInterface(std::move(receiver)); } diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8f137e7b9 100644 +index 8bb497717298886bc6db04ebcba9be1f3b29ab73..60c4a301c32a46cdc332cccd1cd4ae78802ee8b9 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc -@@ -189,11 +189,13 @@ const ChildProcessData& UtilityProcessHost::GetData() { +@@ -190,11 +190,13 @@ const ChildProcessData& UtilityProcessHost::GetData() { return process_->GetData(); } @@ -206,7 +206,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 bool UtilityProcessHost::Start() { return StartProcess(); -@@ -240,6 +242,30 @@ void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) { +@@ -241,6 +243,30 @@ void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) { } #endif // BUILDFLAG(USE_ZYGOTE) @@ -237,7 +237,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() { return static_cast(process_->GetHost()) ->child_process(); -@@ -454,9 +480,26 @@ bool UtilityProcessHost::StartProcess() { +@@ -455,9 +481,26 @@ bool UtilityProcessHost::StartProcess() { } #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN) @@ -266,7 +266,7 @@ index 26966d77a2ca88ce62afa16fd0f33cd5815b8c95..b7dfacb25a09b105e982c013119271b8 #if BUILDFLAG(IS_WIN) if (!preload_libraries_.empty()) { diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h -index 066939fcbb7bc05e27a6bb4ed122750732629454..ab6b64737245a840a3e2b06d2ab501ef4aaed7a2 100644 +index d13e6db4857242480591bff040709532d16f513d..1164da12ee71a8575c17bf1b84a505e8a32b96b3 100644 --- a/content/browser/service_host/utility_process_host.h +++ b/content/browser/service_host/utility_process_host.h @@ -30,6 +30,10 @@ diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch index 0e52381457975..4f97d4fa29c1b 100644 --- a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -21,7 +21,7 @@ making three primary changes to Blink: * Mostly simple "plumbing" for the setting through blink. diff --git a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc -index 25cf6b544dcee15a9616b6963eaae0264aba3db6..13d5b30d00ce8dca96eb3bc5454f9d353375d4c6 100644 +index 5e233d63c09088d73cd1a54a58b235018c193ac3..4f2dcb339ad79f31ba5e4c347cb91d5639d27ce6 100644 --- a/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc +++ b/third_party/blink/common/renderer_preferences/renderer_preferences_mojom_traits.cc @@ -128,6 +128,8 @@ bool StructTraitselectron_corner_smoothing_css = data.electron_corner_smoothing_css(); + - return true; - } + out->canvas_noise_token = data.canvas_noise_token(); + return true; diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h -index cae096396b0635f1c4bba6ac8fee47fd957dc698..03db6cddab5cd1b9f3f7c90390bc53baa9e14b65 100644 +index ff84a20511448d4211d0e25dfc12e7eabc34a9e0..886e9d819c3bde7f33eec3497d1cadb76de4237f 100644 --- a/third_party/blink/public/common/renderer_preferences/renderer_preferences.h +++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences.h @@ -91,6 +91,7 @@ struct BLINK_COMMON_EXPORT RendererPreferences { bool caret_browsing_enabled{false}; bool uses_platform_autofill{false}; std::vector explicitly_allowed_network_ports; -+ bool electron_corner_smoothing_css; ++ bool electron_corner_smoothing_css{true}; + uint64_t canvas_noise_token{0}; RendererPreferences(); - RendererPreferences(const RendererPreferences& other); diff --git a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h -index 33b4bd3f0c9488f1013aea026c7fe559ba750cd8..6b4157199c14a4c276e65512e89f2429253aec5c 100644 +index c88ddaf7fd5fc27889bcacac9366330e4013eba3..e4f492a11637886c60ece665371d117f3a34ec8d 100644 --- a/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h +++ b/third_party/blink/public/common/renderer_preferences/renderer_preferences_mojom_traits.h @@ -275,6 +275,11 @@ struct BLINK_COMMON_EXPORT @@ -58,22 +58,24 @@ index 33b4bd3f0c9488f1013aea026c7fe559ba750cd8..6b4157199c14a4c276e65512e89f2429 + return data.electron_corner_smoothing_css; + } + - static bool Read(blink::mojom::RendererPreferencesDataView, - ::blink::RendererPreferences* out); - }; + static const uint64_t& canvas_noise_token( + const ::blink::RendererPreferences& data) { + return data.canvas_noise_token; diff --git a/third_party/blink/public/mojom/renderer_preferences.mojom b/third_party/blink/public/mojom/renderer_preferences.mojom -index bbcec1dcdaaaf932b3d82c64e8aeb2e7c04b05bf..689205607a763c1d6e040069b1357d84e8ba4bd5 100644 +index 65766b955e81bfc332bc2c4e0b9da48389c1bd68..a475e1bfee46f0a77d1cfbdea47e9de6516d1194 100644 --- a/third_party/blink/public/mojom/renderer_preferences.mojom +++ b/third_party/blink/public/mojom/renderer_preferences.mojom -@@ -201,4 +201,6 @@ struct RendererPreferences { - bool uses_platform_autofill = false; +@@ -202,6 +202,8 @@ struct RendererPreferences { array explicitly_allowed_network_ports; -+ + + bool electron_corner_smoothing_css; - }; ++ + // A randomized 64 bit token that is generated per browser session, + // used for canvas noising. + uint64 canvas_noise_token = 0; diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom -index 3e3d56992ab135ee88257681f93e39a470192857..26e87c2be381c0fd7d5116d95a107082e2549eae 100644 +index a4c78d85e525224823ce4b2434519cc0a39922ef..8071b283b48ab89226313bb199c0cece67ca5039 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom @@ -48,6 +48,7 @@ enum CSSSampleId { @@ -97,28 +99,23 @@ index a53b4901dde0dc83dce6c9b56616eef0d02d94a5..b419672af985f673f375fbb63b4d2b2c protected: ~WebSettings() = default; diff --git a/third_party/blink/renderer/build/scripts/core/css/css_properties.py b/third_party/blink/renderer/build/scripts/core/css/css_properties.py -index 753ba8990f722bafd1770a5e70307cff3764d3f1..16cec517d72887c089f85867e8e37c03199ab394 100755 +index aa3a6b93cceae8c8bfbefdd7a043ae576f921979..17544d7ed4757fb51e2f82fd1b90638131ba05cc 100755 --- a/third_party/blink/renderer/build/scripts/core/css/css_properties.py +++ b/third_party/blink/renderer/build/scripts/core/css/css_properties.py -@@ -311,7 +311,13 @@ class CSSProperties(object): - name_without_leading_dash = property_.name.original +@@ -313,7 +313,7 @@ class CSSProperties(object): if name_without_leading_dash.startswith('-'): name_without_leading_dash = name_without_leading_dash[1:] -+ # Extra sort level to avoid -internal-* properties being assigned -+ # values too large to fit in a byte. -+ internal_weight = 0 -+ if property_.name.original.startswith('-internal'): -+ internal_weight = -1 + internal_visited_order = 1 +- if name_without_leading_dash.startswith('internal-visited-'): ++ if name_without_leading_dash.startswith('internal-'): + internal_visited_order = 0 property_.sorting_key = (-property_.priority, -+ internal_weight, - name_without_leading_dash) - - sorting_keys = {} + internal_visited_order, diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 -index 6cf39b4a15ac290891d56a8d1d7b30846a329f79..5a0d840d5c01fb1ed95bacd36cc4f01443afdf94 100644 +index b44da8add10fdeadd24653441115df035a93dd50..ce7c7bd32f3a8ea262f01c408474ff4ffe7fe0aa 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5 -@@ -8724,6 +8724,24 @@ +@@ -8795,6 +8795,24 @@ property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], }, @@ -144,7 +141,7 @@ index 6cf39b4a15ac290891d56a8d1d7b30846a329f79..5a0d840d5c01fb1ed95bacd36cc4f014 { name: "-internal-visited-color", diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc -index 998fb2cfb682e61d89bb6f832cd91efa658f9773..8ad689bd9327569d26eb5f449a707d6b0d7c2536 100644 +index 10ab8a458fb3348476a6e904f684af77c55b103a..37339df2a5601b59abc50fe75fb844e1b7bb5368 100644 --- a/third_party/blink/renderer/core/css/css_property_equality.cc +++ b/third_party/blink/renderer/core/css/css_property_equality.cc @@ -346,6 +346,8 @@ bool CSSPropertyEquality::PropertiesEqual(const PropertyHandle& property, @@ -157,10 +154,10 @@ index 998fb2cfb682e61d89bb6f832cd91efa658f9773..8ad689bd9327569d26eb5f449a707d6b return a.EmptyCells() == b.EmptyCells(); case CSSPropertyID::kFill: diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc -index c1aa3851a8530f1993de160773d3fae107c4d8bd..d0b87808b0d0466473d21720e44366228daed218 100644 +index 663ce4469a595e2c5a7b8ed992bdc0e22ab101a9..acdbd7ab75b12630356777a475be794091be25ad 100644 --- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc -@@ -11857,5 +11857,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( +@@ -12035,5 +12035,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( CSSValueID::kNone>(stream); } @@ -187,10 +184,10 @@ index c1aa3851a8530f1993de160773d3fae107c4d8bd..d0b87808b0d0466473d21720e4436622 } // namespace css_longhand } // namespace blink diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc -index 797c8e2d7ee777bcd88e0e4e6a65992342c2a098..c8d024213eb4dfe1ae82e0543f066df55555213e 100644 +index f7f49ef23b77c7a3f27ae8ba129e5eea1617d545..0ddf6f59c45d2f6212317dc18f3c5a80eeda5395 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc -@@ -3861,4 +3861,12 @@ PositionArea StyleBuilderConverter::ConvertPositionArea( +@@ -3859,4 +3859,12 @@ PositionArea StyleBuilderConverter::ConvertPositionArea( return PositionArea(span[0], span[1], span[2], span[3]); } @@ -204,10 +201,10 @@ index 797c8e2d7ee777bcd88e0e4e6a65992342c2a098..c8d024213eb4dfe1ae82e0543f066df5 + } // namespace blink diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h -index c0f4544a38dc486708dec5a4b3646fb3f15ff2e0..8b3d4e95fb690f9e7b38265be0a77d6e49271944 100644 +index b9f8145af13fb9d68e85f166905476c177c3a053..0a9ad304f5039747a0580ab63664d4429a67970d 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h -@@ -421,6 +421,8 @@ class StyleBuilderConverter { +@@ -419,6 +419,8 @@ class StyleBuilderConverter { const CSSValue&); static PositionArea ConvertPositionArea(StyleResolverState&, const CSSValue&); @@ -230,7 +227,7 @@ index 4a29a2200eaab5084078e928a68c862296c6ff91..fcd879deec0e68b3b6988402d19570cf + } // namespace blink diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h -index eabcddfa5f17497ef0611fa43f77dd13e2a54e00..96266c4f8c17b589f3d9c549e2836a147b7401ce 100644 +index 5e8d2bfbccd0625c2598544a9cba3d71373eded2..e68a97ee75754fc7196f11cf5c731550b5a12276 100644 --- a/third_party/blink/renderer/core/exported/web_settings_impl.h +++ b/third_party/blink/renderer/core/exported/web_settings_impl.h @@ -237,6 +237,7 @@ class CORE_EXPORT WebSettingsImpl final : public WebSettings { @@ -242,11 +239,11 @@ index eabcddfa5f17497ef0611fa43f77dd13e2a54e00..96266c4f8c17b589f3d9c549e2836a14 bool RenderVSyncNotificationEnabled() const { return render_v_sync_notification_enabled_; diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index 9810991e0a5d8b931a70e056b6651b8e5fdb9881..2b37a0209d370629f08e9065a22b92ff52053141 100644 +index cf80d64963a2a25afbaa6060b4dfda425638858b..5f3a6dba6fe4f93698a8e18afa07efdac73eabee 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -3574,6 +3574,9 @@ void WebViewImpl::UpdateRendererPreferences( - #endif +@@ -3575,6 +3575,9 @@ void WebViewImpl::UpdateRendererPreferences( + CanvasNoiseToken::Set(renderer_preferences_.canvas_noise_token); MaybePreloadSystemFonts(GetPage()); + @@ -271,24 +268,12 @@ index f4cdee12ea4352067f5de3e074e43d51ef56d2e5..6377e4b1ea8aa46b0bf69f8420b6c439 ], } diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc -index 68dbf4accafc0ce8100d6d488195e9dcde8b1502..dcd13e67acde42b181b219b2f690e2fc76ad917d 100644 +index 57f71cbd80d88416693e5b83f695fd02eb7a405b..d638da7a734fa40de118d056c59488b63caff7e2 100644 --- a/third_party/blink/renderer/core/paint/box_painter_base.cc +++ b/third_party/blink/renderer/core/paint/box_painter_base.cc -@@ -324,8 +324,9 @@ void BoxPainterBase::PaintNormalBoxShadow(const PaintInfo& info, - if (has_border_radius) { - FloatRoundedRect rounded_fill_rect(fill_rect, border.GetRadii()); - ApplySpreadToShadowShape(rounded_fill_rect, shadow.Spread()); -- context.FillRoundedRect( -- rounded_fill_rect, Color::kBlack, -+ ContouredRect contoured_fill_rect(rounded_fill_rect, border.GetCornerCurvature()); -+ context.FillContouredRect( -+ contoured_fill_rect, Color::kBlack, - PaintAutoDarkMode(style, DarkModeFilter::ElementRole::kBackground)); - } else { - fill_rect.Outset(shadow.Spread()); -@@ -413,16 +414,20 @@ void BoxPainterBase::PaintInsetBoxShadow(const PaintInfo& info, - AdjustRectForSideClipping(inner_rect, shadow, sides_to_include); - FloatRoundedRect inner_rounded_rect(inner_rect, bounds.GetRadii()); +@@ -417,16 +417,20 @@ void BoxPainterBase::PaintInsetBoxShadow(const PaintInfo& info, + ContouredRect inner_rounded_rect( + FloatRoundedRect(inner_rect, bounds.GetRadii())); ApplySpreadToShadowShape(inner_rounded_rect, -shadow.Spread()); + ContouredRect contoured_bounds( + bounds, ContouredBorderGeometry::ContouredBorder( @@ -310,7 +295,7 @@ index 68dbf4accafc0ce8100d6d488195e9dcde8b1502..dcd13e67acde42b181b219b2f690e2fc context.Clip(bounds.Rect()); } diff --git a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc -index b96a3ba1e16b15807086c8e6a256b256b48e8adb..1396fd3214e18e1ded8fd8a83d964c8c824fbc5e 100644 +index 2c2f4f405074e5baa4a26f255283404f86b40e21..ebeb7d6988ee9e6a4e78cb82fc01fdad6721eaef 100644 --- a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc +++ b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc @@ -43,6 +43,24 @@ float EffectiveCurvature(Superellipse superellipse, const gfx::SizeF& radius) { @@ -349,10 +334,10 @@ index b96a3ba1e16b15807086c8e6a256b256b48e8adb..1396fd3214e18e1ded8fd8a83d964c8c ContouredRect PixelSnappedContouredBorderInternal( diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn -index ae923ecf6c58d608ed3583312edb48ecb6b7f576..759d991162702d31e77590474bc5764c8fd8229b 100644 +index 291676c6583722a3dfbac4d4aaad1ea2ae3cc079..5cd89e8c6c79ab339ffbc330d9bcdb08cca6e869 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn -@@ -1642,6 +1642,8 @@ component("platform") { +@@ -1646,6 +1646,8 @@ component("platform") { "widget/widget_base.h", "widget/widget_base_client.h", "windows_keyboard_codes.h", @@ -362,10 +347,10 @@ index ae923ecf6c58d608ed3583312edb48ecb6b7f576..759d991162702d31e77590474bc5764c sources -= blink_platform_avx_files diff --git a/third_party/blink/renderer/platform/geometry/contoured_rect.h b/third_party/blink/renderer/platform/geometry/contoured_rect.h -index 1a5d76b145307c11ac71cd5840f7ca166655fde2..a40aee431d9ecf8bdb011ccc4e8a9ecd813ffe3a 100644 +index b147b8d321d865295007516b15d0aaccfc6f7fac..8f54a3a657c660a52fcd4c94865ca2197b0af514 100644 --- a/third_party/blink/renderer/platform/geometry/contoured_rect.h +++ b/third_party/blink/renderer/platform/geometry/contoured_rect.h -@@ -42,19 +42,29 @@ class PLATFORM_EXPORT ContouredRect { +@@ -47,19 +47,29 @@ class PLATFORM_EXPORT ContouredRect { constexpr CornerCurvature(float top_left, float top_right, float bottom_right, @@ -398,7 +383,7 @@ index 1a5d76b145307c11ac71cd5840f7ca166655fde2..a40aee431d9ecf8bdb011ccc4e8a9ecd } constexpr bool IsUniform() const { -@@ -66,6 +76,7 @@ class PLATFORM_EXPORT ContouredRect { +@@ -71,6 +81,7 @@ class PLATFORM_EXPORT ContouredRect { constexpr float TopRight() const { return top_right_; } constexpr float BottomRight() const { return bottom_right_; } constexpr float BottomLeft() const { return bottom_left_; } @@ -406,80 +391,43 @@ index 1a5d76b145307c11ac71cd5840f7ca166655fde2..a40aee431d9ecf8bdb011ccc4e8a9ecd constexpr bool operator==(const CornerCurvature&) const = default; -@@ -76,6 +87,7 @@ class PLATFORM_EXPORT ContouredRect { +@@ -81,6 +92,7 @@ class PLATFORM_EXPORT ContouredRect { float top_right_ = kRound; float bottom_right_ = kRound; float bottom_left_ = kRound; + float smoothness_ = 0.0f; }; - constexpr ContouredRect() = default; -diff --git a/third_party/blink/renderer/platform/geometry/path.cc b/third_party/blink/renderer/platform/geometry/path.cc -index 4b63f7f3e113e77bf91810b91c5fad1b6bf5de92..6121bd490717ce6bf4ba7d933e1a9f3eae1752e1 100644 ---- a/third_party/blink/renderer/platform/geometry/path.cc -+++ b/third_party/blink/renderer/platform/geometry/path.cc -@@ -33,6 +33,7 @@ + // A Corner is a axis-aligned quad, with the points ordered (start, outer, +diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc +index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d42368b2e 100644 +--- a/third_party/blink/renderer/platform/geometry/path_builder.cc ++++ b/third_party/blink/renderer/platform/geometry/path_builder.cc +@@ -4,6 +4,7 @@ - #include + #include "third_party/blink/renderer/platform/geometry/path_builder.h" +#include "electron/shell/renderer/electron_smooth_round_rect.h" #include "third_party/blink/renderer/platform/geometry/contoured_rect.h" - #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h" - #include "third_party/blink/renderer/platform/geometry/skia_geometry_utils.h" -@@ -660,6 +661,18 @@ void Path::AddContouredRect(const ContouredRect& contoured_rect) { - return; + #include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h" + #include "third_party/blink/renderer/platform/geometry/path.h" +@@ -231,6 +232,19 @@ PathBuilder& PathBuilder::AddContouredRect( + AddRoundedRect(target_rect); + return *this; } - ++ + // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners + if (contoured_rect.GetCornerCurvature().IsSmooth()) { -+ const gfx::RectF& box = rect.Rect(); -+ const FloatRoundedRect::Radii& radii = rect.GetRadii(); -+ path_.addPath(electron::DrawSmoothRoundRect( ++ const gfx::RectF& box = contoured_rect.Rect(); ++ const FloatRoundedRect::Radii& radii = contoured_rect.GetRadii(); ++ builder_.addPath(electron::DrawSmoothRoundRect( + box.x(), box.y(), box.width(), box.height(), + std::min(contoured_rect.GetCornerCurvature().Smoothness(), 1.0f), + radii.TopLeft().width(), radii.TopRight().width(), + radii.BottomRight().width(), radii.BottomLeft().width())); -+ return; ++ return *this; + } + - const ContouredRect::CornerCurvature& curvature = - contoured_rect.GetCornerCurvature(); - path_.moveTo(gfx::PointFToSkPoint(rect.TopLeftCorner().top_right())); -diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc -index 6361cc655af8c2bef6803efe6f3c382c1eadb851..9439df63a7d265d1f93c89c275d84a8a1dde30c6 100644 ---- a/third_party/blink/renderer/platform/graphics/graphics_context.cc -+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc -@@ -924,6 +924,19 @@ void GraphicsContext::FillRectWithRoundedHole( - DarkModeFlags(this, auto_dark_mode, flags)); - } - -+void GraphicsContext::FillContouredRect(const ContouredRect& contoured_rect, -+ const Color& color, -+ const AutoDarkMode& auto_dark_mode) { -+ if (contoured_rect.HasRoundCurvature()) { -+ FillRoundedRect(contoured_rect.AsRoundedRect(), color, auto_dark_mode); -+ return; -+ } -+ -+ cc::PaintFlags flags = ImmutableState()->FillFlags(); -+ flags.setColor(color.toSkColor4f()); -+ canvas_->drawPath(contoured_rect.GetPath().GetSkPath(), flags); -+} -+ - void GraphicsContext::FillEllipse(const gfx::RectF& ellipse, - const AutoDarkMode& auto_dark_mode) { - DrawOval(gfx::RectFToSkRect(ellipse), ImmutableState()->FillFlags(), -diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h -index 632b0ec1faebc87d13a5538812333bf14f9e402a..ee51cb455600f507e3a97fe3e6f293ff0f47bbd6 100644 ---- a/third_party/blink/renderer/platform/graphics/graphics_context.h -+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h -@@ -318,6 +318,9 @@ class PLATFORM_EXPORT GraphicsContext { - const FloatRoundedRect& rounded_hole_rect, - const Color&, - const AutoDarkMode& auto_dark_mode); -+ void FillContouredRect(const ContouredRect& contoured_rect, -+ const Color& color, -+ const AutoDarkMode& auto_dark_mode); + const FloatRoundedRect& origin_rect = contoured_rect.GetOriginRect(); - void StrokeRect(const gfx::RectF&, - const AutoDarkMode& auto_dark_mode); + if (origin_rect == target_rect) { diff --git a/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch b/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch index 59c949c3587cb..d29ecc8b9f5ae 100644 --- a/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch +++ b/patches/chromium/feat_enable_customizing_symbol_color_in_framecaptionbutton.patch @@ -11,10 +11,10 @@ ensure it has minimum contrast required to be accessible. This should be upstreamed to Chromium if possible. diff --git a/ui/views/window/frame_caption_button.cc b/ui/views/window/frame_caption_button.cc -index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da9134dfe4e6 100644 +index f70d810e5c316a0dfee0b12a0972679f5f56b22b..5a5b2b3e1a70c7365a4d6deb90c183f8392c8c9f 100644 --- a/ui/views/window/frame_caption_button.cc +++ b/ui/views/window/frame_caption_button.cc -@@ -107,7 +107,7 @@ FrameCaptionButton::FrameCaptionButton(PressedCallback callback, +@@ -108,7 +108,7 @@ FrameCaptionButton::FrameCaptionButton(PressedCallback callback, FrameCaptionButton::~FrameCaptionButton() = default; // static @@ -23,7 +23,7 @@ index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da91 // Use IsDark() to change target colors instead of PickContrastingColor(), so // that DefaultFrameHeader::GetTitleColor() (which uses different target // colors) can change between light/dark targets at the same time. It looks -@@ -124,6 +124,22 @@ SkColor FrameCaptionButton::GetButtonColor(SkColor background_color) { +@@ -125,6 +125,22 @@ SkColor FrameCaptionButton::GetButtonColor(SkColor background_color) { .color; } @@ -47,10 +47,10 @@ index 9f2f19eca80d420e10e5269fb24d4ca52f234ae9..ec54aaa17054468a21af4a1a9736da91 float FrameCaptionButton::GetInactiveButtonColorAlphaRatio() { return 0.38f; diff --git a/ui/views/window/frame_caption_button.h b/ui/views/window/frame_caption_button.h -index 0ac923a3ca6052d499ed7c1a4f156b0f19ad4e64..3164f79828218d57843eba823e0f14ff456b2df4 100644 +index e74917399d3306e6557069d85fe90d153cecf585..738965e15ea4a5c3d71a5bf79ea0df78dac9cd73 100644 --- a/ui/views/window/frame_caption_button.h +++ b/ui/views/window/frame_caption_button.h -@@ -44,8 +44,18 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { +@@ -45,8 +45,18 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { FrameCaptionButton& operator=(const FrameCaptionButton&) = delete; ~FrameCaptionButton() override; @@ -70,10 +70,10 @@ index 0ac923a3ca6052d499ed7c1a4f156b0f19ad4e64..3164f79828218d57843eba823e0f14ff // Gets the alpha ratio for the colors of inactive frame caption buttons. static float GetInactiveButtonColorAlphaRatio(); -@@ -134,6 +144,7 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { +@@ -135,6 +145,7 @@ class VIEWS_EXPORT FrameCaptionButton : public Button { // TODO(b/292154873): Store the foreground color instead of the background // color for the SkColor type. - absl::variant color_ = gfx::kPlaceholderColor; + std::variant color_ = gfx::kPlaceholderColor; + SkColor button_color_ = SkColor(); // Whether the button should be painted as active. diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch index 08f61ae649db4..199d092ebd06e 100644 --- a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -522,10 +522,10 @@ index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109cc waiting_on_draw_ack_ = true; diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -index 8c660af0aaa1c031815082838dea1497b725e55b..852f4d9ac45aaa51f5edf0b3a5e3492eec52d743 100644 +index 6de3d8b4cdaf2721a160c6271561b8fd8872e82e..890b95b4aee9210f13ee192a6ea86e072619fcf4 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -@@ -112,7 +112,8 @@ RootCompositorFrameSinkImpl::Create( +@@ -113,7 +113,8 @@ RootCompositorFrameSinkImpl::Create( params->gpu_compositing, params->widget); auto output_surface = output_surface_provider->CreateOutputSurface( params->widget, params->gpu_compositing, display_client.get(), @@ -564,10 +564,10 @@ index 399fba1a3d4e601dc2cdd5f1f4def8b7fd7a3011..8bcbe0d26c80323155d536c0d3a177a1 gpu::SyncPointManager* GetSyncPointManager() override; gpu::Scheduler* GetGpuScheduler() override; diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc -index 6e35e3a2e1cc10d62a487111d1e185bf900d9cfa..fe0dbc425746ec97372cade1365a5654b22881f3 100644 +index 7839f5afb9ac1aef3f809042d14e6bf2f48f84af..7df780d195e3cfcbfc0857133f2c0d21abe04121 100644 --- a/content/browser/compositor/viz_process_transport_factory.cc +++ b/content/browser/compositor/viz_process_transport_factory.cc -@@ -436,8 +436,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( +@@ -431,8 +431,14 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( mojo::AssociatedRemote display_private; root_params->display_private = display_private.BindNewEndpointAndPassReceiver(); @@ -620,10 +620,10 @@ index 2f462f0deb5fc8a637457243fb5d5849fc214d14..695869b83cefaa24af93a2e11b39de05 + Draw(gfx.mojom.Rect damage_rect) => (); }; diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index d353cd4613c734c34f5333607335cc9a434ad88e..cabb39363b30c97f63d323d7a8156774b67f6b32 100644 +index bd5213795191e5947b6c70778ae01a669ab50dd7..3f52b93eab20108e40131472d8abdf9ff60bd191 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -92,6 +92,7 @@ class DisplayPrivate; +@@ -88,6 +88,7 @@ class DisplayPrivate; class ExternalBeginFrameController; } // namespace mojom @@ -631,7 +631,7 @@ index d353cd4613c734c34f5333607335cc9a434ad88e..cabb39363b30c97f63d323d7a8156774 class HostFrameSinkManager; class LocalSurfaceId; class RasterContextProvider; -@@ -154,6 +155,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { +@@ -147,6 +148,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { viz::mojom::ExternalBeginFrameControllerClient> CreateExternalBeginFrameControllerClient() = 0; }; @@ -647,7 +647,7 @@ index d353cd4613c734c34f5333607335cc9a434ad88e..cabb39363b30c97f63d323d7a8156774 // Compositor object to take care of GPU painting. // A Browser compositor object is responsible for generating the final -@@ -198,6 +208,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -191,6 +201,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // Schedules a redraw of the layer tree associated with this compositor. void ScheduleDraw(); @@ -657,7 +657,7 @@ index d353cd4613c734c34f5333607335cc9a434ad88e..cabb39363b30c97f63d323d7a8156774 // Sets the root of the layer tree drawn by this Compositor. The root layer // must have no parent. The compositor's root layer is reset if the root layer // is destroyed. NULL can be passed to reset the root layer, in which case the -@@ -616,6 +629,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -609,6 +622,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, simple_begin_frame_observers_; std::unique_ptr host_begin_frame_observer_; diff --git a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch index fb8d5a45e5aa4..643757d18684e 100644 --- a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch +++ b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch @@ -80,10 +80,10 @@ index 801db538979ba62facdcf3a472dade56723ca639..7abac9a5b13b393713534ae51664c2e5 private: const std::string service_interface_name_; diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index b7dfacb25a09b105e982c013119271b8f137e7b9..8c351bb09e5110a2c6692acc37e7187c43eec8ff 100644 +index 60c4a301c32a46cdc332cccd1cd4ae78802ee8b9..85fc14b2ef520abb191f926dc44a272f4c9e874b 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc -@@ -539,7 +539,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { +@@ -540,7 +540,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { // Take ownership of |client_| so the destructor doesn't notify it of // termination. auto client = std::move(client_); @@ -93,7 +93,7 @@ index b7dfacb25a09b105e982c013119271b8f137e7b9..8c351bb09e5110a2c6692acc37e7187c std::optional UtilityProcessHost::GetServiceName() { diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h -index ab6b64737245a840a3e2b06d2ab501ef4aaed7a2..9cf85b5fc97cf797965642d9bae5b3b0abf56e80 100644 +index 1164da12ee71a8575c17bf1b84a505e8a32b96b3..4cbc30fc4b57440d06a0a0f642cc44c5c755e7f9 100644 --- a/content/browser/service_host/utility_process_host.h +++ b/content/browser/service_host/utility_process_host.h @@ -79,7 +79,7 @@ class CONTENT_EXPORT UtilityProcessHost diff --git a/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch b/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch index 6990e1bb49e7d..d5a070ab577e1 100644 --- a/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch +++ b/patches/chromium/feat_expose_documentloader_setdefersloading_on_webdocumentloader.patch @@ -7,7 +7,7 @@ This allows embedders to call SetDefersLoading without reaching into Blink inter This might be upstreamable? diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h -index 23b29fe25bc463ff1d36aa502a27c4222595e7c5..c1ac6172c4cee72f64f42ca64d2db9c0f1f48738 100644 +index 0527831e1f8d7923ba0f687a5c0da8573189d867..f72af0e6cfcf06d47bd917def993f081530ab66b 100644 --- a/third_party/blink/public/web/web_document_loader.h +++ b/third_party/blink/public/web/web_document_loader.h @@ -38,6 +38,7 @@ @@ -28,10 +28,10 @@ index 23b29fe25bc463ff1d36aa502a27c4222595e7c5..c1ac6172c4cee72f64f42ca64d2db9c0 // Returns the http referrer of original request which initited this load. diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h -index 1c6da71f9526f1567fe18d40524818f48e62584a..63b9bdae7f6552e047023be3b503bf05154e7ca9 100644 +index e86efe8c9fc2d27fefc5e47afe401b8a6b1419ba..9d1ef55e4e122f30564f35406c8b51335e3d6996 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h -@@ -327,7 +327,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, +@@ -328,7 +328,7 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected, soft_navigation_heuristics_task_id, bool should_skip_screenshot); diff --git a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch index 24a9a04e113ac..de5d2e3eb8ee7 100644 --- a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch +++ b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch @@ -17,7 +17,7 @@ headers, moving forward we should find a way in upstream to provide access to these headers for loader clients created on the browser process. diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc -index b60fd23a39eb423450b57275526ac4ba36058225..deafb4cd5a55853eb11a1371ca8331ee5f6050b2 100644 +index 7d97d0fd5481bead1f655f2f933a3ae89a729834..9b4c41209190086353b9a87833c07aabb47470bd 100644 --- a/services/network/public/cpp/resource_request.cc +++ b/services/network/public/cpp/resource_request.cc @@ -178,6 +178,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=( @@ -37,7 +37,7 @@ index b60fd23a39eb423450b57275526ac4ba36058225..deafb4cd5a55853eb11a1371ca8331ee allow_cookies_from_browser == other.allow_cookies_from_browser && include_request_cookies_with_response == diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h -index b87bb47d4d51f734ee3dc1e38158726795fcd19e..dd862ff8c43ecbc08467bf2b49ed5044d92ad3f3 100644 +index 538e2a52da142a442342383d223ea0d90b75be8f..19f0defab1d1ba3369423842e4eabbceee781091 100644 --- a/services/network/public/cpp/resource_request.h +++ b/services/network/public/cpp/resource_request.h @@ -77,6 +77,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { @@ -49,7 +49,7 @@ index b87bb47d4d51f734ee3dc1e38158726795fcd19e..dd862ff8c43ecbc08467bf2b49ed5044 mojo::PendingRemote trust_token_observer; mojo::PendingRemote diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc -index ae26e1cff7235ac77d4610f936cf83b01a9b429d..d45189ba3280cd6802240fc5cee25388f77bdb39 100644 +index 2021e4c8f05008dd57e67c14fc7e66bf49bd6e3e..2a5ed7fc54f9efd773ec6dded1b66bb9c9f26580 100644 --- a/services/network/public/cpp/url_request_mojom_traits.cc +++ b/services/network/public/cpp/url_request_mojom_traits.cc @@ -49,6 +49,7 @@ bool StructTraits>(); out->trust_token_observer = data.TakeTrustTokenObserver< diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h -index fc85f9def245d854d210b14f8190119fbd38404b..1392090ef9a1c8c0ef1a958ebeae75f24a11a6ea 100644 +index 30a38e3f82360417b682c516263f62181825c621..db8a479f483a041a6944465deb6f799a2a9bd93a 100644 --- a/services/network/public/cpp/url_request_mojom_traits.h +++ b/services/network/public/cpp/url_request_mojom_traits.h @@ -71,6 +71,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) @@ -76,7 +76,7 @@ index fc85f9def245d854d210b14f8190119fbd38404b..1392090ef9a1c8c0ef1a958ebeae75f2 cookie_observer( const network::ResourceRequest::TrustedParams& trusted_params) { diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom -index eb5ca6401eec0ff1d6a897ea86ca2a1a252f86ec..caedd3dfde37dd136721429f90799e0493ab0a9d 100644 +index 78fa9126457ee380b12e39e3bf35ea4c2ccaa36c..4f39fa5d3bd665e3343882d11f1ab5747c80610e 100644 --- a/services/network/public/mojom/url_request.mojom +++ b/services/network/public/mojom/url_request.mojom @@ -74,6 +74,9 @@ struct TrustedUrlRequestParams { @@ -90,7 +90,7 @@ index eb5ca6401eec0ff1d6a897ea86ca2a1a252f86ec..caedd3dfde37dd136721429f90799e04 // a cookie. If this is set to non-null, the observer passed to // URLLoaderFactory will be ignored. diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom -index 1e5b36d0a46d9c66b7a56a6668663dd8196bd172..3431f4c3959264b64ac0f35654719ab0b45b5cb7 100644 +index 72188dae668f2bd91b8b7f181448f459600a82c2..b3dae05e160160cc8f2decee5af78c21bd76c822 100644 --- a/services/network/public/mojom/url_response_head.mojom +++ b/services/network/public/mojom/url_response_head.mojom @@ -13,6 +13,7 @@ import "services/network/public/mojom/attribution.mojom"; @@ -101,7 +101,7 @@ index 1e5b36d0a46d9c66b7a56a6668663dd8196bd172..3431f4c3959264b64ac0f35654719ab0 import "services/network/public/mojom/ip_endpoint.mojom"; import "services/network/public/mojom/load_timing_info.mojom"; import "services/network/public/mojom/network_param.mojom"; -@@ -50,6 +51,9 @@ struct URLResponseHead { +@@ -51,6 +52,9 @@ struct URLResponseHead { // The response headers or NULL if the URL type does not support headers. HttpResponseHeaders headers; @@ -112,10 +112,10 @@ index 1e5b36d0a46d9c66b7a56a6668663dd8196bd172..3431f4c3959264b64ac0f35654719ab0 string mime_type; diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc -index c5f551ca79ecfb80a3c29b901d44a83d082a6da6..bae9c2c10d240a8691787d6e52190b13575df020 100644 +index 54501620f428c1e539a8172054180c9c58145766..bbde6ebfb8f051622b1f499d472b00fac2093be7 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc -@@ -669,6 +669,9 @@ URLLoader::URLLoader( +@@ -673,6 +673,9 @@ URLLoader::URLLoader( mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunner::GetCurrentDefault()), per_factory_orb_state_(context.GetMutableOrbState()), @@ -125,7 +125,7 @@ index c5f551ca79ecfb80a3c29b901d44a83d082a6da6..bae9c2c10d240a8691787d6e52190b13 devtools_request_id_(request.devtools_request_id), options_(PopulateOptions(options, factory_params_->is_orb_enabled, -@@ -970,7 +973,7 @@ void URLLoader::ConfigureRequest( +@@ -982,7 +985,7 @@ void URLLoader::ConfigureRequest( &URLLoader::IsSharedDictionaryReadAllowed, base::Unretained(this))); } @@ -134,7 +134,7 @@ index c5f551ca79ecfb80a3c29b901d44a83d082a6da6..bae9c2c10d240a8691787d6e52190b13 url_request_->SetResponseHeadersCallback(base::BindRepeating( &URLLoader::SetRawResponseHeaders, base::Unretained(this))); } -@@ -2062,6 +2065,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { +@@ -2142,6 +2145,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { } response_ = BuildResponseHead(); @@ -155,10 +155,10 @@ index c5f551ca79ecfb80a3c29b901d44a83d082a6da6..bae9c2c10d240a8691787d6e52190b13 // Parse and remove the Trust Tokens response headers, if any are expected, diff --git a/services/network/url_loader.h b/services/network/url_loader.h -index 06b9d01648881d1b955cc6db195a658811e47e84..730fd3ef9f95ba634b43856d432c072962d6000e 100644 +index e6962f686212ca39d66b5d21bd3ff1f852e83644..fd3eb86e2f0f9b3705029befc54c85690e8ca519 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h -@@ -712,6 +712,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader +@@ -773,6 +773,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader std::unique_ptr resource_scheduler_request_handle_; diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index 132d065cea863..96ed54b14c4c6 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,10 +14,10 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 3d8a590aacd3a65d5a19004dc11e770b31a0f614..64dd7b6e507b61fab7a044823462fb04eabba698 100644 +index 29829e282edfa8821bd366a9e9a3755d7f3f8643..9feff09aa83eb88460dce786ab2514f0a9b21c6e 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -941,13 +941,13 @@ void HWNDMessageHandler::FrameTypeChanged() { +@@ -952,13 +952,13 @@ void HWNDMessageHandler::FrameTypeChanged() { void HWNDMessageHandler::PaintAsActiveChanged() { if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || @@ -33,7 +33,7 @@ index 3d8a590aacd3a65d5a19004dc11e770b31a0f614..64dd7b6e507b61fab7a044823462fb04 } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -2337,17 +2337,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, +@@ -2356,17 +2356,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, delegate_->SchedulePaint(); } diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch index 44376494c4f73..ef21bc45bbe47 100644 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch @@ -12,10 +12,10 @@ ui problems (like dissapearing popup during typing in html's input list. diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index bfea25523fd1df85d9393fce80259a8654b8a7c2..e3cd3083bc8c19fdc29729d7ff6db7d0fc562ce6 100644 +index 00113c5a91e4285a102afd37c6c08736d056faf6..f6a719ba09ed9aedc1bdc8322e9bee8d2014fc39 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h -@@ -1209,6 +1209,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, +@@ -1211,6 +1211,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // True if widget was created in headless mode. bool is_headless() const { return is_headless_; } diff --git a/patches/chromium/fix_aspect_ratio_with_max_size.patch b/patches/chromium/fix_aspect_ratio_with_max_size.patch index 5761ac3ed7e4b..0ad82581aa514 100644 --- a/patches/chromium/fix_aspect_ratio_with_max_size.patch +++ b/patches/chromium/fix_aspect_ratio_with_max_size.patch @@ -11,10 +11,10 @@ enlarge window above dimensions set during creation of the BrowserWindow. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index adade58a533c373087d8c51a5744e8f118ba6e9d..3a60e310d1c4048f0e37e085c97b8dfc093aefda 100644 +index 539f5920661010139dc69afb3ff7dd4c9bae762a..1cf98eadc8cbc1ada481c709a873dc1dd443de66 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3759,15 +3759,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, +@@ -3781,15 +3781,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, delegate_->GetMinMaxSize(&min_window_size, &max_window_size); min_window_size = delegate_->DIPToScreenSize(min_window_size); max_window_size = delegate_->DIPToScreenSize(max_window_size); diff --git a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch index 233722adb2e93..13162b7e04374 100644 --- a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch +++ b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch @@ -28,10 +28,10 @@ The patch should be removed in favor of either: Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397. diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc -index 4f7e3cd70d45dd9d0d67b6a39a5dac59af9b95af..a8a0bedfc14cd475ff112e5ff07e137ec4219662 100644 +index 005c18b0685e504a7c323cb2a622e893955fe738..c9e55ca1da0e3f37777cc989511942e622ad05a8 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc -@@ -11068,6 +11068,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { +@@ -11083,6 +11083,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { "blob"); } @@ -45,7 +45,7 @@ index 4f7e3cd70d45dd9d0d67b6a39a5dac59af9b95af..a8a0bedfc14cd475ff112e5ff07e137e // origin of |common_params.url| and/or |common_params.initiator_origin|. url::Origin resolved_origin = url::Origin::Resolve( diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc -index ab265777ded9c875ccafd83b44853d92e35c60fb..074fa3544afd423555578cf0df835171ab559201 100644 +index ca61af9af555e267228430496118797eff130160..bb7c020ba1672e21a3ffee13ff1d3934bde0f099 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc @@ -2334,6 +2334,10 @@ Frame* DocumentLoader::CalculateOwnerFrame() { diff --git a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch index 4134ae484789e..475f205dbbc6c 100644 --- a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch +++ b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch @@ -53,10 +53,10 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb void Compositor::SetSeamlessRefreshRates( const std::vector& seamless_refresh_rates) { diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index cabb39363b30c97f63d323d7a8156774b67f6b32..54e012fa4b0253d8c8f88cc2021be0dcf77cbdee 100644 +index 3f52b93eab20108e40131472d8abdf9ff60bd191..ee19a847cf6d446a927734b9fdd3f1547a9f2d14 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -519,6 +519,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -512,6 +512,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, const cc::LayerTreeSettings& GetLayerTreeSettings() const; @@ -67,7 +67,7 @@ index cabb39363b30c97f63d323d7a8156774b67f6b32..54e012fa4b0253d8c8f88cc2021be0dc size_t saved_events_metrics_count_for_testing() const { return host_->saved_events_metrics_count_for_testing(); } -@@ -710,6 +714,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -703,6 +707,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // See go/report-ux-metrics-at-painting for details. bool animation_started_ = false; diff --git a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch index a79c76006c8d0..36992bfed42b1 100644 --- a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch +++ b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch @@ -9,7 +9,7 @@ to support content settings UI. The support pulls in chrome content settings and UI code which are not valid in the scope of Electron. diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc -index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec4294409950 100644 +index 9f82c7f92f735ed9f9569a8d299d23c7cb83e596..9b7598a26781da8b1e1c7364606574024000921f 100644 --- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc +++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc @@ -6,6 +6,7 @@ @@ -30,7 +30,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 #include "media/base/media_switches.h" #include "net/base/url_util.h" #include "third_party/blink/public/common/features.h" -@@ -49,7 +52,7 @@ constexpr gfx::Size kMinWindowSize(240, 52); +@@ -53,7 +56,7 @@ constexpr gfx::Size kMinWindowSize(240, 52); // not apply to video Picture-in-Picture windows. constexpr double kMaxWindowSizeRatio = 0.8; @@ -39,7 +39,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // Returns true if a document picture-in-picture window should be focused upon // opening it. bool ShouldFocusPictureInPictureWindow(const NavigateParams& params) { -@@ -196,7 +199,7 @@ bool PictureInPictureWindowManager::ExitPictureInPictureViaWindowUi( +@@ -200,7 +203,7 @@ bool PictureInPictureWindowManager::ExitPictureInPictureViaWindowUi( return false; } @@ -48,7 +48,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // The user manually closed the pip window, so let the tab helper know in case // the auto-pip permission dialog was visible. if (auto* tab_helper = AutoPictureInPictureTabHelper::FromWebContents( -@@ -401,7 +404,7 @@ gfx::Size PictureInPictureWindowManager::GetMaximumWindowSize( +@@ -415,7 +418,7 @@ gfx::Size PictureInPictureWindowManager::GetMaximumWindowSize( // static void PictureInPictureWindowManager::SetWindowParams(NavigateParams& params) { @@ -57,7 +57,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // Always show document picture-in-picture in a new window. When this is // not opened via the AutoPictureInPictureTabHelper, focus the window. params.window_action = ShouldFocusPictureInPictureWindow(params) -@@ -493,6 +496,7 @@ PictureInPictureWindowManager::GetOverlayView( +@@ -507,6 +510,7 @@ PictureInPictureWindowManager::GetOverlayView( return nullptr; } @@ -65,7 +65,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 // It would be nice to create this in `EnterPictureInPicture*`, but detecting // auto-pip while pip is in the process of opening doesn't work. // -@@ -531,6 +535,8 @@ PictureInPictureWindowManager::GetOverlayView( +@@ -545,6 +549,8 @@ PictureInPictureWindowManager::GetOverlayView( } return overlay_view; @@ -75,7 +75,7 @@ index ea4646388c4747438ddd7a9164a9e61091679c28..5aded14b616e70ac98e0daa7ff94ec42 PictureInPictureOcclusionTracker* diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index f9e29227fe9396747bed8b1ec970cfdfcf72c03c..d96f85145d1ad2843cf2659f68e2fd1d93c4c1c4 100644 +index e8524bb6706ae06a0feabccbe44250580098e937..87b28c59d5a3abc49c7f5ed084ad78dcae02b39a 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -430,11 +430,13 @@ std::unique_ptr VideoOverlayWindowViews::Create( diff --git a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch index c831b895ff618..bedbac426a774 100644 --- a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch +++ b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch @@ -13,10 +13,10 @@ messages in the legacy window handle layer. These conditions are regularly hit with WCO-enabled windows on Windows. diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc -index e63c2f637828d426e1c8fb16774a08722bfc9fcc..bfc5254ec692fc5cfbd93fabae7969adeb644393 100644 +index ffbef89276aeaa167424b67a07080b38457ec5d3..f39a79687595d7547e3a25fc4dc1cdbc5c2495b4 100644 --- a/content/browser/renderer_host/legacy_render_widget_host_win.cc +++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc -@@ -325,12 +325,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, +@@ -328,12 +328,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param, BOOL& handled) { @@ -31,23 +31,23 @@ index e63c2f637828d426e1c8fb16774a08722bfc9fcc..bfc5254ec692fc5cfbd93fabae7969ad tme.hwndTrack = hwnd(); tme.dwHoverTime = 0; TrackMouseEvent(&tme); -@@ -361,7 +361,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, - // out of the picture. - if (!handled && - (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) { -- ret = ::DefWindowProc(GetParent(), message, w_param, l_param); -+ // Send WM_NCMOUSEMOVE messages using the LegacyRenderWidgetHostHWND's -+ // handle so mouse tracking on non-client areas doesn't break. -+ HWND target = message == WM_NCMOUSEMOVE ? hwnd() : GetParent(); -+ ret = ::DefWindowProc(target, message, w_param, l_param); - handled = TRUE; - } +@@ -366,7 +366,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, + // the picture. + if (!handled && + (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) { +- ret = ::DefWindowProc(GetParent(), message, w_param, l_param); ++ // Send WM_NCMOUSEMOVE messages using the LegacyRenderWidgetHostHWND's ++ // handle so mouse tracking on non-client areas doesn't break. ++ HWND target = message == WM_NCMOUSEMOVE ? hwnd() : GetParent(); ++ ret = ::DefWindowProc(target, message, w_param, l_param); + handled = TRUE; } + return ret; diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.h b/content/browser/renderer_host/legacy_render_widget_host_win.h -index aad10a91356b8b13e15fac0488d25187af23ce36..f9a3db0c54831ad0a1c9946a98a6d7154776ae51 100644 +index 17952b4b6ab559ba1c9076b100b060a7011edeec..1eda20e67d3b3184bec154f24c4259bb4a49b099 100644 --- a/content/browser/renderer_host/legacy_render_widget_host_win.h +++ b/content/browser/renderer_host/legacy_render_widget_host_win.h -@@ -103,6 +103,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND +@@ -105,6 +105,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND MESSAGE_HANDLER_EX(WM_VSCROLL, OnScroll) MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest) MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange) diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index bbfafcb56c48d..262ec694fc975 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -9,10 +9,10 @@ focus node change via TextInputManager. chromium-bug: https://crbug.com/1369605 diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc -index 7e9f2e71f08c1324a805462064d4fa485041c19f..2b4543ca40eac0f56c6408e27aac523827093724 100644 +index 95a52f1cc2024e4a9cd694429d5304a5860a1c1e..3e096f196b9514fec5738f29e7c63bcbb9b2f640 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc -@@ -3220,6 +3220,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( +@@ -3241,6 +3241,12 @@ void RenderWidgetHostViewAura::OnTextSelectionChanged( } } @@ -26,10 +26,10 @@ index 7e9f2e71f08c1324a805462064d4fa485041c19f..2b4543ca40eac0f56c6408e27aac5238 RenderWidgetHostViewAura* popup_child_host_view) { popup_child_host_view_ = popup_child_host_view; diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h -index 06fc7daf2761ae728f3adf9eb2ef3910a14cf827..8d34f9dfd9a8625a3fab397f7b96c12c87cb8a61 100644 +index 6f96b83c36ee026bd37b54de55da72cc802ed699..c54ed7d8f5d4d371626865c6ae63ef2efbef1dba 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h -@@ -653,6 +653,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura +@@ -654,6 +654,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura RenderWidgetHostViewBase* updated_view) override; void OnTextSelectionChanged(TextInputManager* text_input_mangager, RenderWidgetHostViewBase* updated_view) override; @@ -87,10 +87,10 @@ index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac4 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index c3b563b0b727bc35f6d4499c589110644ebe9cd1..6010d9f9fc9bfeffb3e5a64de7352b52a202cbf7 100644 +index 1e6ee8c65d56f2821485f855bbf4e8bb8212058c..51355738262d80afaf1f319b5d90c8a74d435ffd 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9772,7 +9772,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9834,7 +9834,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/fix_properly_honor_printing_page_ranges.patch b/patches/chromium/fix_properly_honor_printing_page_ranges.patch index 08b002943d27a..3ba30eeb0356c 100644 --- a/patches/chromium/fix_properly_honor_printing_page_ranges.patch +++ b/patches/chromium/fix_properly_honor_printing_page_ranges.patch @@ -62,7 +62,7 @@ index f39f8b2dd3aa2b89498ed5331aa9b9ba6a02abf5..c0e155c14b2b4e81cde35cea1db284bc PMPrintSettings print_settings = static_cast([print_info_ PMPrintSettings]); diff --git a/printing/printing_context_system_dialog_win.cc b/printing/printing_context_system_dialog_win.cc -index 858e1bb00390b6097a27ffe20997672914e8f28f..953f203d21b4da559791efe228f27b56c2f85263 100644 +index d5eec01f35bd182d5bb49138fc66446fcd5f8cdf..3c289d7cdf7fdf7717fc2397e3344cbe614e0b70 100644 --- a/printing/printing_context_system_dialog_win.cc +++ b/printing/printing_context_system_dialog_win.cc @@ -73,14 +73,30 @@ void PrintingContextSystemDialogWin::AskUserForSettings( @@ -70,7 +70,7 @@ index 858e1bb00390b6097a27ffe20997672914e8f28f..953f203d21b4da559791efe228f27b56 dialog_options.nStartPage = START_PAGE_GENERAL; if (max_pages) { - // Default initialize to print all the pages. - memset(ranges, 0, sizeof(ranges)); + UNSAFE_TODO(memset(ranges, 0, sizeof(ranges))); - ranges[0].nFromPage = 1; - ranges[0].nToPage = max_pages; - dialog_options.nPageRanges = 1; diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index 7474ebc136021..3bc12bffae486 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,10 +18,10 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 8fe48c9bef144218e34434d563883b15733d03bc..3d8a590aacd3a65d5a19004dc11e770b31a0f614 100644 +index cec234006cbcacff953ce9ff4175006b057aa341..29829e282edfa8821bd366a9e9a3755d7f3f8643 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1796,7 +1796,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { +@@ -1816,7 +1816,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); diff --git a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch index 3c7da3c7a1ef0..23b8d8723968f 100644 --- a/patches/chromium/fix_restore_original_resize_performance_on_macos.patch +++ b/patches/chromium/fix_restore_original_resize_performance_on_macos.patch @@ -11,10 +11,10 @@ This patch should be upstreamed as a conditional revert of the logic in desktop vs mobile runtimes. i.e. restore the old logic only on desktop platforms diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 7c31b82e2903507bb69aaa71fc0ed51cad06d1c2..0ad97d9973fc6d637967f911cb37ada2313e2776 100644 +index 9bcab4e1e8a0fa429488555f4f7bd1c54888d10e..bb2e3afdd2b3c6579e32d7eeba59bfc06e952ea2 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc -@@ -2114,9 +2114,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { +@@ -2122,9 +2122,8 @@ RenderWidgetHostImpl::GetWidgetInputHandler() { void RenderWidgetHostImpl::NotifyScreenInfoChanged() { // The resize message (which may not happen immediately) will carry with it // the screen info as well as the new size (if the screen has changed scale diff --git a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch index 398721de00d98..7820ed391296e 100644 --- a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch +++ b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch @@ -59,7 +59,7 @@ index cba373664bec3a32abad6fe0396bd67b53b7e67f..a54f1b3351efd2d8f324436f7f35cd43 #endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_EXECUTION_CALLBACK_H_ diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 99ae1320be10ac19d204edfe1c1cf4f52c62eb76..e6a3957848f760809deb0080ee5b87c849dca587 100644 +index 19efdf1f30eb5409f9d8a64f008891a2f4bda47c..0e1212d227ee5ffc536bd0349708a31db6ddfe28 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc @@ -3099,6 +3099,7 @@ void LocalFrame::RequestExecuteScript( @@ -92,7 +92,7 @@ index 5cdeaa531babca965bed7e1e18ee993f1ba0d847..651e64c0ea39ec28db117aa3a61ea87d mojom::blink::WantResultOption, mojom::blink::PromiseResultOption); diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc -index b77ad041a717ef8f317a9d15edd61af3c465b2aa..e3955c64ff894682b91c4cc3c49c672659e3a3bf 100644 +index ff24f4607190bc127a3da3aba0544cb6f67dde3f..9aa4f9d0356c078f5f42b06dc166686dc0c64b81 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc @@ -964,6 +964,7 @@ void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld( @@ -203,7 +203,7 @@ index fa65331f40b90d812b71a489fd560e9359152d2b..390714d631dc88ef92d59ef9618a5706 const mojom::blink::UserActivationOption user_activation_option_; const mojom::blink::LoadEventBlockingOption blocking_option_; diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc -index b71f1318b186d33fdedd051b0afd04cc60d4d430..5c9fb1e773d9550d2b6b248c66c97ad07e84674e 100644 +index 5e12b61ba14cd1afb07b71ff15e73e905da0addc..685a2ebb6694c173471d0450149321254da652ec 100644 --- a/third_party/blink/renderer/core/frame/web_frame_test.cc +++ b/third_party/blink/renderer/core/frame/web_frame_test.cc @@ -298,6 +298,7 @@ void ExecuteScriptsInMainWorld( @@ -215,7 +215,7 @@ index b71f1318b186d33fdedd051b0afd04cc60d4d430..5c9fb1e773d9550d2b6b248c66c97ad0 mojom::blink::WantResultOption::kWantResult, wait_for_promise); } diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -index 7f9cc8cfe501692d4dab847416298ff980aaa500..6524313c1c2a7520b5cf3c4e6abeb2e300c67ec0 100644 +index 6377ce1f6e3c6e6507d73a4bf151b54b30bf042a..0ceb0046224a901297cdf011d5d444e5dd1362c8 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc @@ -1107,14 +1107,15 @@ void WebLocalFrameImpl::RequestExecuteScript( @@ -237,7 +237,7 @@ index 7f9cc8cfe501692d4dab847416298ff980aaa500..6524313c1c2a7520b5cf3c4e6abeb2e3 bool WebLocalFrameImpl::IsInspectorConnected() { diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h -index 9ae322eb9d30c684ef18addf59201aad4474d34c..3a320e56db9b43c813b3e63f0394cc1c93f9c3b8 100644 +index f52966d82c8e904e213e7b2c77ea6d28e980a8e8..940cf84db8ed28212243fac7c61d90f12d85735a 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h @@ -196,6 +196,7 @@ class CORE_EXPORT WebLocalFrameImpl final diff --git a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch index 1e2433baad824..9700e831af6ca 100644 --- a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch +++ b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch @@ -6,10 +6,10 @@ Subject: fix: select the first menu item when opened via keyboard This fixes an accessibility issue where the root view is 'focused' to the screen reader instead of the first menu item as with all other native menus. This patch will be upstreamed. diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc -index e82f04bf408c5f7c08df6d476ae3130705af6ae4..402e39e39b24f676ec6fc7525235c0ec5f5cdf9b 100644 +index c5f3700f8f04b11b1a3dcc214dab40622652cd3a..43cbc0f8febd2330f47b5617b3dc9da9beac4962 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc -@@ -700,6 +700,14 @@ void MenuController::Run(Widget* parent, +@@ -701,6 +701,14 @@ void MenuController::Run(Widget* parent, SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); } @@ -24,7 +24,7 @@ index e82f04bf408c5f7c08df6d476ae3130705af6ae4..402e39e39b24f676ec6fc7525235c0ec if (button_controller) { pressed_lock_ = button_controller->TakeLock( false, ui::LocatedEvent::FromIfValid(event)); -@@ -2406,19 +2414,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { +@@ -2407,19 +2415,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { } item->GetSubmenu()->ShowAt(params); diff --git a/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch b/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch deleted file mode 100644 index 3ed1c84f8fa3b..0000000000000 --- a/patches/chromium/fix_take_snapped_status_into_account_when_showing_a_window.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Thu, 13 Mar 2025 10:47:00 +0100 -Subject: fix: take Snapped status into account when showing a window - -Adjusts HWNDMessageHandler::Show to correctly restore windows that were -in a snapped state prior to being hidden or maximized. From Windows -documentation at -https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowarranged: - -> A snapped window (see Snap your windows) is considered to be arranged. -> You should treat arranged as a window state similar to maximized. Arranged, -> maximized, and minimized are mutually exclusive states. - -The logic already took into account a window being maximized and -correctly restored it, but if the window was snapped prior to this CL it -would be removed from its snapped state when re-shown. This fixes that. - -Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/6330848. - -diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 64dd7b6e507b61fab7a044823462fb04eabba698..2cd734db007174834c70365ffe6b46d52673e5cb 100644 ---- a/ui/views/win/hwnd_message_handler.cc -+++ b/ui/views/win/hwnd_message_handler.cc -@@ -683,7 +683,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, - SetWindowPlacement(hwnd(), &placement); - native_show_state = SW_SHOWMAXIMIZED; - } else { -- const bool is_maximized = IsMaximized(); -+ const bool is_maximized_or_arranged = -+ IsMaximized() || IsWindowArranged(hwnd()); - - // Use SW_SHOW/SW_SHOWNA instead of SW_SHOWNORMAL/SW_SHOWNOACTIVATE so that - // the window is not restored to its original position if it is maximized. -@@ -693,7 +694,8 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, - // position, some do not. See crbug.com/1296710 - switch (show_state) { - case ui::mojom::WindowShowState::kInactive: -- native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE; -+ native_show_state = -+ is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE; - break; - case ui::mojom::WindowShowState::kMaximized: - native_show_state = SW_SHOWMAXIMIZED; -@@ -704,9 +706,11 @@ void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state, - case ui::mojom::WindowShowState::kNormal: - if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) || - (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { -- native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE; -+ native_show_state = -+ is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE; - } else { -- native_show_state = is_maximized ? SW_SHOW : SW_SHOWNORMAL; -+ native_show_state = -+ is_maximized_or_arranged ? SW_SHOW : SW_SHOWNORMAL; - } - break; - case ui::mojom::WindowShowState::kFullscreen: diff --git a/patches/chromium/fix_win32_synchronous_spellcheck.patch b/patches/chromium/fix_win32_synchronous_spellcheck.patch index 355c8f8e9a6ae..d704d32f69508 100644 --- a/patches/chromium/fix_win32_synchronous_spellcheck.patch +++ b/patches/chromium/fix_win32_synchronous_spellcheck.patch @@ -12,10 +12,10 @@ This patch can be removed when an asynchronous spellcheck API option is added to Electron. diff --git a/components/spellcheck/browser/windows_spell_checker.cc b/components/spellcheck/browser/windows_spell_checker.cc -index f5a7411037758427eddc088b5426554b4a500d33..04b3edd4d8c58d38e260cc54beb0dab86368fc25 100644 +index 7b78720e8d25a3dee4821c816bd9629b1526e50a..be89b74fd85983e63c983aec0d10036ce1ca63df 100644 --- a/components/spellcheck/browser/windows_spell_checker.cc +++ b/components/spellcheck/browser/windows_spell_checker.cc -@@ -239,6 +239,11 @@ std::vector BackgroundHelper::RequestTextCheckForAllLanguages( +@@ -240,6 +240,11 @@ std::vector BackgroundHelper::RequestTextCheckForAllLanguages( (action == CORRECTIVE_ACTION_GET_SUGGESTIONS || action == CORRECTIVE_ACTION_REPLACE)) { std::vector suggestions; diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index c485ba5d569da..71c8533d01373 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -6,7 +6,7 @@ Subject: frame_host_manager.patch Allows embedder to intercept site instances created by chromium. diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc -index e9f000ae316ac64cd23827f1533ec1416b3ed040..c3fb6dbd8f4c39e1087be567f6a2c2df69b19ef8 100644 +index 9bf65e1e0c893e64cf6dc378fd9b8ae9363d954c..c7ea4d6a44a1c4cce129e9e269f2bcc04838272d 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc @@ -4752,6 +4752,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( @@ -20,7 +20,7 @@ index e9f000ae316ac64cd23827f1533ec1416b3ed040..c3fb6dbd8f4c39e1087be567f6a2c2df } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 7459b083156d1f6bc01198690c4c9ec02d88d862..d564908c539ee9f8c32c27c90f51b63c152f1392 100644 +index 7cfc3331a004fd52d9863a097271f4d892480933..55b0cae39e7aac22315d75b821a8b1123e762e15 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -339,6 +339,11 @@ class CONTENT_EXPORT ContentBrowserClient { diff --git a/patches/chromium/gin_enable_disable_v8_platform.patch b/patches/chromium/gin_enable_disable_v8_platform.patch index d9ea89a0c86c9..b0ac9ec5498b0 100644 --- a/patches/chromium/gin_enable_disable_v8_platform.patch +++ b/patches/chromium/gin_enable_disable_v8_platform.patch @@ -41,10 +41,10 @@ index ff42cfbb6a228e902317c7e3ab035d8437d5dd62..e27f177ce27e177abf6cee84cd466e7a // Returns whether `Initialize` has already been invoked in the process. // Initialization is a one-way operation (i.e., this method cannot return diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index 51c8cd7cc3f44936e1e33d9dc937d5a68d737dd6..578e200a88d89b356e991b3317ff1e71f25ff75e 100644 +index 69ab1ef4d2a386126009036d4517c69dcaf9a33a..11cafc3e1588cce52b76cc2f09f66b3e451fb087 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -555,7 +555,8 @@ void SetFeatureFlags() { +@@ -543,7 +543,8 @@ void SetFeatureFlags() { void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, const std::string& js_command_line_flags, bool disallow_v8_feature_flag_overrides, @@ -54,7 +54,7 @@ index 51c8cd7cc3f44936e1e33d9dc937d5a68d737dd6..578e200a88d89b356e991b3317ff1e71 static bool v8_is_initialized = false; if (v8_is_initialized) return; -@@ -570,7 +571,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, +@@ -558,7 +559,8 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, } SetFlags(mode, js_command_line_flags); diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index 7b66ab45be6cd..a0f30ca98fb90 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,10 +6,10 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec -index 77707df02c02ba2ffd66ac280d53479993498fff..acc783a27d84465b81b8aef8b6f151a9ad5f0669 100644 +index a0d9ecaa8da4b79fcb920c1d0ff10361aeb75b50..d93cdbd2451ace33a7d4af7771d1ef746fb674ac 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec -@@ -1480,6 +1480,11 @@ +@@ -1500,6 +1500,11 @@ "<(SHARED_INTERMEDIATE_DIR)/third_party/blink/public/strings/permission_element_generated_strings.grd": { "META": {"sizes": {"messages": [2000],}}, "messages": [10080], diff --git a/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch b/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch index e4d74dfbb8ba1..86eddbcccf563 100644 --- a/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch +++ b/patches/chromium/hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch @@ -11,7 +11,7 @@ If removing this patch causes no sync failures, it's safe to delete :+1: Ref https://chromium-review.googlesource.com/c/chromium/src/+/2953903 diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py -index 6602d1f1b2f5e6a4f3a49be2d79264aa1477d3af..26b511e1ec9691a91e13acb8297dc38ecc582313 100755 +index 9cf06fbc79ffdc6cd44cd163e1924bc63c9099f6..944d5622c74e7df32b6ab26807442d80b27cc2bb 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py @@ -304,6 +304,8 @@ def GetDefaultHostOs(): diff --git a/patches/chromium/ignore_rc_check.patch b/patches/chromium/ignore_rc_check.patch index 427c2306315c9..0e92b98969025 100644 --- a/patches/chromium/ignore_rc_check.patch +++ b/patches/chromium/ignore_rc_check.patch @@ -7,10 +7,10 @@ Dont compare RC.exe and RC.py output. FIXME: It has to be reverted once the script is fixed. diff --git a/build/toolchain/win/rc/rc.py b/build/toolchain/win/rc/rc.py -index a650506a1e0a19566861b672cb800024965d7ff5..cde05b15eef3decdee0093ca8b84201008e4adb0 100755 +index ca98984ee20cf4f608182c5ee5926673b436b89c..fc8a2af013f45db6e4e30891270a5f071c783d65 100755 --- a/build/toolchain/win/rc/rc.py +++ b/build/toolchain/win/rc/rc.py -@@ -244,7 +244,10 @@ def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): +@@ -251,7 +251,10 @@ def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): # Assert Microsoft rc.exe and rc.py produced identical .res files. if rc_exe_exit_code == 0: import filecmp diff --git a/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch b/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch index 704fededfb255..36b9ec492ee0f 100644 --- a/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch +++ b/patches/chromium/introduce_ozoneplatform_electron_can_call_x11_property.patch @@ -21,10 +21,10 @@ index 35023e37a34bbcdcfa09cbdad55648f8e0419b37..9a10db499bd0a48feb1c96f87e71af4f properties->supports_global_application_menus = true; properties->app_modal_dialogs_use_event_blocker = true; diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h -index 2f088c0ee697b2525c9770eb8d02e28ae48a85d2..ed8fe0a40537b598695af8aa42c2f0bdc61ab1bb 100644 +index e35bd769c53a544b8a1387f6c25e1ae376643b3d..be7d09d9003011ef4f609f66b8c51a12aa410d80 100644 --- a/ui/ozone/public/ozone_platform.h +++ b/ui/ozone/public/ozone_platform.h -@@ -128,6 +128,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { +@@ -129,6 +129,10 @@ class COMPONENT_EXPORT(OZONE) OzonePlatform { // Linux only: determines if Skia can fall back to the X11 output device. bool skia_can_fall_back_to_x11 = false; diff --git a/patches/chromium/load_v8_snapshot_in_browser_process.patch b/patches/chromium/load_v8_snapshot_in_browser_process.patch index 3df6a60b1c153..ece0963c33e4d 100644 --- a/patches/chromium/load_v8_snapshot_in_browser_process.patch +++ b/patches/chromium/load_v8_snapshot_in_browser_process.patch @@ -9,10 +9,10 @@ but due to the nature of electron, we need to load the v8 snapshot in the browser process. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 82e5123e35509d22493809a37e56be5e9bea4264..054f38d683280638c7ac618d2ff8f7aef1a0def0 100644 +index 4e7e5c5f506144f8d523dc60f5a1ff94de9aad2e..2474e8c608956bcf50e0c7204d728854fe498d31 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -292,11 +292,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { +@@ -293,11 +293,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, const std::string& process_type) { diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 4861e98f1170f..2cebf3e864d80 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,10 +35,10 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index 6bf37c804ddb77a28e788af02bb215970ba343c8..4bf9d4050c44dd155b3455082c8b87afa30e9ac7 100644 +index 70444961a612c3340e8a689f2cf002918be75366..aa055d6e5f9b8a72587cd8c9fbc7203a15352553 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn -@@ -1031,6 +1031,7 @@ component("base") { +@@ -1040,6 +1040,7 @@ component("base") { "//build:ios_buildflags", "//build/config/compiler:compiler_buildflags", "//third_party/modp_b64", @@ -449,7 +449,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec bool shouldShowWindowTitle = YES; if (_bridge) diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -index aa59dfc5c689c6347a4a34fbce46ea549f46dc9d..29fa39513d0273d0a23fb45e627dda14b91c62d4 100644 +index 5c0a53152095c2f799251f78b3c3464504104bfc..2c81ecef35d40036111cbd3dbdb44f7086d8946e 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm @@ -42,6 +42,7 @@ @@ -460,7 +460,7 @@ index aa59dfc5c689c6347a4a34fbce46ea549f46dc9d..29fa39513d0273d0a23fb45e627dda14 #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/cert/x509_util_apple.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" -@@ -677,10 +678,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { +@@ -679,10 +680,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { // this should be treated as an error and caught early. CHECK(bridged_view_); @@ -545,7 +545,7 @@ index dbf334caa3a6d10017b69ad76802e389a011436b..da828823e8195cc9e497866363c9af93 void ForwardKeyboardEvent(const input::NativeWebKeyboardEvent& key_event, diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index b712b8af0e770aa3acbeb1167b1a20bc1547c98a..fdb476a7e470c4b32649d4b3b7e4e44559b5b1c1 100644 +index 4017ee032569466f5311e5c9612c82c086eab935..f2499bc084312a09b2324567d270fc1b899e7617 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -35,6 +35,7 @@ @@ -556,7 +556,7 @@ index b712b8af0e770aa3acbeb1167b1a20bc1547c98a..fdb476a7e470c4b32649d4b3b7e4e445 #include "skia/ext/skia_utils_mac.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/input/input_handler.mojom.h" -@@ -2142,15 +2143,21 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -2136,15 +2137,21 @@ - (NSAccessibilityRole)accessibilityRole { // Since this implementation doesn't have to wait any IPC calls, this doesn't // make any key-typing jank. --hbono 7/23/09 // @@ -579,7 +579,7 @@ index b712b8af0e770aa3acbeb1167b1a20bc1547c98a..fdb476a7e470c4b32649d4b3b7e4e445 return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 4424636d013725023c2bf35529c034adecf823b1..62151d9e640771580ae85231762dfef260b0e4ff 100644 +index e0c49c4984509685acbcb3718bbf04b2909e3f16..5b8ea681dfee77a08bb451ec19f6e5d7ddf5b6d8 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn @@ -339,6 +339,7 @@ source_set("browser") { @@ -591,7 +591,7 @@ index 4424636d013725023c2bf35529c034adecf823b1..62151d9e640771580ae85231762dfef2 public_deps = [ diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h -index d73737088c819274c854db491a7d4d26f9367eb2..b72427517b88e5cb4179dbc551f3f4f321fd8b4e 100644 +index 1b923f0e0dc6d7dc9e67d278b8da00b35745241e..6f7ee79df9b9e3026663e2a4637007de5b5da902 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -24,6 +24,7 @@ @@ -612,7 +612,7 @@ index d73737088c819274c854db491a7d4d26f9367eb2..b72427517b88e5cb4179dbc551f3f4f3 @class RenderWidgetHostViewCocoa; namespace content { -@@ -691,9 +694,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac +@@ -692,9 +695,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // EnsureSurfaceSynchronizedForWebTest(). uint32_t latest_capture_sequence_number_ = 0u; @@ -625,7 +625,7 @@ index d73737088c819274c854db491a7d4d26f9367eb2..b72427517b88e5cb4179dbc551f3f4f3 // Used to force the NSApplication's focused accessibility element to be the // content::BrowserAccessibilityCocoa accessibility tree when the NSView for diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b686f6c8c9 100644 +index 1f9fbdbc7d617adb019fb8a207baad817b5a8a0e..1a726fb4a9e8173dc7da6901eb1632c7f326358e 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -49,6 +49,7 @@ @@ -636,7 +636,7 @@ index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b6 #include "media/base/media_switches.h" #include "skia/ext/platform_canvas.h" #include "skia/ext/skia_utils_mac.h" -@@ -274,8 +275,10 @@ +@@ -275,8 +276,10 @@ void RenderWidgetHostViewMac::MigrateNSViewBridge( remote_cocoa::mojom::Application* remote_cocoa_application, uint64_t parent_ns_view_id) { @@ -647,7 +647,7 @@ index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b6 // Reset `ns_view_` before resetting `remote_ns_view_` to avoid dangling // pointers. `ns_view_` gets reinitialized later in this method. -@@ -1626,8 +1629,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1633,8 +1636,10 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, gfx::NativeViewAccessible RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() { @@ -658,7 +658,7 @@ index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b6 return [GetInProcessNSView() window]; } -@@ -1676,9 +1681,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -1683,9 +1688,11 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, } void RenderWidgetHostViewMac::SetAccessibilityWindow(NSWindow* window) { @@ -670,7 +670,7 @@ index e8c48002df717501128b9a135d5662dfca044e9c..71db145609652dbbe733dbc96d2630b6 } bool RenderWidgetHostViewMac::SyncIsWidgetForMainFrame( -@@ -2205,20 +2212,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, +@@ -2212,20 +2219,26 @@ void CombineTextNodesAndMakeCallback(SpeechCallback callback, void RenderWidgetHostViewMac::GetRenderWidgetAccessibilityToken( GetRenderWidgetAccessibilityTokenCallback callback) { base::ProcessId pid = getpid(); @@ -792,10 +792,10 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn -index 1163dd9e0d25b74fbdc584f742392a4452008724..246c50d5f05ecbf4f57dfc8f3bd68db03fe131f7 100644 +index 2e308872830d0e4105d381dc5bd702942b11de9a..201258c8995de5e8d9f2beadba0f7b34c86bcb6f 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn -@@ -658,6 +658,7 @@ static_library("test_support") { +@@ -660,6 +660,7 @@ static_library("test_support") { "//url", "//url/mojom:url_mojom_gurl", "//v8", @@ -803,7 +803,7 @@ index 1163dd9e0d25b74fbdc584f742392a4452008724..246c50d5f05ecbf4f57dfc8f3bd68db0 ] data_deps = [ -@@ -1111,6 +1112,7 @@ static_library("browsertest_support") { +@@ -1113,6 +1114,7 @@ static_library("browsertest_support") { } configs += [ "//v8:external_startup_data" ] @@ -811,7 +811,7 @@ index 1163dd9e0d25b74fbdc584f742392a4452008724..246c50d5f05ecbf4f57dfc8f3bd68db0 } mojom("content_test_mojo_bindings") { -@@ -1946,6 +1948,7 @@ test("content_browsertests") { +@@ -1950,6 +1952,7 @@ test("content_browsertests") { "//ui/shell_dialogs", "//ui/snapshot", "//ui/webui:test_support", @@ -819,7 +819,7 @@ index 1163dd9e0d25b74fbdc584f742392a4452008724..246c50d5f05ecbf4f57dfc8f3bd68db0 ] if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) { -@@ -3264,6 +3267,7 @@ test("content_unittests") { +@@ -3268,6 +3271,7 @@ test("content_unittests") { "//ui/shell_dialogs:shell_dialogs", "//ui/webui:test_support", "//url", @@ -840,10 +840,10 @@ index 8779cf7cbbe2a583fda51867eaebc627f00bd4cd..98da1587fdc8cc8358a69db2b51d3982 # TODO(crbug.com/40139469): Blink test plugin must be migrated from PPAPI. diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn -index d27d7140cc55a35e6ffb65028265bcb2ae5932d1..1ea92b29c730aba1c667de83bb98ca85f2046470 100644 +index 16407ea4758addb6f6b56dc044d58cde0922897a..98cf8359b412a61b48aa87b607740bbeda4ca159 100644 --- a/device/bluetooth/BUILD.gn +++ b/device/bluetooth/BUILD.gn -@@ -253,6 +253,7 @@ component("bluetooth") { +@@ -257,6 +257,7 @@ component("bluetooth") { "IOKit.framework", "Foundation.framework", ] @@ -1051,10 +1051,10 @@ index 453e2185fc85fcb29fa7af3f94cce5bda8118b0c..1c383675bb9113b5b1df9280b8ee9941 source_set("sandbox_unittests") { diff --git a/sandbox/mac/sandbox_logging.cc b/sandbox/mac/sandbox_logging.cc -index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2bd68c0f9 100644 +index 950cf7cfee4e11766dccf5c0bf3f15a8562f0f1e..a5adaaabdbbd91fedbc4cb679c865bc342536090 100644 --- a/sandbox/mac/sandbox_logging.cc +++ b/sandbox/mac/sandbox_logging.cc -@@ -16,6 +16,7 @@ +@@ -21,6 +21,7 @@ #include #include "build/build_config.h" @@ -1062,7 +1062,7 @@ index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2 #include "sandbox/mac/sandbox_crash_message.h" #if defined(ARCH_CPU_X86_64) -@@ -33,9 +34,11 @@ +@@ -38,9 +39,11 @@ } #endif @@ -1074,7 +1074,7 @@ index 095c639b9893e885d8937e29ed7d47a7c28bc6b6..7e0cf9b9f94b16741358bdb45122f8b2 namespace sandbox::logging { -@@ -76,9 +79,11 @@ void SendOsLog(Level level, const char* message) { +@@ -81,9 +84,11 @@ void SendOsLog(Level level, const char* message) { sandbox::crash_message::SetCrashMessage(message); } @@ -1579,10 +1579,10 @@ index dcf493d62990018040a3f84b6f875af737bd2214..3d1c4dcc9ee0bbfdac15f40d9c74e9f3 void DisplayCALayerTree::GotIOSurfaceFrame( diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn -index 8b25953be73da43fa2e0b5957569ae481dc6a082..7f9175e7eb67ef27fe110ee72f5e9c885018c2ac 100644 +index 6207f4941839fdc5a44dc5fe61f50281503623d4..d2d635c1bd2e0497b7fbab331b48e753fae1013e 100644 --- a/ui/accessibility/platform/BUILD.gn +++ b/ui/accessibility/platform/BUILD.gn -@@ -288,6 +288,7 @@ component("platform") { +@@ -295,6 +295,7 @@ component("platform") { "AppKit.framework", "Foundation.framework", ] @@ -1591,7 +1591,7 @@ index 8b25953be73da43fa2e0b5957569ae481dc6a082..7f9175e7eb67ef27fe110ee72f5e9c88 if (is_ios) { diff --git a/ui/accessibility/platform/browser_accessibility_manager_mac.mm b/ui/accessibility/platform/browser_accessibility_manager_mac.mm -index c642b2ce1e19a48bc00822c429d833d5d45c4f98..ebe0b80b142a997aaf928fa72a2ca71492d1a54e 100644 +index 71c78a4d3350b6a01cd1f8321f0c1faebc829b0f..79c54d9d5c1b335b3877199a64c6d3f940c5a85b 100644 --- a/ui/accessibility/platform/browser_accessibility_manager_mac.mm +++ b/ui/accessibility/platform/browser_accessibility_manager_mac.mm @@ -13,6 +13,7 @@ @@ -1673,10 +1673,10 @@ index c8171f0527fe5194f0ea73b57c4444d4c630fbc4..c2ac4da580e3e7f749a0a4de1e859af6 // Accessible object if (AXElementWrapper::IsValidElement(value)) { diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn -index 4b91bad93384b435bed2e8da5e185603fb0a66a3..d68202e3fe92d01b148bf494a3599e2a8d8ced91 100644 +index a89628087cf9e38a95314972a4d2c38e39176005..2e05b5a472f36d3bff8769cde514820a3e622f4b 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn -@@ -363,6 +363,13 @@ component("base") { +@@ -365,6 +365,13 @@ component("base") { ] } @@ -1690,7 +1690,7 @@ index 4b91bad93384b435bed2e8da5e185603fb0a66a3..d68202e3fe92d01b148bf494a3599e2a if (is_ios) { sources += [ "device_form_factor_ios.mm", -@@ -516,6 +523,12 @@ component("base") { +@@ -518,6 +525,12 @@ component("base") { "//url", ] @@ -1796,10 +1796,10 @@ index fc25ba79d2b0e1acdb7ba54b89e7d6e16f94771b..de771ef414b9a69e331261524f08e9a1 } // namespace diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn -index eea9eb92fadfcb72adfffffb0e43082a79bcf3a7..dd2110c1e9393f7ad33a0e64c09ef732b3292a54 100644 +index e598004da50257f4ee37a6431222c2b9e9f24b62..3f2ea7a469d6580d25fe51b657555d77a9ce9b80 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn -@@ -125,6 +125,12 @@ component("display") { +@@ -127,6 +127,12 @@ component("display") { "//ui/gfx/geometry", ] @@ -1813,7 +1813,7 @@ index eea9eb92fadfcb72adfffffb0e43082a79bcf3a7..dd2110c1e9393f7ad33a0e64c09ef732 deps += [ "//build:ios_buildflags" ] } diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm -index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab64684661a01d 100644 +index 874ac9d572931fe175ccab8beb7738fe0a7b3c1b..b70e2a8a7be9e00a379f47c77589dde6b8b1f081 100644 --- a/ui/display/mac/screen_mac.mm +++ b/ui/display/mac/screen_mac.mm @@ -30,6 +30,7 @@ @@ -1824,7 +1824,7 @@ index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab6468 #include "ui/display/display.h" #include "ui/display/display_change_notifier.h" #include "ui/display/util/display_util.h" -@@ -176,7 +177,17 @@ DisplayMac BuildDisplayForScreen(NSScreen* screen) { +@@ -177,7 +178,17 @@ DisplayMac BuildDisplayForScreen(NSScreen* screen) { display.set_color_depth(Display::kDefaultBitsPerPixel); display.set_depth_per_component(Display::kDefaultBitsPerComponent); } @@ -1843,10 +1843,10 @@ index 29ae2da6a8a2c2a612dfb92f7f9c03ca5fa306b1..440c139a32a0c205e77b657d4aab6468 // Query the display's refresh rate. if (@available(macos 12.0, *)) { diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn -index cb507971bef962e91cd8fca696692194696d1142..d2aee3962e3377980940e6d3538487a566a609b4 100644 +index 428ba0136f09ace2652e96b0928f84b59048e4ba..9ab318c4f56728eb675d8faa3a17a29f7079905c 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn -@@ -334,6 +334,12 @@ component("gfx") { +@@ -335,6 +335,12 @@ component("gfx") { "//ui/base:ui_data_pack", ] @@ -1898,10 +1898,10 @@ index fe3f85073e31de487a08e57d7f9b07aa4eccf8f3..cf5b07203c8bd559a404600cc98cc8ec // enough. return PlatformFontMac::SystemFontType::kGeneral; diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn -index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a125fdae0eb 100644 +index f24cdf1484910c6fdc706aefc22c97669e245e57..efcd1779c0f105510c298ac9778792cfc8108847 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn -@@ -720,6 +720,8 @@ component("views") { +@@ -721,6 +721,8 @@ component("views") { "IOSurface.framework", "QuartzCore.framework", ] @@ -1910,7 +1910,7 @@ index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a12 } if (is_win) { -@@ -1142,6 +1144,8 @@ source_set("test_support") { +@@ -1148,6 +1150,8 @@ source_set("test_support") { "//ui/base/mojom:ui_base_types", ] @@ -1920,7 +1920,7 @@ index 0f9f25edf65eaee5191ac1f6b70bd43a25091578..e6cce5c607e437c8a9d0fe43eb472a12 sources += [ "test/desktop_window_tree_host_win_test_api.cc", diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h -index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e8d0ea7c0 100644 +index cbd0b7708a7afd39852a7f75a21173d5b8a71a5e..1e8d5b3f66992a6bcbecd7149c45982319076d0c 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.h +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h @@ -18,6 +18,7 @@ @@ -1941,7 +1941,7 @@ index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e @class NSView; namespace remote_cocoa { -@@ -487,10 +490,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost +@@ -489,10 +492,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost mojo::AssociatedRemote remote_ns_window_remote_; @@ -1955,7 +1955,7 @@ index 6a18c94e8c9d531ab3a59faf6027848caed8de57..63aa6b3ba824a3fa71c5a3db94b9ac5e // Used to force the NSApplication's focused accessibility element to be the // views::Views accessibility tree when the NSView for this is focused. diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm -index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb490c46a081 100644 +index edf13478844a9686cfbfbd4d96214044c03134b4..7bda1853d47034c80a4e416b9839e8d18c5a8e2c 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm @@ -21,6 +21,7 @@ @@ -1966,7 +1966,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #include "ui/accessibility/accessibility_features.h" -@@ -352,7 +353,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -357,7 +358,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, if (in_process_ns_window_bridge_) { return in_process_ns_window_bridge_->ns_view(); } @@ -1978,7 +1978,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 } gfx::NativeViewAccessible -@@ -367,7 +372,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -372,7 +377,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, return [in_process_ns_window_bridge_->ns_view() window]; } @@ -1990,7 +1990,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 } remote_cocoa::mojom::NativeWidgetNSWindow* -@@ -1371,9 +1380,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1384,9 +1393,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator, // for PWAs. However this breaks accessibility on in-process windows, // so set it back to NO when a local window gains focus. See // https://crbug.com/41485830. @@ -2002,7 +2002,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 // Explicitly set the keyboard accessibility state on regaining key // window status. if (is_key && is_content_first_responder) { -@@ -1514,17 +1525,20 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1527,17 +1538,20 @@ void HandleAccelerator(const ui::Accelerator& accelerator, void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens( const std::vector& window_token, const std::vector& view_token) { @@ -2023,7 +2023,7 @@ index 7e5047fabe59fb2773f13836145cfce64bc1a135..fed3d6a70139443d76ce6181df69bb49 *pid = getpid(); id element_id = GetNativeViewAccessible(); -@@ -1537,6 +1551,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator, +@@ -1550,6 +1564,7 @@ void HandleAccelerator(const ui::Accelerator& accelerator, } *token = ui::RemoteAccessibility::GetTokenForLocalElement(element_id); diff --git a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch index fd07f9159c316..b79aaef2ab0ae 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -7,7 +7,7 @@ This adds a callback from the network service that's used to implement session.setCertificateVerifyCallback. diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index b9a14ef983f6c985fefeee342eddfdf3f0a24a84..b1df0a09a9bfd226ffe7b37144e6599b099e619e 100644 +index 8014bb16d8108115f901346fa4ed4d84220d86c5..ee56aab6831be256390b88e65a2a949c591bbd31 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -159,6 +159,11 @@ @@ -178,7 +178,7 @@ index f2dcec57a22d95892a08f1fa43696d6eea46a820..930e0bd987c48d111b2c8d71147c09e4 std::unique_ptr internal_host_resolver_; std::set, base::UniquePtrComparator> diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 0f053267c23795be6bb0e75d10a92e1777532484..cf4d2dcf86b6536c37d46875f74517e478b34928 100644 +index a9c82bcf9a4ebf946791eccc35a0dcfff0c18288..6d79f4a4cb1a40be907ef52e35f0e5a0acab31fb 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom @@ -308,6 +308,17 @@ struct SocketBrokerRemotes { @@ -199,7 +199,7 @@ index 0f053267c23795be6bb0e75d10a92e1777532484..cf4d2dcf86b6536c37d46875f74517e4 // Parameters for constructing a network context. struct NetworkContextParams { // The user agent string. -@@ -945,6 +956,9 @@ interface NetworkContext { +@@ -952,6 +963,9 @@ interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote client); diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index 49a7656aeac03..407a93b96c0eb 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -7,7 +7,7 @@ Pass RenderFrameHost through to PlatformNotificationService so Electron can identify which renderer a notification came from. diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc -index 8dd86234b4520c420c5d5a4e5f69d67e27734762..3f50bb8fe51a9a46f5d8b08ddd9c40d45b8cffff 100644 +index eedafc323a9e4f736aa637d76034f4cb5239eee8..c562a59998901aaedc05be694a725150feeb4b0a 100644 --- a/chrome/browser/notifications/platform_notification_service_impl.cc +++ b/chrome/browser/notifications/platform_notification_service_impl.cc @@ -244,6 +244,7 @@ bool PlatformNotificationServiceImpl::WasClosedProgrammatically( @@ -133,10 +133,10 @@ index 05d3a12dd84c7005d46cc73b312f97ef418d96f5..4765de982802541b3efc7211d106acc7 const GURL& document_url, const WeakDocumentPtr& weak_document_ptr, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index a8e3f365d2e9c72f9ae46388dc5b12ad57887b6b..bf18683a08e1c144b360f904ab65adcab66aa607 100644 +index 6e46bfbd8ed2d9842f3ba7b82aa8e6eac1c4c12a..28e952d9766566e0a5213492aaf606aa56dc23f6 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -2146,7 +2146,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2206,7 +2206,7 @@ void RenderProcessHostImpl::CreateNotificationService( case RenderProcessHost::NotificationServiceCreatorType::kSharedWorker: case RenderProcessHost::NotificationServiceCreatorType::kDedicatedWorker: { storage_partition_impl_->GetPlatformNotificationContext()->CreateService( @@ -145,7 +145,7 @@ index a8e3f365d2e9c72f9ae46388dc5b12ad57887b6b..bf18683a08e1c144b360f904ab65adca creator_type, std::move(receiver)); break; } -@@ -2154,7 +2154,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2214,7 +2214,7 @@ void RenderProcessHostImpl::CreateNotificationService( CHECK(rfh); storage_partition_impl_->GetPlatformNotificationContext()->CreateService( diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 755e24fb055b1..d61f71763f6ef 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,10 +10,10 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 5de5748662c957f5b0e671057b827e2eb2224893..72ebb0e18dbc8c678ff93c7a3c028d7fb362263f 100644 +index 314ef160662faf6d7c00d9d6999abb1db8bbebe5..6cae6b280bfab3f1cbacbb4a4d71fa1d78b54619 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -806,8 +806,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( +@@ -808,8 +808,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( // TODO(crbug.com/40092527): Consider adding a separate boolean that // tracks this instead of piggybacking `origin_calculation_debug_info`. if (renderer_side_origin.opaque() && diff --git a/patches/chromium/picture-in-picture.patch b/patches/chromium/picture-in-picture.patch index c39dc3bb41374..f294c6286406d 100644 --- a/patches/chromium/picture-in-picture.patch +++ b/patches/chromium/picture-in-picture.patch @@ -38,7 +38,7 @@ index 8168b4cfbafd42fa93a5aa9a3691c2552fabfb86..ba49212bd76d209f99c1cee649fc1466 ui::ImageModel::FromVectorIcon(*icon, kColorPipWindowForeground, kCloseButtonIconSize)); diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index 6671f304fc15221d18fa1be2c4f20368db518e59..f9e29227fe9396747bed8b1ec970cfdfcf72c03c 100644 +index f246f7ea6b39a881721171515049dda8d3f2ac0b..e8524bb6706ae06a0feabccbe44250580098e937 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -18,9 +18,11 @@ diff --git a/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch b/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch index 3c8b864170a3e..d25f6fc465f9d 100644 --- a/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch +++ b/patches/chromium/port_autofill_colors_to_the_color_pipeline.patch @@ -8,10 +8,10 @@ needed in chromium but our autofill implementation uses them. This patch can be our autofill implementation to work like Chromium's. diff --git a/ui/color/color_id.h b/ui/color/color_id.h -index 4521cef41d441fcf6bd7889243598cab40d3d642..3dd279e6c35387e1f40c389d7dfccfa5497155e8 100644 +index 8b7cc48c3fad34d91f8732c760228b546ce24a89..cf61dcb09d859729c9f1239b8278babbc057a74e 100644 --- a/ui/color/color_id.h +++ b/ui/color/color_id.h -@@ -405,6 +405,10 @@ +@@ -407,6 +407,10 @@ E_CPONLY(kColorRadioButtonForegroundUnchecked) \ E_CPONLY(kColorRadioButtonForegroundDisabled) \ E_CPONLY(kColorRadioButtonForegroundChecked) \ @@ -22,7 +22,7 @@ index 4521cef41d441fcf6bd7889243598cab40d3d642..3dd279e6c35387e1f40c389d7dfccfa5 E_CPONLY(kColorSegmentedButtonBorder) \ E_CPONLY(kColorSegmentedButtonFocus) \ E_CPONLY(kColorSegmentedButtonForegroundChecked) \ -@@ -513,6 +517,7 @@ +@@ -515,6 +519,7 @@ E_CPONLY(kColorTreeNodeForeground) \ E_CPONLY(kColorTreeNodeForegroundSelectedFocused) \ E_CPONLY(kColorTreeNodeForegroundSelectedUnfocused) \ diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index fe0c5162f7f29..8dada6327322e 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -577,10 +577,10 @@ index 1917f8b94962d7a4c83f139623a5f5d352011627..47ef610c43c4dcfee0cf528eb2e6075b // Indication that the job is getting canceled. bool canceling_job_ = false; diff --git a/chrome/browser/printing/printer_query.cc b/chrome/browser/printing/printer_query.cc -index 402be34ab888cdf834d0fb65de0832e9a8021ced..82ddc92a35d824926c30279e660cc4e86e6f0b09 100644 +index 4f2e70c243550b471d0a6b2be743a29004c75599..9a3580d7c2a01290e7aaff1200e7f17c9add2c70 100644 --- a/chrome/browser/printing/printer_query.cc +++ b/chrome/browser/printing/printer_query.cc -@@ -355,17 +355,19 @@ void PrinterQuery::UpdatePrintSettings(base::Value::Dict new_settings, +@@ -356,17 +356,19 @@ void PrinterQuery::UpdatePrintSettings(base::Value::Dict new_settings, #endif // BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS) } @@ -881,10 +881,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 62151d9e640771580ae85231762dfef260b0e4ff..52555181f779772dff8a471c4389af8128fd7a6a 100644 +index 5b8ea681dfee77a08bb451ec19f6e5d7ddf5b6d8..15ad186308981ddfbbe32f47ef9127ab051132e3 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3129,8 +3129,9 @@ source_set("browser") { +@@ -3134,8 +3134,9 @@ source_set("browser") { "//ppapi/shared_impl", ] @@ -897,10 +897,10 @@ index 62151d9e640771580ae85231762dfef260b0e4ff..52555181f779772dff8a471c4389af81 if (is_chromeos) { sources += [ diff --git a/printing/printing_context.cc b/printing/printing_context.cc -index 0bb34c4715224a0cef10465778db946f299ac513..c70803a5737676eec498dd18074a47ffabac62c0 100644 +index b959c1428784388f514d33bf54550908f60f3ff1..c37bc40570b1e2b29af6e677684705556b15560e 100644 --- a/printing/printing_context.cc +++ b/printing/printing_context.cc -@@ -154,7 +154,6 @@ void PrintingContext::UsePdfSettings() { +@@ -156,7 +156,6 @@ void PrintingContext::UsePdfSettings() { mojom::ResultCode PrintingContext::UpdatePrintSettings( base::Value::Dict job_settings) { diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index 1e0cb6d3513f6..d571ca89d8840 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -8,7 +8,7 @@ Chrome moved the SetCursor IPC message to mojo, which we use to tell OSR about ` Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2172779 diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h -index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9adc9ec9abc 100644 +index a54a0dedf8ef1cfffa4e80a4707debed0e83d277..e66e71fdbabb40a5307b12cd8965e773e76c04fd 100644 --- a/content/browser/renderer_host/render_widget_host_delegate.h +++ b/content/browser/renderer_host/render_widget_host_delegate.h @@ -28,6 +28,7 @@ @@ -30,10 +30,10 @@ index 8ad5a5042355ce918ab13784fbc0d633b6f0efa9..7f7b86abf3e18501025a854000f0d9ad // RenderWidgetHost on the primary main frame, and false otherwise. virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index a409c64768968c81be3b6c7e7646f8df26e2afe6..7c31b82e2903507bb69aaa71fc0ed51cad06d1c2 100644 +index 20fcda4eb20459b69247003c51c2a3ed37c7b1e8..9bcab4e1e8a0fa429488555f4f7bd1c54888d10e 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc -@@ -2048,6 +2048,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { +@@ -2056,6 +2056,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) { if (view_) { view_->UpdateCursor(cursor); } @@ -44,10 +44,10 @@ index a409c64768968c81be3b6c7e7646f8df26e2afe6..7c31b82e2903507bb69aaa71fc0ed51c void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index e500fe77f55836b3e5832536f98cf9581984b8c1..952fd20f71560acd89c74f08e9d8cdbf34fb5a1a 100644 +index d7bf74c206cedc628d0b92fff25c29393f6c9d95..30593ec9ecd2b135a6054ed50e95c849c09b47bd 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5810,6 +5810,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { +@@ -5839,6 +5839,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { return text_input_manager_.get(); } @@ -60,10 +60,10 @@ index e500fe77f55836b3e5832536f98cf9581984b8c1..952fd20f71560acd89c74f08e9d8cdbf RenderWidgetHostImpl* render_widget_host) { return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index 550beed4beda751159a82b94687e05018c0ade54..1e606e88aa5a44b06acf1f421556331346928e08 100644 +index a35dc028fbcf202a7ba1aa7213f8c815e9a31e3f..fe3b8cf40ea7c26aade59c7224416594509f6308 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h -@@ -1177,6 +1177,7 @@ class CONTENT_EXPORT WebContentsImpl +@@ -1178,6 +1178,7 @@ class CONTENT_EXPORT WebContentsImpl void SendScreenRects() override; void SendActiveState(bool active) override; TextInputManager* GetTextInputManager() override; diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index 5623dfa4c34b3..bf60e0c2a1249 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 4325cdfd256ae7a1008e073d42da995b82df5bba..b895fb34f7fe7d48613a972dc29039c7d9c26987 100644 +index 2a17aa6a3f687d60a7ca0e839e59f637819a9376..95a714ca4a0023dc31b212611f91fc1d03543656 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9909,25 +9909,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9971,25 +9971,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/render_widget_host_view_base.patch b/patches/chromium/render_widget_host_view_base.patch index 20ddb8a97889b..2ef6f6e7fd7a5 100644 --- a/patches/chromium/render_widget_host_view_base.patch +++ b/patches/chromium/render_widget_host_view_base.patch @@ -24,7 +24,7 @@ index ce00b0540a7ac7f7c7b4c65f1a1343f72ae21c42..cc3b694431f14b166a305a446a48c25d const blink::WebMouseEvent& event, const ui::LatencyInfo& latency) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h -index 568a87065acb56faf3f91e35b4e9ad2782edbe28..90a8f27e5d240c7d88a314c580b459d1090b1d25 100644 +index ee54e91c031849307e51905fbdcdf53c5f17bf8f..72e12f6910e581c3a6f19241523322dcb6959c61 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -30,6 +30,8 @@ diff --git a/patches/chromium/render_widget_host_view_mac.patch b/patches/chromium/render_widget_host_view_mac.patch index d9584e8426801..3d7f24d1e77fa 100644 --- a/patches/chromium/render_widget_host_view_mac.patch +++ b/patches/chromium/render_widget_host_view_mac.patch @@ -8,10 +8,10 @@ respond to the first mouse click in their window, which is desirable for some kinds of utility windows. Similarly for `disableAutoHideCursor`. diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index f70d59fce833ca4daf81d164e5ca9fd39f2520f7..b712b8af0e770aa3acbeb1167b1a20bc1547c98a 100644 +index 22ee05153569d0db7cbc7ab520944e84b9475c8e..4017ee032569466f5311e5c9612c82c086eab935 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -@@ -169,6 +169,15 @@ void ExtractUnderlines(NSAttributedString* string, +@@ -170,6 +170,15 @@ void ExtractUnderlines(NSAttributedString* string, } // namespace @@ -27,7 +27,7 @@ index f70d59fce833ca4daf81d164e5ca9fd39f2520f7..b712b8af0e770aa3acbeb1167b1a20bc // RenderWidgetHostViewCocoa --------------------------------------------------- // Private methods: -@@ -780,6 +789,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { +@@ -774,6 +783,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { } - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { @@ -37,7 +37,7 @@ index f70d59fce833ca4daf81d164e5ca9fd39f2520f7..b712b8af0e770aa3acbeb1167b1a20bc // Enable "click-through" if mouse clicks are accepted in inactive windows return [self acceptsMouseEventsOption] > kAcceptMouseEventsInActiveWindow; } -@@ -925,6 +937,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { +@@ -919,6 +931,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { // its parent view. BOOL hitSelf = NO; while (view) { @@ -48,7 +48,7 @@ index f70d59fce833ca4daf81d164e5ca9fd39f2520f7..b712b8af0e770aa3acbeb1167b1a20bc if (view == self) hitSelf = YES; if ([view isKindOfClass:[self class]] && ![view isEqual:self] && -@@ -1259,6 +1275,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { +@@ -1253,6 +1269,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { eventType == NSEventTypeKeyDown && !(modifierFlags & NSEventModifierFlagCommand); diff --git a/patches/chromium/resource_file_conflict.patch b/patches/chromium/resource_file_conflict.patch index 4bcf3eaf5215d..e5d394a5c6e66 100644 --- a/patches/chromium/resource_file_conflict.patch +++ b/patches/chromium/resource_file_conflict.patch @@ -52,10 +52,10 @@ Some alternatives to this patch: None of these options seems like a substantial maintainability win over this patch to me (@nornagon). diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index 9e0dca945ef9a9a0209c84ab6cf5e1cda352257e..b38442f018b218944c7b85c9f8bd8b8eb6137b9e 100644 +index 26ec2b8abc6783a48518c1b15a12eb2212b0db7a..95c73dcb082999d0a85d82f1fe330d27c88055ca 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -1549,7 +1549,7 @@ if (is_chrome_branded && !is_android) { +@@ -1550,7 +1550,7 @@ if (is_chrome_branded && !is_android) { } } @@ -64,7 +64,7 @@ index 9e0dca945ef9a9a0209c84ab6cf5e1cda352257e..b38442f018b218944c7b85c9f8bd8b8e chrome_paks("packed_resources") { if (is_mac) { output_dir = "$root_gen_dir/repack" -@@ -1595,6 +1595,12 @@ repack("browser_tests_pak") { +@@ -1596,6 +1596,12 @@ repack("browser_tests_pak") { deps = [ "//chrome/test/data/webui:resources" ] } diff --git a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch index 29fc4d520e3ec..a68eb28410936 100644 --- a/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch +++ b/patches/chromium/revert_code_health_clean_up_stale_macwebcontentsocclusion.patch @@ -233,7 +233,7 @@ index 2991489fae8a4eecad97b1ecb2271f096d9a9229..93b7aa620ad1da250ac06e3383ca6897 } diff --git a/content/common/features.cc b/content/common/features.cc -index 31484e57bd0989af7a2e9584bbd430cdfa713346..fdb5e8f5f395b128c1c5300b94a50f693d6b52e1 100644 +index 136290cb51f7b44cc6f8754c49203a7bad1634e8..d11fd0742b10e6318a0d3740759f474b41675ce6 100644 --- a/content/common/features.cc +++ b/content/common/features.cc @@ -261,6 +261,14 @@ BASE_FEATURE(kIOSurfaceCapturer, @@ -252,7 +252,7 @@ index 31484e57bd0989af7a2e9584bbd430cdfa713346..fdb5e8f5f395b128c1c5300b94a50f69 // invalidated upon notifications sent by base::SystemMonitor. If disabled, the // cache is considered invalid on every enumeration request. diff --git a/content/common/features.h b/content/common/features.h -index 906c0da9313ac0272f5e5a79ef797de596804cfb..94b63dfe1235b7643e29926edd2c27c447302b35 100644 +index dc481abb8ef01c8e5a23d0f683a59c2ee6420826..7f61079290ba740cdad7b74feeb94143bdcc6bd4 100644 --- a/content/common/features.h +++ b/content/common/features.h @@ -68,6 +68,9 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kInterestGroupUpdateIfOlderThan); diff --git a/patches/chromium/revert_enable_crel_for_arm32_targets.patch b/patches/chromium/revert_enable_crel_for_arm32_targets.patch new file mode 100644 index 0000000000000..08f5f6afbb8cb --- /dev/null +++ b/patches/chromium/revert_enable_crel_for_arm32_targets.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Maddock +Date: Fri, 28 Mar 2025 20:22:26 -0400 +Subject: revert: Enable CREL for arm32 targets + +Enabling CREL on Linux ARM64 seems to cause it to segfault. Disable for Electron +as its one of our supported platforms. +https://chromium-review.googlesource.com/q/I3a62f02f564f07be63173b0773b4ecaffbe939b9 + +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 6cbc6b8cfae044c36e83cc54c23dc500b445e5da..1d9798b5bffb4fb3166b72911cea5ba1282a757f 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -628,7 +628,8 @@ config("compiler") { + + # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF + # (excluding toolchains that use an older version of LLVM). +- if (is_linux && !llvm_android_mainline && ++ # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. ++ if (is_linux && !llvm_android_mainline && current_cpu != "arm" && + default_toolchain != "//build/toolchain/cros:target") { + cflags += [ "-Wa,--crel,--allow-experimental-crel" ] + } diff --git a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch index b057e5375c917..5c78fa8c6b9f1 100644 --- a/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch +++ b/patches/chromium/revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch @@ -6,7 +6,7 @@ Subject: Revert "Remove the AllowAggressiveThrottlingWithWebSocket feature." This reverts commit 615c1810a187840ffeb04096087efff86edb37de. diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc -index 97325e06385105ec75d354002c6a147d5dff7218..d5b18bbcc9d2c46c6c2ebae1b36f4f1327b27b2c 100644 +index d7f5c10b6f82d2dcebc7a1ee11ef05757c3fa7ad..b37b02aaddf524b427086aa20c8140a22097a738 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc @@ -99,6 +99,17 @@ enum WebSocketOpCode { diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index 181389e7367f2..d60a15c9a8bb2 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,10 +6,10 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index e5701fe6ddcf8b9d846373d331e91e705870e523..a64b6d00637a177fd4f6d66b236bed9b0d8d4d8c 100644 +index f1ef5e0d6c1c659506656df5f98a8637729009a0..5f2e9766fe7e8a0ced2a15af7f8fcd529c02c1bb 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1310,7 +1310,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { +@@ -1324,7 +1324,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { } bool RenderThreadImpl::IsElasticOverscrollEnabled() { diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index f8a580caef7f6..05a9c39728926 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,10 +22,10 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index bf18683a08e1c144b360f904ab65adcab66aa607..829a0c2f09490099bb58ece6a41995e3fe3d6c81 100644 +index 28e952d9766566e0a5213492aaf606aa56dc23f6..c0d553dcbb37db7657c2374d4a6b978d7bfee88c 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -1754,6 +1754,10 @@ bool RenderProcessHostImpl::Init() { +@@ -1814,6 +1814,10 @@ bool RenderProcessHostImpl::Init() { std::unique_ptr sandbox_delegate = std::make_unique( *cmd_line, IsPdf(), IsJitDisabled()); diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index a9064eadbde0c..3c76a5553a504 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,10 +9,10 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 59b05937ceb1b81b69d913f587150021b9031106..97512399db631236afd1aeafb1ecee97cb754f60 100644 +index b6870d74a761a847e9952d409b37ddb778790b08..052e518e42c7c2d7f0e17ebc8dc4e8ec78215802 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -3882,6 +3882,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3908,6 +3908,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, params.main_frame_name, GetOpener(), primary_main_frame_policy, base::UnguessableToken::Create()); @@ -26,7 +26,7 @@ index 59b05937ceb1b81b69d913f587150021b9031106..97512399db631236afd1aeafb1ecee97 std::unique_ptr delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -3892,6 +3899,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3918,6 +3925,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, view_ = CreateWebContentsView(this, std::move(delegate), &render_view_host_delegate_view_); } diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index d3bab20f97d4d..5702e57110afd 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 27db5dbfd05106788feda9daab9d1aaaf1ae920f..5de5748662c957f5b0e671057b827e2eb2224893 100644 +index ea6ba910234659d1213b1f3f624da0f49b851725..314ef160662faf6d7c00d9d6999abb1db8bbebe5 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8745,6 +8745,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8748,6 +8748,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,10 +37,10 @@ index 27db5dbfd05106788feda9daab9d1aaaf1ae920f..5de5748662c957f5b0e671057b827e2e if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 97512399db631236afd1aeafb1ecee97cb754f60..c3b563b0b727bc35f6d4499c589110644ebe9cd1 100644 +index 052e518e42c7c2d7f0e17ebc8dc4e8ec78215802..1e6ee8c65d56f2821485f855bbf4e8bb8212058c 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4157,21 +4157,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( +@@ -4183,21 +4183,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"), "WebContentsImpl::PreHandleKeyboardEvent"); @@ -78,7 +78,7 @@ index 97512399db631236afd1aeafb1ecee97cb754f60..c3b563b0b727bc35f6d4499c58911064 } bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { -@@ -4330,7 +4334,7 @@ void WebContentsImpl::EnterFullscreenMode( +@@ -4356,7 +4360,7 @@ void WebContentsImpl::EnterFullscreenMode( OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode"); DCHECK(CanEnterFullscreenMode(requesting_frame)); DCHECK(requesting_frame->IsActive()); diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index ac939d791b229..81333a7cd013f 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -26,10 +26,10 @@ index 7a2d251ba2d13d0a34df176111e6524a27b87f55..cbbe0fbdd25a0f7859b113fdb3dcd9ce // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index d912ff1b05e9fe5c4d8edf1f681fc824b6f2d38a..92c28a027ebd10a3bb8a864231b237908fbf7394 100644 +index ac71ba108e5085aadc3e7f22e7bf690c38870f2b..d0c7bd3bf1c20e505548c7146ab71f2af2e57b43 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -906,6 +906,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -903,6 +903,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } @@ -43,10 +43,10 @@ index d912ff1b05e9fe5c4d8edf1f681fc824b6f2d38a..92c28a027ebd10a3bb8a864231b23790 const v8::Local& worker) { GetContentClient()->renderer()->DidInitializeWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index b6d525fb400ae27e04967c9b1b197e01f4a21123..5daf30bfb5cfc27148aa5f0d784ee8348c577dbf 100644 +index a3aaa495c754de62dc1970eebd9bba80bdbfb869..a5c668a7fd8426eb409b6577cf1b6ce5d24b9fde 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -198,6 +198,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -199,6 +199,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -55,10 +55,10 @@ index b6d525fb400ae27e04967c9b1b197e01f4a21123..5daf30bfb5cfc27148aa5f0d784ee834 const blink::WebSecurityOrigin& script_origin) override; blink::ProtocolHandlerSecurityLevel GetProtocolHandlerSecurityLevel( diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index 1e84e382f1f94607384e0a800f4a96daf5e8ba75..d19743d2d2a512a984d831f437adced81ae912dd 100644 +index 336f771d0587751bd826c1288b4f77b2d672f95b..6a843c144b8f602e8232b2cae7954824c4ad6bc3 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -676,6 +676,7 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -677,6 +677,7 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} @@ -67,10 +67,10 @@ index 1e84e382f1f94607384e0a800f4a96daf5e8ba75..d19743d2d2a512a984d831f437adced8 const WebSecurityOrigin& script_origin) { return false; diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc -index 6e0a669c741753e784fe8a967b1deb125b02d0bf..2449348f956f81845bf314558fa5b7268500adeb 100644 +index 00c8757098f9959f78a0a7383cf1197f88ae14aa..9b2878bc23c78f092816524608776dd32fbde5a1 100644 --- a/third_party/blink/renderer/core/workers/worker_thread.cc +++ b/third_party/blink/renderer/core/workers/worker_thread.cc -@@ -762,6 +762,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { +@@ -751,6 +751,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { } pause_handle_.reset(); diff --git a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch index aa28ee4c6fd73..fe7a511bf53aa 100644 --- a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch +++ b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch @@ -35,10 +35,10 @@ index cbbe0fbdd25a0f7859b113fdb3dcd9ce57e597d6..1345bb5008e1b4fc3a450f7e353d52ec // from the worker thread. virtual void WillDestroyWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 92c28a027ebd10a3bb8a864231b237908fbf7394..21ac02b10b698b943feb982743c09d4f1107c437 100644 +index d0c7bd3bf1c20e505548c7146ab71f2af2e57b43..020a2cc8125eea8a017950fdcdec4a2810201e21 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -918,6 +918,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( +@@ -915,6 +915,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( worker); } @@ -52,10 +52,10 @@ index 92c28a027ebd10a3bb8a864231b237908fbf7394..21ac02b10b698b943feb982743c09d4f const blink::WebSecurityOrigin& script_origin) { return GetContentClient()->renderer()->AllowScriptExtensionForServiceWorker( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index 5daf30bfb5cfc27148aa5f0d784ee8348c577dbf..182d574f14a679707e824e82bb4feeb4ee68de90 100644 +index a5c668a7fd8426eb409b6577cf1b6ce5d24b9fde..a53bfe2f7566828433c3c6c8e95cbd12cd6189a9 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -198,6 +198,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -199,6 +199,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -65,10 +65,10 @@ index 5daf30bfb5cfc27148aa5f0d784ee8348c577dbf..182d574f14a679707e824e82bb4feeb4 bool AllowScriptExtensionForServiceWorker( const blink::WebSecurityOrigin& script_origin) override; diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index d19743d2d2a512a984d831f437adced81ae912dd..78267456cf2bccc4bc1120ea8107c950ead24c89 100644 +index 6a843c144b8f602e8232b2cae7954824c4ad6bc3..ede4908eaad7f1c22fac0fe5071189c50fb5d71a 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -676,6 +676,8 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -677,6 +677,8 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} diff --git a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch index dcbe48b7fd685..e4a94ceeffcff 100644 --- a/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch +++ b/patches/devtools_frontend/chore_expose_ui_to_allow_electron_to_set_dock_side.patch @@ -10,10 +10,10 @@ to handle this without patching, but this is fairly clean for now and no longer patching legacy devtools code. diff --git a/front_end/entrypoints/main/MainImpl.ts b/front_end/entrypoints/main/MainImpl.ts -index 75d54b60cb265eef90a493295dd9d495f8327b7c..2d98c8470115b70bda7d728ff4c00d2282159913 100644 +index ab07d9b54f6bc8183a151feabf094eaaf1c64306..b3300b1d49b816013f3ca82b084977e5644ca5b7 100644 --- a/front_end/entrypoints/main/MainImpl.ts +++ b/front_end/entrypoints/main/MainImpl.ts -@@ -761,6 +761,8 @@ export class MainImpl { +@@ -734,6 +734,8 @@ export class MainImpl { globalThis.Main = globalThis.Main || {}; // @ts-expect-error Exported for Tests.js globalThis.Main.Main = MainImpl; diff --git a/patches/node/.patches b/patches/node/.patches index 7603d4f935433..7e230586f067e 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -48,5 +48,8 @@ feat_add_oom_error_callback_in_node_isolatesettings.patch fix_-wnonnull_warning.patch refactor_attach_cppgc_heap_on_v8_isolate_creation.patch fix_ensure_traverseparent_bails_on_resource_path_exit.patch -fix_expose_readfilesync_override_for_modules.patch +cli_move_--trace-atomics-wait_to_eol.patch +fix_cppgc_initializing_twice.patch +fix_task_starvation_in_inspector_context_test.patch zlib_fix_pointer_alignment.patch +fix_expose_readfilesync_override_for_modules.patch diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index a5dcecf25f499..7076c3671a84a 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -140,10 +140,18 @@ index a73de23a1debfdac66873e0baccf882e383bfc36..7ac5291be093773ee7efd39e77e01bf5 // Handles compilation and caching of built-in JavaScript modules and // bootstrap scripts, whose source are bundled into the binary as static data. diff --git a/tools/install.py b/tools/install.py -index 17515720ba9c85d533465365188021074a8d30f4..7232be863d517c8445059a57c5938c2cbdeaf81b 100755 +index 17515720ba9c85d533465365188021074a8d30f4..92f83a83be67aafc9ead6923b868dbb0de39db34 100755 --- a/tools/install.py +++ b/tools/install.py -@@ -291,6 +291,7 @@ def headers(options, action): +@@ -212,6 +212,7 @@ def headers(options, action): + 'include/cppgc/internal/caged-heap-local-data.h', + 'include/cppgc/internal/caged-heap.h', + 'include/cppgc/internal/compiler-specific.h', ++ 'include/cppgc/internal/conditional-stack-allocated.h', + 'include/cppgc/internal/finalizer-trait.h', + 'include/cppgc/internal/gc-info.h', + 'include/cppgc/internal/logging.h', +@@ -291,6 +292,7 @@ def headers(options, action): 'include/v8-promise.h', 'include/v8-proxy.h', 'include/v8-regexp.h', diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch new file mode 100644 index 0000000000000..a9a2aa7f1d2cc --- /dev/null +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -0,0 +1,995 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Marco Ippolito +Date: Wed, 1 May 2024 14:24:48 +0200 +Subject: cli: move --trace-atomics-wait to eol +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PR-URL: https://github.com/nodejs/node/pull/52747 +Fixes: https://github.com/nodejs/node/issues/42982 +Reviewed-By: Matteo Collina +Reviewed-By: Rafael Gonzaga +Reviewed-By: Michaël Zasso +Reviewed-By: Benjamin Gruenbaum +Reviewed-By: Yagiz Nizipli + +diff --git a/doc/api/cli.md b/doc/api/cli.md +index 431a6aa7a2cf4d537cb719f15c2749254e0433b3..1d1672a01c4fae6b339cb144d0d6e35b49209a14 100644 +--- a/doc/api/cli.md ++++ b/doc/api/cli.md +@@ -32,11 +32,11 @@ is passed. If no corresponding file is found, an error is thrown. + If a file is found, its path will be passed to the + [ES module loader][Modules loaders] under any of the following conditions: + +-* The program was started with a command-line flag that forces the entry ++- The program was started with a command-line flag that forces the entry + point to be loaded with ECMAScript module loader, such as `--import` or + [`--experimental-default-type=module`][]. +-* The file has an `.mjs` extension. +-* The file does not have a `.cjs` extension, and the nearest parent ++- The file has an `.mjs` extension. ++- The file does not have a `.cjs` extension, and the nearest parent + `package.json` file contains a top-level [`"type"`][] field with a value of + `"module"`. + +@@ -164,7 +164,10 @@ Example: + ```js + const childProcess = require('node:child_process'); + // Attempt to bypass the permission +-childProcess.spawn('node', ['-e', 'require("fs").writeFileSync("/new-file", "example")']); ++childProcess.spawn('node', [ ++ '-e', ++ 'require("fs").writeFileSync("/new-file", "example")', ++]); + ``` + + ```console +@@ -206,8 +209,8 @@ the [Permission Model][]. + + The valid arguments for the `--allow-fs-read` flag are: + +-* `*` - To allow all `FileSystemRead` operations. +-* Multiple paths can be allowed using multiple `--allow-fs-read` flags. ++- `*` - To allow all `FileSystemRead` operations. ++- Multiple paths can be allowed using multiple `--allow-fs-read` flags. + Example `--allow-fs-read=/folder1/ --allow-fs-read=/folder1/` + + Examples can be found in the [File System Permissions][] documentation. +@@ -251,8 +254,8 @@ the [Permission Model][]. + + The valid arguments for the `--allow-fs-write` flag are: + +-* `*` - To allow all `FileSystemWrite` operations. +-* Multiple paths can be allowed using multiple `--allow-fs-write` flags. ++- `*` - To allow all `FileSystemWrite` operations. ++- Multiple paths can be allowed using multiple `--allow-fs-write` flags. + Example `--allow-fs-write=/folder1/ --allow-fs-write=/folder1/` + + Paths delimited by comma (`,`) are no longer allowed. +@@ -399,10 +402,10 @@ creation behavior. + + The following options are currently supported: + +-* `builder` {string} Required. Provides the name to the script that is executed ++- `builder` {string} Required. Provides the name to the script that is executed + before building the snapshot, as if [`--build-snapshot`][] had been passed + with `builder` as the main script name. +-* `withoutCodeCache` {boolean} Optional. Including the code cache reduces the ++- `withoutCodeCache` {boolean} Optional. Including the code cache reduces the + time spent on compiling functions included in the snapshot at the expense + of a bigger snapshot size and potentially breaking portability of the + snapshot. +@@ -550,9 +553,9 @@ Defaults to current working directory. + + Affects the default output directory of: + +-* [`--cpu-prof-dir`][] +-* [`--heap-prof-dir`][] +-* [`--redirect-warnings`][] ++- [`--cpu-prof-dir`][] ++- [`--heap-prof-dir`][] ++- [`--redirect-warnings`][] + + ### `--disable-proto=mode` + +@@ -697,9 +700,9 @@ changes: + Set the default value of `order` in [`dns.lookup()`][] and + [`dnsPromises.lookup()`][]. The value could be: + +-* `ipv4first`: sets default `order` to `ipv4first`. +-* `ipv6first`: sets default `order` to `ipv6first`. +-* `verbatim`: sets default `order` to `verbatim`. ++- `ipv4first`: sets default `order` to `ipv4first`. ++- `ipv6first`: sets default `order` to `ipv6first`. ++- `verbatim`: sets default `order` to `verbatim`. + + The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher + priority than `--dns-result-order`. +@@ -890,7 +893,7 @@ added: v22.7.0 + > Stability: 1 - Experimental + + Enables the use of [`AsyncLocalStorage`][] backed by `AsyncContextFrame` rather +-than the default implementation which relies on async\_hooks. This new model is ++than the default implementation which relies on async_hooks. This new model is + implemented very differently and so could have differences in how context data + flows within the application. As such, it is presently recommended to be sure + your application behaviour is unaffected by this change before using it in +@@ -909,12 +912,12 @@ added: + + Define which module system, `module` or `commonjs`, to use for the following: + +-* String input provided via `--eval` or STDIN, if `--input-type` is unspecified. ++- String input provided via `--eval` or STDIN, if `--input-type` is unspecified. + +-* Files ending in `.js` or with no extension, if there is no `package.json` file ++- Files ending in `.js` or with no extension, if there is no `package.json` file + present in the same folder or any parent folder. + +-* Files ending in `.js` or with no extension, if the nearest parent ++- Files ending in `.js` or with no extension, if the nearest parent + `package.json` field lacks a `"type"` field; unless the `package.json` folder + or any parent folder is inside a `node_modules` folder. + +@@ -1450,15 +1453,15 @@ interoperability with non-conformant HTTP implementations. + + When enabled, the parser will accept the following: + +-* Invalid HTTP headers values. +-* Invalid HTTP versions. +-* Allow message containing both `Transfer-Encoding` ++- Invalid HTTP headers values. ++- Invalid HTTP versions. ++- Allow message containing both `Transfer-Encoding` + and `Content-Length` headers. +-* Allow extra data after message when `Connection: close` is present. +-* Allow extra transfer encodings after `chunked` has been provided. +-* Allow `\n` to be used as token separator instead of `\r\n`. +-* Allow `\r\n` not to be provided after a chunk. +-* Allow spaces to be present after a chunk size and before `\r\n`. ++- Allow extra data after message when `Connection: close` is present. ++- Allow extra transfer encodings after `chunked` has been provided. ++- Allow `\n` to be used as token separator instead of `\r\n`. ++- Allow `\r\n` not to be provided after a chunk. ++- Allow spaces to be present after a chunk size and before `\r\n`. + + All the above will expose your application to request smuggling + or poisoning attack. Avoid using this option. +@@ -1536,8 +1539,8 @@ a [remote code execution][] attack. + + If specifying a host, make sure that either: + +-* The host is not accessible from public networks. +-* A firewall disallows unwanted connections on the port. ++- The host is not accessible from public networks. ++- A firewall disallows unwanted connections on the port. + + **More specifically, `--inspect=0.0.0.0` is insecure if the port (`9229` by + default) is not firewall-protected.** +@@ -1799,7 +1802,7 @@ added: + --> + + Enable OpenSSL 3.0 legacy provider. For more information please see +-[OSSL\_PROVIDER-legacy][OSSL_PROVIDER-legacy]. ++[OSSL_PROVIDER-legacy][OSSL_PROVIDER-legacy]. + + ### `--openssl-shared-config` + +@@ -1849,12 +1852,12 @@ changes: + Enable the Permission Model for current process. When enabled, the + following permissions are restricted: + +-* File System - manageable through ++- File System - manageable through + [`--allow-fs-read`][], [`--allow-fs-write`][] flags +-* Child Process - manageable through [`--allow-child-process`][] flag +-* Worker Threads - manageable through [`--allow-worker`][] flag +-* WASI - manageable through [`--allow-wasi`][] flag +-* Addons - manageable through [`--allow-addons`][] flag ++- Child Process - manageable through [`--allow-child-process`][] flag ++- Worker Threads - manageable through [`--allow-worker`][] flag ++- WASI - manageable through [`--allow-wasi`][] flag ++- Addons - manageable through [`--allow-addons`][] flag + + ### `--preserve-symlinks` + +@@ -2194,16 +2197,16 @@ cases. + Some features of other `run` implementations that are intentionally excluded + are: + +-* Running `pre` or `post` scripts in addition to the specified script. +-* Defining package manager-specific environment variables. ++- Running `pre` or `post` scripts in addition to the specified script. ++- Defining package manager-specific environment variables. + + #### Environment variables + + The following environment variables are set when running a script with `--run`: + +-* `NODE_RUN_SCRIPT_NAME`: The name of the script being run. For example, if ++- `NODE_RUN_SCRIPT_NAME`: The name of the script being run. For example, if + `--run` is used to run `test`, the value of this variable will be `test`. +-* `NODE_RUN_PACKAGE_JSON_PATH`: The path to the `package.json` that is being ++- `NODE_RUN_PACKAGE_JSON_PATH`: The path to the `package.json` that is being + processed. + + ### `--secure-heap-min=n` +@@ -2601,39 +2604,6 @@ added: v12.0.0 + Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.3'. Use to disable support + for TLSv1.2, which is not as secure as TLSv1.3. + +-### `--trace-atomics-wait` +- +- +- +-> Stability: 0 - Deprecated +- +-Print short summaries of calls to [`Atomics.wait()`][] to stderr. +-The output could look like this: +- +-```text +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the values mismatched +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) started +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread +-``` +- +-The fields here correspond to: +- +-* The thread id as given by [`worker_threads.threadId`][] +-* The base address of the `SharedArrayBuffer` in question, as well as the +- byte offset corresponding to the index passed to `Atomics.wait()` +-* The expected value that was passed to `Atomics.wait()` +-* The timeout passed to `Atomics.wait` +- + ### `--trace-deprecation` + + + +-* `--allow-addons` +-* `--allow-child-process` +-* `--allow-fs-read` +-* `--allow-fs-write` +-* `--allow-wasi` +-* `--allow-worker` +-* `--conditions`, `-C` +-* `--diagnostic-dir` +-* `--disable-proto` +-* `--disable-sigusr1` +-* `--disable-warning` +-* `--disable-wasm-trap-handler` +-* `--dns-result-order` +-* `--enable-fips` +-* `--enable-network-family-autoselection` +-* `--enable-source-maps` +-* `--entry-url` +-* `--experimental-abortcontroller` +-* `--experimental-async-context-frame` +-* `--experimental-default-type` +-* `--experimental-detect-module` +-* `--experimental-eventsource` +-* `--experimental-import-meta-resolve` +-* `--experimental-json-modules` +-* `--experimental-loader` +-* `--experimental-modules` +-* `--experimental-permission` +-* `--experimental-print-required-tla` +-* `--experimental-require-module` +-* `--experimental-shadow-realm` +-* `--experimental-specifier-resolution` +-* `--experimental-strip-types` +-* `--experimental-top-level-await` +-* `--experimental-transform-types` +-* `--experimental-vm-modules` +-* `--experimental-wasi-unstable-preview1` +-* `--experimental-wasm-modules` +-* `--experimental-webstorage` +-* `--force-context-aware` +-* `--force-fips` +-* `--force-node-api-uncaught-exceptions-policy` +-* `--frozen-intrinsics` +-* `--heap-prof-dir` +-* `--heap-prof-interval` +-* `--heap-prof-name` +-* `--heap-prof` +-* `--heapsnapshot-near-heap-limit` +-* `--heapsnapshot-signal` +-* `--http-parser` +-* `--icu-data-dir` +-* `--import` +-* `--input-type` +-* `--insecure-http-parser` +-* `--inspect-brk` +-* `--inspect-port`, `--debug-port` +-* `--inspect-publish-uid` +-* `--inspect-wait` +-* `--inspect` +-* `--localstorage-file` +-* `--max-http-header-size` +-* `--napi-modules` +-* `--network-family-autoselection-attempt-timeout` +-* `--no-addons` +-* `--no-deprecation` +-* `--no-experimental-fetch` +-* `--no-experimental-global-customevent` +-* `--no-experimental-global-navigator` +-* `--no-experimental-global-webcrypto` +-* `--no-experimental-repl-await` +-* `--no-experimental-sqlite` +-* `--no-experimental-websocket` +-* `--no-extra-info-on-fatal-exception` +-* `--no-force-async-hooks-checks` +-* `--no-global-search-paths` +-* `--no-network-family-autoselection` +-* `--no-warnings` +-* `--node-memory-debug` +-* `--openssl-config` +-* `--openssl-legacy-provider` +-* `--openssl-shared-config` +-* `--pending-deprecation` +-* `--permission` +-* `--preserve-symlinks-main` +-* `--preserve-symlinks` +-* `--prof-process` +-* `--redirect-warnings` +-* `--report-compact` +-* `--report-dir`, `--report-directory` +-* `--report-exclude-env` +-* `--report-exclude-network` +-* `--report-filename` +-* `--report-on-fatalerror` +-* `--report-on-signal` +-* `--report-signal` +-* `--report-uncaught-exception` +-* `--require`, `-r` +-* `--secure-heap-min` +-* `--secure-heap` +-* `--snapshot-blob` +-* `--test-coverage-branches` +-* `--test-coverage-exclude` +-* `--test-coverage-functions` +-* `--test-coverage-include` +-* `--test-coverage-lines` +-* `--test-name-pattern` +-* `--test-only` +-* `--test-reporter-destination` +-* `--test-reporter` +-* `--test-shard` +-* `--test-skip-pattern` +-* `--throw-deprecation` +-* `--title` +-* `--tls-cipher-list` +-* `--tls-keylog` +-* `--tls-max-v1.2` +-* `--tls-max-v1.3` +-* `--tls-min-v1.0` +-* `--tls-min-v1.1` +-* `--tls-min-v1.2` +-* `--tls-min-v1.3` +-* `--trace-atomics-wait` +-* `--trace-deprecation` +-* `--trace-env-js-stack` +-* `--trace-env-native-stack` +-* `--trace-env` +-* `--trace-event-categories` +-* `--trace-event-file-pattern` +-* `--trace-events-enabled` +-* `--trace-exit` +-* `--trace-require-module` +-* `--trace-sigint` +-* `--trace-sync-io` +-* `--trace-tls` +-* `--trace-uncaught` +-* `--trace-warnings` +-* `--track-heap-objects` +-* `--unhandled-rejections` +-* `--use-bundled-ca` +-* `--use-largepages` +-* `--use-openssl-ca` +-* `--v8-pool-size` +-* `--watch-path` +-* `--watch-preserve-output` +-* `--watch` +-* `--zero-fill-buffers` ++- `--allow-addons` ++- `--allow-child-process` ++- `--allow-fs-read` ++- `--allow-fs-write` ++- `--allow-wasi` ++- `--allow-worker` ++- `--conditions`, `-C` ++- `--diagnostic-dir` ++- `--disable-proto` ++- `--disable-sigusr1` ++- `--disable-warning` ++- `--disable-wasm-trap-handler` ++- `--dns-result-order` ++- `--enable-fips` ++- `--enable-network-family-autoselection` ++- `--enable-source-maps` ++- `--entry-url` ++- `--experimental-abortcontroller` ++- `--experimental-async-context-frame` ++- `--experimental-default-type` ++- `--experimental-detect-module` ++- `--experimental-eventsource` ++- `--experimental-import-meta-resolve` ++- `--experimental-json-modules` ++- `--experimental-loader` ++- `--experimental-modules` ++- `--experimental-permission` ++- `--experimental-print-required-tla` ++- `--experimental-require-module` ++- `--experimental-shadow-realm` ++- `--experimental-specifier-resolution` ++- `--experimental-strip-types` ++- `--experimental-top-level-await` ++- `--experimental-transform-types` ++- `--experimental-vm-modules` ++- `--experimental-wasi-unstable-preview1` ++- `--experimental-wasm-modules` ++- `--experimental-webstorage` ++- `--force-context-aware` ++- `--force-fips` ++- `--force-node-api-uncaught-exceptions-policy` ++- `--frozen-intrinsics` ++- `--heap-prof-dir` ++- `--heap-prof-interval` ++- `--heap-prof-name` ++- `--heap-prof` ++- `--heapsnapshot-near-heap-limit` ++- `--heapsnapshot-signal` ++- `--http-parser` ++- `--icu-data-dir` ++- `--import` ++- `--input-type` ++- `--insecure-http-parser` ++- `--inspect-brk` ++- `--inspect-port`, `--debug-port` ++- `--inspect-publish-uid` ++- `--inspect-wait` ++- `--inspect` ++- `--localstorage-file` ++- `--max-http-header-size` ++- `--napi-modules` ++- `--network-family-autoselection-attempt-timeout` ++- `--no-addons` ++- `--no-deprecation` ++- `--no-experimental-fetch` ++- `--no-experimental-global-customevent` ++- `--no-experimental-global-navigator` ++- `--no-experimental-global-webcrypto` ++- `--no-experimental-repl-await` ++- `--no-experimental-sqlite` ++- `--no-experimental-websocket` ++- `--no-extra-info-on-fatal-exception` ++- `--no-force-async-hooks-checks` ++- `--no-global-search-paths` ++- `--no-network-family-autoselection` ++- `--no-warnings` ++- `--node-memory-debug` ++- `--openssl-config` ++- `--openssl-legacy-provider` ++- `--openssl-shared-config` ++- `--pending-deprecation` ++- `--permission` ++- `--preserve-symlinks-main` ++- `--preserve-symlinks` ++- `--prof-process` ++- `--redirect-warnings` ++- `--report-compact` ++- `--report-dir`, `--report-directory` ++- `--report-exclude-env` ++- `--report-exclude-network` ++- `--report-filename` ++- `--report-on-fatalerror` ++- `--report-on-signal` ++- `--report-signal` ++- `--report-uncaught-exception` ++- `--require`, `-r` ++- `--secure-heap-min` ++- `--secure-heap` ++- `--snapshot-blob` ++- `--test-coverage-branches` ++- `--test-coverage-exclude` ++- `--test-coverage-functions` ++- `--test-coverage-include` ++- `--test-coverage-lines` ++- `--test-name-pattern` ++- `--test-only` ++- `--test-reporter-destination` ++- `--test-reporter` ++- `--test-shard` ++- `--test-skip-pattern` ++- `--throw-deprecation` ++- `--title` ++- `--tls-cipher-list` ++- `--tls-keylog` ++- `--tls-max-v1.2` ++- `--tls-max-v1.3` ++- `--tls-min-v1.0` ++- `--tls-min-v1.1` ++- `--tls-min-v1.2` ++- `--tls-min-v1.3` ++- `--trace-deprecation` ++- `--trace-env-js-stack` ++- `--trace-env-native-stack` ++- `--trace-env` ++- `--trace-event-categories` ++- `--trace-event-file-pattern` ++- `--trace-events-enabled` ++- `--trace-exit` ++- `--trace-require-module` ++- `--trace-sigint` ++- `--trace-sync-io` ++- `--trace-tls` ++- `--trace-uncaught` ++- `--trace-warnings` ++- `--track-heap-objects` ++- `--unhandled-rejections` ++- `--use-bundled-ca` ++- `--use-largepages` ++- `--use-openssl-ca` ++- `--v8-pool-size` ++- `--watch-path` ++- `--watch-preserve-output` ++- `--watch` ++- `--zero-fill-buffers` + + + +@@ -3266,19 +3235,19 @@ V8 options that are allowed are: + + + +-* `--abort-on-uncaught-exception` +-* `--disallow-code-generation-from-strings` +-* `--enable-etw-stack-walking` +-* `--expose-gc` +-* `--interpreted-frames-native-stack` +-* `--jitless` +-* `--max-old-space-size` +-* `--max-semi-space-size` +-* `--perf-basic-prof-only-functions` +-* `--perf-basic-prof` +-* `--perf-prof-unwinding-info` +-* `--perf-prof` +-* `--stack-trace-limit` ++- `--abort-on-uncaught-exception` ++- `--disallow-code-generation-from-strings` ++- `--enable-etw-stack-walking` ++- `--expose-gc` ++- `--interpreted-frames-native-stack` ++- `--jitless` ++- `--max-old-space-size` ++- `--max-semi-space-size` ++- `--perf-basic-prof-only-functions` ++- `--perf-basic-prof` ++- `--perf-prof-unwinding-info` ++- `--perf-prof` ++- `--stack-trace-limit` + + + +@@ -3446,23 +3415,12 @@ and the line lengths of the source file (in the key `lineLengths`). + "url": "./path-to-map.json", + "data": { + "version": 3, +- "sources": [ +- "file:///absolute/path/to/original.js" +- ], +- "names": [ +- "Foo", +- "console", +- "info" +- ], ++ "sources": ["file:///absolute/path/to/original.js"], ++ "names": ["Foo", "console", "info"], + "mappings": "MAAMA,IACJC,YAAaC", + "sourceRoot": "./" + }, +- "lineLengths": [ +- 13, +- 62, +- 38, +- 27 +- ] ++ "lineLengths": [13, 62, 38, 27] + } + } + } +@@ -3470,7 +3428,7 @@ and the line lengths of the source file (in the key `lineLengths`). + + ### `NO_COLOR=` + +-[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the ++[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the + environment variable is arbitrary. + + ### `OPENSSL_CONF=file` +@@ -3552,12 +3510,12 @@ Asynchronous system APIs are used by Node.js whenever possible, but where they + do not exist, libuv's threadpool is used to create asynchronous node APIs based + on synchronous system APIs. Node.js APIs that use the threadpool are: + +-* all `fs` APIs, other than the file watcher APIs and those that are explicitly ++- all `fs` APIs, other than the file watcher APIs and those that are explicitly + synchronous +-* asynchronous crypto APIs such as `crypto.pbkdf2()`, `crypto.scrypt()`, ++- asynchronous crypto APIs such as `crypto.pbkdf2()`, `crypto.scrypt()`, + `crypto.randomBytes()`, `crypto.randomFill()`, `crypto.generateKeyPair()` +-* `dns.lookup()` +-* all `zlib` APIs, other than those that are explicitly synchronous ++- `dns.lookup()` ++- all `zlib` APIs, other than those that are explicitly synchronous + + Because libuv's threadpool has a fixed size, it means that if for whatever + reason any of these APIs takes a long time, other (seemingly unrelated) APIs +@@ -3723,7 +3681,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 + [`--redirect-warnings`]: #--redirect-warningsfile + [`--require`]: #-r---require-module + [`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage +-[`Atomics.wait()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait + [`Buffer`]: buffer.md#class-buffer + [`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html + [`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax +@@ -3746,7 +3703,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 + [`tls.DEFAULT_MIN_VERSION`]: tls.md#tlsdefault_min_version + [`unhandledRejection`]: process.md#event-unhandledrejection + [`v8.startupSnapshot` API]: v8.md#startup-snapshot-api +-[`worker_threads.threadId`]: worker_threads.md#workerthreadid + [collecting code coverage from tests]: test.md#collecting-code-coverage + [conditional exports]: packages.md#conditional-exports + [context-aware]: addons.md#context-aware-addons +diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md +index 0df7dce60058d518c9607094344461b60e540e60..f5f8ac77c848edf62b0a82f0644f61077a02aedd 100644 +--- a/doc/api/deprecations.md ++++ b/doc/api/deprecations.md +@@ -3313,6 +3313,9 @@ Values other than `undefined`, `null`, integer numbers, and integer strings + + + +-Type: Runtime ++Type: End-of-Life + +-The [`--trace-atomics-wait`][] flag is deprecated because ++The `--trace-atomics-wait` flag has been removed because + it uses the V8 hook `SetAtomicsWaitCallback`, + that will be removed in a future V8 release. + +@@ -3737,7 +3740,6 @@ deprecated, as their values are guaranteed to be identical to that of `process.f + [`--force-node-api-uncaught-exceptions-policy`]: cli.md#--force-node-api-uncaught-exceptions-policy + [`--pending-deprecation`]: cli.md#--pending-deprecation + [`--throw-deprecation`]: cli.md#--throw-deprecation +-[`--trace-atomics-wait`]: cli.md#--trace-atomics-wait + [`--unhandled-rejections`]: cli.md#--unhandled-rejectionsmode + [`Buffer.allocUnsafeSlow(size)`]: buffer.md#static-method-bufferallocunsafeslowsize + [`Buffer.from(array)`]: buffer.md#static-method-bufferfromarray +diff --git a/doc/node.1 b/doc/node.1 +index 9f534746ef9d9c1c1ee2edd6c195573a2e228600..e01fc511a1034518c0fb9bc5fa925524aecad927 100644 +--- a/doc/node.1 ++++ b/doc/node.1 +@@ -533,11 +533,6 @@ but the option is supported for compatibility with older Node.js versions. + Set default minVersion to 'TLSv1.3'. Use to disable support for TLSv1.2 in + favour of TLSv1.3, which is more secure. + . +-.It Fl -trace-atomics-wait +-Print short summaries of calls to +-.Sy Atomics.wait() . +-. +-This flag is deprecated. + .It Fl -trace-deprecation + Print stack traces for deprecations. + . +diff --git a/src/node.cc b/src/node.cc +index 0ed78ab6b52906e980eebf1f625a1c7cbfc8097b..2ff08a9cb6124316049a91bda70cf6985045286a 100644 +--- a/src/node.cc ++++ b/src/node.cc +@@ -226,44 +226,6 @@ void Environment::WaitForInspectorFrontendByOptions() { + } + #endif // HAVE_INSPECTOR + +-#define ATOMIC_WAIT_EVENTS(V) \ +- V(kStartWait, "started") \ +- V(kWokenUp, "was woken up by another thread") \ +- V(kTimedOut, "timed out") \ +- V(kTerminatedExecution, "was stopped by terminated execution") \ +- V(kAPIStopped, "was stopped through the embedder API") \ +- V(kNotEqual, "did not wait because the values mismatched") \ +- +-static void AtomicsWaitCallback(Isolate::AtomicsWaitEvent event, +- Local array_buffer, +- size_t offset_in_bytes, int64_t value, +- double timeout_in_ms, +- Isolate::AtomicsWaitWakeHandle* stop_handle, +- void* data) { +- Environment* env = static_cast(data); +- +- const char* message = "(unknown event)"; +- switch (event) { +-#define V(key, msg) \ +- case Isolate::AtomicsWaitEvent::key: \ +- message = msg; \ +- break; +- ATOMIC_WAIT_EVENTS(V) +-#undef V +- } +- +- fprintf(stderr, +- "(node:%d) [Thread %" PRIu64 "] Atomics.wait(%p + %zx, %" PRId64 +- ", %.f) %s\n", +- static_cast(uv_os_getpid()), +- env->thread_id(), +- array_buffer->Data(), +- offset_in_bytes, +- value, +- timeout_in_ms, +- message); +-} +- + void Environment::InitializeDiagnostics() { + isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback( + Environment::BuildEmbedderGraph, this); +@@ -272,17 +234,6 @@ void Environment::InitializeDiagnostics() { + } + if (options_->trace_uncaught) + isolate_->SetCaptureStackTraceForUncaughtExceptions(true); +- if (options_->trace_atomics_wait) { +- ProcessEmitDeprecationWarning( +- Environment::GetCurrent(isolate_), +- "The flag --trace-atomics-wait is deprecated.", +- "DEP0165"); +- isolate_->SetAtomicsWaitCallback(AtomicsWaitCallback, this); +- AddCleanupHook([](void* data) { +- Environment* env = static_cast(data); +- env->isolate()->SetAtomicsWaitCallback(nullptr, nullptr); +- }, this); +- } + if (options_->trace_promises) { + isolate_->SetPromiseHook(TracePromises); + } +diff --git a/src/node_options.cc b/src/node_options.cc +index 866f2a39a5e51a8bf434ccd54d76c685bcdd1502..2bedaa88582c369a4a420ff20a5204af96feb52e 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -758,10 +758,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { + "throw an exception on deprecations", + &EnvironmentOptions::throw_deprecation, + kAllowedInEnvvar); +- AddOption("--trace-atomics-wait", +- "(deprecated) trace Atomics.wait() operations", +- &EnvironmentOptions::trace_atomics_wait, +- kAllowedInEnvvar); + AddOption("--trace-deprecation", + "show stack traces on deprecations", + &EnvironmentOptions::trace_deprecation, +diff --git a/src/node_options.h b/src/node_options.h +index 41dd04f5e2b1cd54c32df70830389d44d7b39aa2..b10287d3eadc49b44e2e3fb8150a48be6866b0b4 100644 +--- a/src/node_options.h ++++ b/src/node_options.h +@@ -203,7 +203,6 @@ class EnvironmentOptions : public Options { + std::vector coverage_include_pattern; + std::vector coverage_exclude_pattern; + bool throw_deprecation = false; +- bool trace_atomics_wait = false; + bool trace_deprecation = false; + bool trace_exit = false; + bool trace_sync_io = false; +diff --git a/test/parallel/test-trace-atomic-deprecation.js b/test/parallel/test-trace-atomic-deprecation.js +deleted file mode 100644 +index 8aeddb28e938d23e646d882cfe24b2e2311f9ab2..0000000000000000000000000000000000000000 +--- a/test/parallel/test-trace-atomic-deprecation.js ++++ /dev/null +@@ -1,14 +0,0 @@ +-'use strict'; +- +-const common = require('../common'); +-const assert = require('node:assert'); +-const { test } = require('node:test'); +- +-test('should emit deprecation warning DEP0165', async () => { +- const { code, stdout, stderr } = await common.spawnPromisified( +- process.execPath, ['--trace-atomics-wait', '-e', '{}'] +- ); +- assert.match(stderr, /\[DEP0165\] DeprecationWarning:/); +- assert.strictEqual(stdout, ''); +- assert.strictEqual(code, 0); +-}); +diff --git a/test/parallel/test-trace-atomics-wait.js b/test/parallel/test-trace-atomics-wait.js +deleted file mode 100644 +index 6449a2be2b47e0758090dc13d136877b1874c635..0000000000000000000000000000000000000000 +--- a/test/parallel/test-trace-atomics-wait.js ++++ /dev/null +@@ -1,101 +0,0 @@ +-'use strict'; +-require('../common'); +-const assert = require('assert'); +-const child_process = require('child_process'); +-const { Worker } = require('worker_threads'); +- +-if (process.argv[2] === 'child') { +- const i32arr = new Int32Array(new SharedArrayBuffer(8)); +- assert.strictEqual(Atomics.wait(i32arr, 0, 1), 'not-equal'); +- assert.strictEqual(Atomics.wait(i32arr, 0, 0, 10), 'timed-out'); +- +- new Worker(` +- const i32arr = require('worker_threads').workerData; +- Atomics.store(i32arr, 1, -1); +- Atomics.notify(i32arr, 1); +- Atomics.wait(i32arr, 1, -1); +- `, { eval: true, workerData: i32arr }); +- +- Atomics.wait(i32arr, 1, 0); +- assert.strictEqual(Atomics.load(i32arr, 1), -1); +- Atomics.store(i32arr, 1, 0); +- Atomics.notify(i32arr, 1); +- return; +-} +- +-const proc = child_process.spawnSync( +- process.execPath, +- [ '--disable-warning=DEP0165', '--trace-atomics-wait', __filename, 'child' ], +- { encoding: 'utf8', stdio: [ 'inherit', 'inherit', 'pipe' ] }); +- +-if (proc.status !== 0) console.log(proc); +-assert.strictEqual(proc.status, 0); +- +-const SABAddress = proc.stderr.match(/Atomics\.wait\((?.+) \+/).groups.SAB; +-const actualTimeline = proc.stderr +- .replace(new RegExp(SABAddress, 'g'), '
') +- .replace(new RegExp(`\\(node:${proc.pid}\\) `, 'g'), '') +- .replace(/\binf(inity)?\b/gi, 'inf') +- .replace(/\r/g, '') +- .trim(); +-console.log('+++ normalized stdout +++'); +-console.log(actualTimeline); +-console.log('--- normalized stdout ---'); +- +-const begin = +-`[Thread 0] Atomics.wait(
+ 0, 1, inf) started +-[Thread 0] Atomics.wait(
+ 0, 1, inf) did not wait because the \ +-values mismatched +-[Thread 0] Atomics.wait(
+ 0, 0, 10) started +-[Thread 0] Atomics.wait(
+ 0, 0, 10) timed out`; +- +-const expectedTimelines = [ +- `${begin} +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) was woken up by another thread`, +- `${begin} +-[Thread 1] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, -1, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) was woken up by another thread`, +- `${begin} +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 1] Atomics.wait(
+ 4, -1, inf) was woken up by another thread`, +- `${begin} +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 1] Atomics.wait(
+ 4, -1, inf) did not wait because the \ +-values mismatched`, +- `${begin} +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) did not wait because the \ +-values mismatched`, +- `${begin} +-[Thread 1] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, -1, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) was woken up by another thread +-[Thread 1] Atomics.wait(
+ 4, -1, inf) did not wait because the \ +-values mismatched`, +- `${begin} +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) did not wait because the \ +-values mismatched +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 1] Atomics.wait(
+ 4, -1, inf) did not wait because the \ +-values mismatched`, +- `${begin} +-[Thread 1] Atomics.wait(
+ 4, -1, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) started +-[Thread 0] Atomics.wait(
+ 4, 0, inf) did not wait because the \ +-values mismatched +-[Thread 1] Atomics.wait(
+ 4, -1, inf) was woken up by another thread`, +-]; +- +-assert(expectedTimelines.includes(actualTimeline)); diff --git a/patches/node/fix_cppgc_initializing_twice.patch b/patches/node/fix_cppgc_initializing_twice.patch new file mode 100644 index 0000000000000..f03ffed13d79f --- /dev/null +++ b/patches/node/fix_cppgc_initializing_twice.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Maddock +Date: Thu, 27 Mar 2025 17:00:11 -0400 +Subject: fix: cppgc initializing twice + +Refs https://chromium-review.googlesource.com/c/v8/v8/+/6333562 + +cppgc will now be initialized as part of V8::Initialize. For now, skip +initialization if it's already been initialized. + +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 2ff08a9cb6124316049a91bda70cf6985045286a..5de93329cbd40a2ca314d602b123092ed15741c5 100644 +--- a/src/node.cc ++++ b/src/node.cc +@@ -1173,7 +1173,7 @@ InitializeOncePerProcessInternal(const std::vector& args, + result->platform_ = per_process::v8_platform.Platform(); + } + +- if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) { ++ if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc) && !cppgc::IsInitialized()) { + v8::PageAllocator* allocator = nullptr; + if (result->platform_ != nullptr) { + allocator = result->platform_->GetPageAllocator(); diff --git a/patches/node/fix_task_starvation_in_inspector_context_test.patch b/patches/node/fix_task_starvation_in_inspector_context_test.patch new file mode 100644 index 0000000000000..9e3438d72c40b --- /dev/null +++ b/patches/node/fix_task_starvation_in_inspector_context_test.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Calvin Watford +Date: Thu, 3 Apr 2025 10:59:30 -0600 +Subject: Fix task starvation in inspector context test + +A V8 change makes these contexts get collected in a task that is posted +and run asynchronously. The tests were synchronously GC'ing in an +infinite loop, preventing the task loop from running the task that would +GC these contexts. + +This change should be upstreamed in some way. + +Ref: https://chromium-review.googlesource.com/c/v8/v8/+/4733273 + +diff --git a/test/parallel/test-inspector-contexts.js b/test/parallel/test-inspector-contexts.js +index e7bdc53f8cd5763572798cbd9ef07c902e3fc335..8b7a1f1aaf648efe10761af205ac561952a06980 100644 +--- a/test/parallel/test-inspector-contexts.js ++++ b/test/parallel/test-inspector-contexts.js +@@ -17,6 +17,13 @@ function notificationPromise(method) { + return new Promise((resolve) => session.once(method, resolve)); + } + ++function gcImmediate() { ++ return new Promise((resolve) => { ++ global.gc(); ++ setImmediate(resolve); ++ }); ++} ++ + async function testContextCreatedAndDestroyed() { + console.log('Testing context created/destroyed notifications'); + { +@@ -68,7 +75,7 @@ async function testContextCreatedAndDestroyed() { + // GC is unpredictable... + console.log('Checking/waiting for GC.'); + while (!contextDestroyed) +- global.gc(); ++ await gcImmediate(); + console.log('Context destroyed.'); + + assert.strictEqual(contextDestroyed.params.executionContextId, id, +@@ -100,7 +107,7 @@ async function testContextCreatedAndDestroyed() { + // GC is unpredictable... + console.log('Checking/waiting for GC again.'); + while (!contextDestroyed) +- global.gc(); ++ await gcImmediate(); + console.log('Other context destroyed.'); + } + +@@ -126,7 +133,7 @@ async function testContextCreatedAndDestroyed() { + // GC is unpredictable... + console.log('Checking/waiting for GC a third time.'); + while (!contextDestroyed) +- global.gc(); ++ await gcImmediate(); + console.log('Context destroyed once again.'); + } + +@@ -150,7 +157,7 @@ async function testContextCreatedAndDestroyed() { + // GC is unpredictable... + console.log('Checking/waiting for GC a fourth time.'); + while (!contextDestroyed) +- global.gc(); ++ await gcImmediate(); + console.log('Context destroyed a fourth time.'); + } + } diff --git a/patches/v8/.patches b/patches/v8/.patches index 905bd3558cc0d..280a34b936037 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,3 +1,2 @@ chore_allow_customizing_microtask_policy_per_context.patch deps_add_v8_object_setinternalfieldfornodecore.patch -revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch diff --git a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch index 6c58105618a7e..8abe837f5bd5d 100644 --- a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch +++ b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch @@ -46,7 +46,7 @@ index 3e57ae8efe33f326ef0e5d609c311d4be5b8afd6..dc521d39c2280dfc3217e97c1e413b2b V8_INLINE static void* GetAlignedPointerFromInternalField( const BasicTracedReference& object, int index) { diff --git a/src/api/api.cc b/src/api/api.cc -index c1ba8bb806927ec63af004a620768d736b122c4f..f4ed96bf45183c8a7453ebac994904c2ea7d9707 100644 +index 545c1093ee7544da3878637e11c557e3964b8835..f80a7af94a9f2459a96976543f6117fb41f1aabb 100644 --- a/src/api/api.cc +++ b/src/api/api.cc @@ -6324,14 +6324,33 @@ Local v8::Object::SlowGetInternalField(int index) { diff --git a/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch b/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch deleted file mode 100644 index 199ce1061b774..0000000000000 --- a/patches/v8/revert_api_delete_deprecated_attachcppheap_and_detachcppheap.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Charles Kerr -Date: Thu, 6 Mar 2025 14:31:19 -0600 -Subject: Revert "[api] Delete deprecated AttachCppHeap and DetachCppHeap" - -Restore this API because Node.js needs it. - -This patch can be removed after an upstream fix lands in Node.js, -e.g. in https://github.com/nodejs/node-v8/tree/canary - -diff --git a/include/v8-isolate.h b/include/v8-isolate.h -index 97f1030dd2ca47ca4b58ac64e2e11e615bc46130..24ef6b5e0af63179e557b9896134838e112c59db 100644 ---- a/include/v8-isolate.h -+++ b/include/v8-isolate.h -@@ -1172,6 +1172,28 @@ class V8_EXPORT Isolate { - */ - void SetEmbedderRootsHandler(EmbedderRootsHandler* handler); - -+ /** -+ * Attaches a managed C++ heap as an extension to the JavaScript heap. The -+ * embedder maintains ownership of the CppHeap. At most one C++ heap can be -+ * attached to V8. -+ * -+ * Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see -+ * CppHeap. -+ * -+ * If a CppHeap is set via CreateParams, then this call is a noop. -+ */ -+ V8_DEPRECATED("Set the heap on Isolate creation using CreateParams instead.") -+ void AttachCppHeap(CppHeap*); -+ -+ /** -+ * Detaches a managed C++ heap if one was attached using `AttachCppHeap()`. -+ * -+ * If a CppHeap is set via CreateParams, then this call is a noop. -+ */ -+ V8_DEPRECATED( -+ "The CppHeap gets detached automatically during Isolate tear down.") -+ void DetachCppHeap(); -+ - using ReleaseCppHeapCallback = void (*)(std::unique_ptr); - - /** -@@ -1219,7 +1241,6 @@ class V8_EXPORT Isolate { - class V8_DEPRECATED("AtomicsWaitWakeHandle is unused and will be removed.") - #endif - V8_EXPORT AtomicsWaitWakeHandle { -- - public: - /** - * Stop this `Atomics.wait()` call and call the |AtomicsWaitCallback| -diff --git a/src/api/api.cc b/src/api/api.cc -index f4ed96bf45183c8a7453ebac994904c2ea7d9707..59327d4619661a138c407b468794e6a0f60a91e3 100644 ---- a/src/api/api.cc -+++ b/src/api/api.cc -@@ -9878,6 +9878,16 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) { - i_isolate->heap()->SetEmbedderRootsHandler(handler); - } - -+void Isolate::AttachCppHeap(CppHeap* cpp_heap) { -+ i::Isolate* i_isolate = reinterpret_cast(this); -+ i_isolate->heap()->AttachCppHeap(cpp_heap); -+} -+ -+void Isolate::DetachCppHeap() { -+ i::Isolate* i_isolate = reinterpret_cast(this); -+ i_isolate->heap()->DetachCppHeap(); -+} -+ - CppHeap* Isolate::GetCppHeap() const { - const i::Isolate* i_isolate = reinterpret_cast(this); - return i_isolate->heap()->cpp_heap(); -diff --git a/src/heap/cppgc-js/cpp-heap.cc b/src/heap/cppgc-js/cpp-heap.cc -index a03e4d6fdb8acb2623434f08b1f63ff86cb1e77a..756e5f89a3b947761c01fc3cc59c654eb99836d7 100644 ---- a/src/heap/cppgc-js/cpp-heap.cc -+++ b/src/heap/cppgc-js/cpp-heap.cc -@@ -513,6 +513,11 @@ CppHeap::CppHeap( - } - - CppHeap::~CppHeap() { -+ if (isolate_) { -+ // TODO(ahaas): Delete this code once `v8::Isolate::DetachCppHeap` has been -+ // deleted. -+ isolate_->heap()->DetachCppHeap(); -+ } - Terminate(); - } - -diff --git a/src/heap/heap.cc b/src/heap/heap.cc -index 3823f26158a12d93636bb3376065ba7ce9d8d4d6..86cdcc8c3c4c09134db8d89f93fd3405cabcf385 100644 ---- a/src/heap/heap.cc -+++ b/src/heap/heap.cc -@@ -6070,6 +6070,21 @@ void Heap::AttachCppHeap(v8::CppHeap* cpp_heap) { - cpp_heap_ = cpp_heap; - } - -+void Heap::DetachCppHeap() { -+ // The API function should be a noop in case a CppHeap was passed on Isolate -+ // creation. -+ if (owning_cpp_heap_) { -+ return; -+ } -+ -+ // The CppHeap may have been detached already. -+ if (!cpp_heap_) return; -+ -+ CppHeap::From(cpp_heap_)->StartDetachingIsolate(); -+ CppHeap::From(cpp_heap_)->DetachIsolate(); -+ cpp_heap_ = nullptr; -+} -+ - std::optional Heap::overridden_stack_state() const { - if (!embedder_stack_state_origin_) return {}; - return embedder_stack_state_; -diff --git a/src/heap/heap.h b/src/heap/heap.h -index 645340bda0137810497a9885b066e500b51428d4..b83def4691341c595dfba0b4f996345849ead640 100644 ---- a/src/heap/heap.h -+++ b/src/heap/heap.h -@@ -1110,6 +1110,9 @@ class Heap final { - // Unified heap (C++) support. =============================================== - // =========================================================================== - -+ V8_EXPORT_PRIVATE void AttachCppHeap(v8::CppHeap* cpp_heap); -+ V8_EXPORT_PRIVATE void DetachCppHeap(); -+ - v8::CppHeap* cpp_heap() const { return cpp_heap_; } - - std::optional overridden_stack_state() const; -@@ -1651,8 +1654,6 @@ class Heap final { - private: - class AllocationTrackerForDebugging; - -- void AttachCppHeap(v8::CppHeap* cpp_heap); -- - using ExternalStringTableUpdaterCallback = - Tagged (*)(Heap* heap, FullObjectSlot pointer); - diff --git a/script/nan-spec-runner.js b/script/nan-spec-runner.js index 06ce3f1c5671e..031062f4b3552 100644 --- a/script/nan-spec-runner.js +++ b/script/nan-spec-runner.js @@ -131,7 +131,10 @@ async function main () { 'nannew-test.js', 'buffer-test.js', // we can't patch this test because it uses CRLF line endings - 'methodswithdata-test.js' + 'methodswithdata-test.js', + // these two are incompatible with crrev.com/c/4733273 + 'weak-test.js', + 'weak2-test.js' ]); const testsToRun = fs.readdirSync(path.resolve(NAN_DIR, 'test', 'js')) .filter(test => !DISABLED_TESTS.has(test)) diff --git a/shell/app/electron_main_delegate.cc b/shell/app/electron_main_delegate.cc index a66e140a1ecd4..9c34d5ca7b77f 100644 --- a/shell/app/electron_main_delegate.cc +++ b/shell/app/electron_main_delegate.cc @@ -455,8 +455,7 @@ ElectronMainDelegate::CreateContentUtilityClient() { return utility_client_.get(); } -absl::variant -ElectronMainDelegate::RunProcess( +std::variant ElectronMainDelegate::RunProcess( const std::string& process_type, content::MainFunctionParams main_function_params) { if (process_type == kRelauncherProcess) @@ -466,7 +465,7 @@ ElectronMainDelegate::RunProcess( } bool ElectronMainDelegate::ShouldCreateFeatureList(InvokedIn invoked_in) { - return absl::holds_alternative(invoked_in); + return std::holds_alternative(invoked_in); } bool ElectronMainDelegate::ShouldInitializeMojo(InvokedIn invoked_in) { diff --git a/shell/app/electron_main_delegate.h b/shell/app/electron_main_delegate.h index ed484077f378f..af365128494e4 100644 --- a/shell/app/electron_main_delegate.h +++ b/shell/app/electron_main_delegate.h @@ -52,7 +52,7 @@ class ElectronMainDelegate : public content::ContentMainDelegate { content::ContentGpuClient* CreateContentGpuClient() override; content::ContentRendererClient* CreateContentRendererClient() override; content::ContentUtilityClient* CreateContentUtilityClient() override; - absl::variant RunProcess( + std::variant RunProcess( const std::string& process_type, content::MainFunctionParams main_function_params) override; bool ShouldCreateFeatureList(InvokedIn invoked_in) override; diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc index 3c19d191720b3..b394fb0dbee2a 100644 --- a/shell/browser/api/electron_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -1155,7 +1155,7 @@ void App::DisableDomainBlockingFor3DAPIs(gin_helper::ErrorThrower thrower) { bool App::IsAccessibilitySupportEnabled() { auto* ax_state = content::BrowserAccessibilityState::GetInstance(); - return ax_state->IsAccessibleBrowser(); + return ax_state->GetAccessibilityMode() == ui::kAXModeComplete; } void App::SetAccessibilitySupportEnabled(gin_helper::ErrorThrower thrower, @@ -1169,9 +1169,9 @@ void App::SetAccessibilitySupportEnabled(gin_helper::ErrorThrower thrower, auto* ax_state = content::BrowserAccessibilityState::GetInstance(); if (enabled) { - ax_state->OnScreenReaderDetected(); + ax_state->EnableProcessAccessibility(); } else { - ax_state->DisableAccessibility(); + ax_state->DisableProcessAccessibility(); } Browser::Get()->OnAccessibilitySupportChanged(); } @@ -1629,6 +1629,8 @@ void ConfigureHostResolver(v8::Isolate* isolate, bool enable_built_in_resolver = base::FeatureList::IsEnabled(net::features::kAsyncDns); + bool enable_happy_eyeballs_v3 = + base::FeatureList::IsEnabled(net::features::kHappyEyeballsV3); bool additional_dns_query_types_enabled = true; if (opts.Has("enableBuiltInResolver") && @@ -1637,6 +1639,12 @@ void ConfigureHostResolver(v8::Isolate* isolate, return; } + if (opts.Has("enableHappyEyeballs") && + !opts.Get("enableHappyEyeballs", &enable_happy_eyeballs_v3)) { + thrower.ThrowTypeError("enableHappyEyeballs must be a boolean"); + return; + } + if (opts.Has("secureDnsMode") && !opts.Get("secureDnsMode", &secure_dns_mode)) { thrower.ThrowTypeError( @@ -1677,8 +1685,8 @@ void ConfigureHostResolver(v8::Isolate* isolate, // Configure the stub resolver. This must be done after the system // NetworkContext is created, but before anything has the chance to use it. content::GetNetworkService()->ConfigureStubHostResolver( - enable_built_in_resolver, secure_dns_mode, doh_config, - additional_dns_query_types_enabled); + enable_built_in_resolver, enable_happy_eyeballs_v3, secure_dns_mode, + doh_config, additional_dns_query_types_enabled); } // static diff --git a/shell/browser/api/electron_api_cookies.cc b/shell/browser/api/electron_api_cookies.cc index c9292d4c123c0..a7296bde7de30 100644 --- a/shell/browser/api/electron_api_cookies.cc +++ b/shell/browser/api/electron_api_cookies.cc @@ -250,7 +250,10 @@ const std::string InclusionStatusToString(net::CookieInclusionStatus status) { {Reason::EXCLUDE_THIRD_PARTY_PHASEOUT, "The cookie is blocked for third-party cookie phaseout."}, {Reason::EXCLUDE_NO_COOKIE_CONTENT, - "The cookie contains no content or only whitespace."}}); + "The cookie contains no content or only whitespace."}, + {Reason::EXCLUDE_ANONYMOUS_CONTEXT, + "The cookie is unpartitioned and being accessed from an anonymous " + "context."}}); static_assert( Reasons.size() == net::CookieInclusionStatus::ExclusionReasonBitset::kValueCount, diff --git a/shell/browser/extensions/api/scripting/scripting_api.cc b/shell/browser/extensions/api/scripting/scripting_api.cc index b8061016a28eb..b140bd69b6064 100644 --- a/shell/browser/extensions/api/scripting/scripting_api.cc +++ b/shell/browser/extensions/api/scripting/scripting_api.cc @@ -470,7 +470,9 @@ ExtensionFunction::ResponseAction ScriptingExecuteScriptFunction::Run() { constexpr bool kRequiresLocalization = false; std::string error; if (!CheckAndLoadFiles( - std::move(*injection_.files), *extension(), kRequiresLocalization, + std::move(*injection_.files), + script_parsing::ContentScriptType::kJs, *extension(), + kRequiresLocalization, base::BindOnce(&ScriptingExecuteScriptFunction::DidLoadResources, this), &error)) { @@ -609,7 +611,9 @@ ExtensionFunction::ResponseAction ScriptingInsertCSSFunction::Run() { constexpr bool kRequiresLocalization = true; std::string error; if (!CheckAndLoadFiles( - std::move(*injection_.files), *extension(), kRequiresLocalization, + std::move(*injection_.files), + script_parsing::ContentScriptType::kCss, *extension(), + kRequiresLocalization, base::BindOnce(&ScriptingInsertCSSFunction::DidLoadResources, this), &error)) { return RespondNow(Error(std::move(error))); @@ -723,8 +727,9 @@ ExtensionFunction::ResponseAction ScriptingRemoveCSSFunction::Run() { if (injection.files) { std::vector resources; - if (!scripting::GetFileResources(*injection.files, *extension(), &resources, - &error)) { + if (!scripting::GetFileResources(*injection.files, + script_parsing::ContentScriptType::kCss, + *extension(), &resources, &error)) { return RespondNow(Error(std::move(error))); } diff --git a/shell/browser/javascript_environment.cc b/shell/browser/javascript_environment.cc index e051083ba4538..c0d639326bd5c 100644 --- a/shell/browser/javascript_environment.cc +++ b/shell/browser/javascript_environment.cc @@ -32,8 +32,9 @@ namespace electron { namespace { -gin::IsolateHolder CreateIsolateHolder(v8::Isolate* isolate, - size_t* max_young_generation_size) { +std::unique_ptr CreateIsolateHolder( + v8::Isolate* isolate, + size_t* max_young_generation_size) { std::unique_ptr create_params = gin::IsolateHolder::getDefaultIsolateParams(); // The value is needed to adjust heap limit when capturing @@ -45,14 +46,12 @@ gin::IsolateHolder CreateIsolateHolder(v8::Isolate* isolate, // This is necessary for important aspects of Node.js // including heap and cpu profilers to function properly. - return {base::SingleThreadTaskRunner::GetCurrentDefault(), - gin::IsolateHolder::kSingleThread, - gin::IsolateHolder::IsolateType::kUtility, - std::move(create_params), - gin::IsolateHolder::IsolateCreationMode::kNormal, - nullptr, - nullptr, - isolate}; + return std::make_unique( + base::SingleThreadTaskRunner::GetCurrentDefault(), + gin::IsolateHolder::kSingleThread, + gin::IsolateHolder::IsolateType::kUtility, std::move(create_params), + gin::IsolateHolder::IsolateCreationMode::kNormal, nullptr, nullptr, + isolate); } } // namespace @@ -62,8 +61,8 @@ JavascriptEnvironment::JavascriptEnvironment(uv_loop_t* event_loop, : isolate_holder_{CreateIsolateHolder( Initialize(event_loop, setup_wasm_streaming), &max_young_generation_size_)}, - isolate_{isolate_holder_.isolate()}, - locker_{isolate_} { + isolate_{isolate_holder_->isolate()}, + locker_{std::make_unique(isolate_)} { isolate_->Enter(); v8::HandleScope scope(isolate_); @@ -83,6 +82,12 @@ JavascriptEnvironment::~JavascriptEnvironment() { isolate_->Exit(); g_isolate = nullptr; + // Deinit gin::IsolateHolder prior to calling NodePlatform::UnregisterIsolate. + // Otherwise cppgc::internal::Sweeper::Start will try to request a task runner + // from the NodePlatform with an already unregistered isolate. + locker_.reset(); + isolate_holder_.reset(); + platform_->UnregisterIsolate(isolate_); } @@ -139,7 +144,7 @@ v8::Isolate* JavascriptEnvironment::GetIsolate() { void JavascriptEnvironment::CreateMicrotasksRunner() { DCHECK(!microtasks_runner_); microtasks_runner_ = std::make_unique(isolate()); - isolate_holder_.WillCreateMicrotasksRunner(); + isolate_holder_->WillCreateMicrotasksRunner(); base::CurrentThread::Get()->AddTaskObserver(microtasks_runner_.get()); } @@ -148,7 +153,7 @@ void JavascriptEnvironment::DestroyMicrotasksRunner() { // Should be called before running gin_helper::CleanedUpAtExit::DoCleanup. // This helps to signal wrappable finalizer callbacks to not act on freed // parameters. - isolate_holder_.WillDestroyMicrotasksRunner(); + isolate_holder_->WillDestroyMicrotasksRunner(); { v8::HandleScope scope(isolate_); gin_helper::CleanedUpAtExit::DoCleanup(); diff --git a/shell/browser/javascript_environment.h b/shell/browser/javascript_environment.h index ed78e980f7d71..80c2affee4619 100644 --- a/shell/browser/javascript_environment.h +++ b/shell/browser/javascript_environment.h @@ -47,13 +47,13 @@ class JavascriptEnvironment { std::unique_ptr platform_; size_t max_young_generation_size_ = 0; - gin::IsolateHolder isolate_holder_; + std::unique_ptr isolate_holder_; // owned-by: isolate_holder_ const raw_ptr isolate_; // depends-on: isolate_ - const v8::Locker locker_; + std::unique_ptr locker_; std::unique_ptr microtasks_runner_; }; diff --git a/shell/browser/mac/electron_application.mm b/shell/browser/mac/electron_application.mm index 0f8cb520e947a..797ae197a89ed 100644 --- a/shell/browser/mac/electron_application.mm +++ b/shell/browser/mac/electron_application.mm @@ -193,7 +193,9 @@ - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute { - (id)accessibilityAttributeValue:(NSString*)attribute { if ([attribute isEqualToString:@"AXManualAccessibility"]) { auto* ax_state = content::BrowserAccessibilityState::GetInstance(); - return [NSNumber numberWithBool:ax_state->IsAccessibleBrowser()]; + bool is_accessible_browser = + ax_state->GetAccessibilityMode() == ui::kAXModeComplete; + return [NSNumber numberWithBool:is_accessible_browser]; } return [super accessibilityAttributeValue:attribute]; @@ -209,9 +211,9 @@ - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { if ([attribute isEqualToString:@"AXEnhancedUserInterface"] || is_manual_ax) { auto* ax_state = content::BrowserAccessibilityState::GetInstance(); if ([value boolValue]) { - ax_state->OnScreenReaderDetected(); + ax_state->EnableProcessAccessibility(); } else { - ax_state->DisableAccessibility(); + ax_state->DisableProcessAccessibility(); } electron::Browser::Get()->OnAccessibilitySupportChanged(); diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index b3fee341e6740..0d675b4f70dc6 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -58,7 +58,7 @@ class BrowserView; } #if BUILDFLAG(IS_MAC) -typedef NSView* NativeWindowHandle; +typedef gfx::NativeView NativeWindowHandle; #else typedef gfx::AcceleratedWidget NativeWindowHandle; #endif diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index dbeceb836ea77..507a6b6efb56c 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1193,11 +1193,11 @@ static bool FromV8(v8::Isolate* isolate, } gfx::NativeView NativeWindowMac::GetNativeView() const { - return [window_ contentView]; + return gfx::NativeView([window_ contentView]); } gfx::NativeWindow NativeWindowMac::GetNativeWindow() const { - return window_; + return gfx::NativeWindow(window_); } gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() const { @@ -1220,7 +1220,7 @@ static bool FromV8(v8::Isolate* isolate, } NativeWindowHandle NativeWindowMac::GetNativeWindowHandle() const { - return [window_ contentView]; + return GetNativeView(); } void NativeWindowMac::SetProgressBar(double progress, @@ -1451,7 +1451,7 @@ static bool FromV8(v8::Isolate* isolate, // other views. vibrant_native_view_host_ = rootView->AddChildViewAt( std::make_unique(), 0); - vibrant_native_view_host_->Attach(vibrantView); + vibrant_native_view_host_->Attach(gfx::NativeView(vibrantView)); rootView->DeprecatedLayoutImmediately(); diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 64961174313e8..4b324bc1c3f6b 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -281,8 +281,8 @@ bool NativeWindowViews::PreHandleMSG(UINT message, checked_for_a11y_support_ = true; auto* const axState = content::BrowserAccessibilityState::GetInstance(); - if (axState && !axState->IsAccessibleBrowser()) { - axState->OnScreenReaderDetected(); + if (axState && axState->GetAccessibilityMode() != ui::kAXModeComplete) { + axState->EnableProcessAccessibility(); Browser::Get()->OnAccessibilitySupportChanged(); } diff --git a/shell/browser/net/system_network_context_manager.cc b/shell/browser/net/system_network_context_manager.cc index 73cea7fb0b461..1b2819ebf441f 100644 --- a/shell/browser/net/system_network_context_manager.cc +++ b/shell/browser/net/system_network_context_manager.cc @@ -271,6 +271,7 @@ void SystemNetworkContextManager::OnNetworkServiceCreated( // NetworkContext is created, but before anything has the chance to use it. content::GetNetworkService()->ConfigureStubHostResolver( base::FeatureList::IsEnabled(net::features::kAsyncDns), + base::FeatureList::IsEnabled(net::features::kHappyEyeballsV3), default_secure_dns_mode, doh_config, additional_dns_query_types_enabled); // The OSCrypt keys are process bound, so if network service is out of diff --git a/shell/browser/net/url_loader_network_observer.h b/shell/browser/net/url_loader_network_observer.h index 11bcf585a3708..9f5e335b8b152 100644 --- a/shell/browser/net/url_loader_network_observer.h +++ b/shell/browser/net/url_loader_network_observer.h @@ -70,11 +70,15 @@ class URLLoaderNetworkObserver const std::optional& private_network_device_id, const std::optional& private_network_device_name, OnPrivateNetworkAccessPermissionRequiredCallback callback) override {} + void OnLocalNetworkAccessPermissionRequired( + OnLocalNetworkAccessPermissionRequiredCallback callback) override {} void OnUrlLoaderConnectedToPrivateNetwork( const GURL& request_url, network::mojom::IPAddressSpace response_address_space, network::mojom::IPAddressSpace client_address_space, network::mojom::IPAddressSpace target_address_space) override {} + void OnAdAuctionEventRecordHeaderReceived( + network::AdAuctionEventRecord event_record) override {} void Clone( mojo::PendingReceiver observer) override; diff --git a/shell/browser/osr/osr_render_widget_host_view.h b/shell/browser/osr/osr_render_widget_host_view.h index 2bd201bcdda72..f46c7a6c91810 100644 --- a/shell/browser/osr/osr_render_widget_host_view.h +++ b/shell/browser/osr/osr_render_widget_host_view.h @@ -150,7 +150,8 @@ class OffScreenRenderWidgetHostView void TransformPointToRootSurface(gfx::PointF* point) override {} gfx::Rect GetBoundsInRootWindow() override; std::optional GetDisplayFeature() override; - void SetDisplayFeatureForTesting( + void DisableDisplayFeatureOverrideForEmulation() override {} + void OverrideDisplayFeatureForEmulation( const content::DisplayFeature* display_feature) override {} void NotifyHostAndDelegateOnWasShown( blink::mojom::RecordContentToVisibleTimeRequestPtr) final; diff --git a/shell/browser/osr/osr_web_contents_view_mac.mm b/shell/browser/osr/osr_web_contents_view_mac.mm index c4e6fc01f8e1e..f4444dffc0298 100644 --- a/shell/browser/osr/osr_web_contents_view_mac.mm +++ b/shell/browser/osr/osr_web_contents_view_mac.mm @@ -31,15 +31,15 @@ - (void)drawRect:(NSRect)dirtyRect { namespace electron { gfx::NativeView OffScreenWebContentsView::GetNativeView() const { - return offScreenView_; + return gfx::NativeView(offScreenView_); } gfx::NativeView OffScreenWebContentsView::GetContentNativeView() const { - return offScreenView_; + return gfx::NativeView(offScreenView_); } gfx::NativeWindow OffScreenWebContentsView::GetTopLevelNativeWindow() const { - return [offScreenView_ window]; + return gfx::NativeWindow([offScreenView_ window]); } void OffScreenWebContentsView::PlatformCreate() { diff --git a/shell/browser/ui/devtools_manager_delegate.cc b/shell/browser/ui/devtools_manager_delegate.cc index 2825db7e25268..2a5f9880817c8 100644 --- a/shell/browser/ui/devtools_manager_delegate.cc +++ b/shell/browser/ui/devtools_manager_delegate.cc @@ -124,7 +124,8 @@ void DevToolsManagerDelegate::HandleCommand( scoped_refptr DevToolsManagerDelegate::CreateNewTarget(const GURL& url, - TargetType target_type) { + TargetType target_type, + bool new_window) { return nullptr; } diff --git a/shell/browser/ui/devtools_manager_delegate.h b/shell/browser/ui/devtools_manager_delegate.h index 7a26f43b0cb96..b19d57a03dc46 100644 --- a/shell/browser/ui/devtools_manager_delegate.h +++ b/shell/browser/ui/devtools_manager_delegate.h @@ -33,7 +33,8 @@ class DevToolsManagerDelegate : public content::DevToolsManagerDelegate { NotHandledCallback callback) override; scoped_refptr CreateNewTarget( const GURL& url, - TargetType target_type) override; + TargetType target_type, + bool new_window) override; std::string GetDiscoveryPageHTML() override; bool HasBundledFrontendResources() override; content::BrowserContext* GetDefaultBrowserContext() override; diff --git a/shell/browser/ui/webui/accessibility_ui.cc b/shell/browser/ui/webui/accessibility_ui.cc index 0a730a6b727d4..38a401dba0716 100644 --- a/shell/browser/ui/webui/accessibility_ui.cc +++ b/shell/browser/ui/webui/accessibility_ui.cc @@ -160,8 +160,8 @@ void HandleAccessibilityRequestCallback( static_cast(current_context)->prefs(); ui::AXMode mode = content::BrowserAccessibilityState::GetInstance()->GetAccessibilityMode(); - bool is_native_enabled = content::BrowserAccessibilityState::GetInstance() - ->IsRendererAccessibilityEnabled(); + bool is_a11y_allowed = content::BrowserAccessibilityState::GetInstance() + ->IsAccessibilityAllowed(); bool native = mode.has_mode(ui::AXMode::kNativeAPIs); bool web = mode.has_mode(ui::AXMode::kWebContents); bool text = mode.has_mode(ui::AXMode::kInlineTextBoxes); @@ -171,12 +171,12 @@ void HandleAccessibilityRequestCallback( // The "native" and "web" flags are disabled if // --disable-renderer-accessibility is set. - data.Set(kNative, is_native_enabled ? (native ? kOn : kOff) : kDisabled); - data.Set(kWeb, is_native_enabled ? (web ? kOn : kOff) : kDisabled); + data.Set(kNative, is_a11y_allowed ? (native ? kOn : kOff) : kDisabled); + data.Set(kWeb, is_a11y_allowed ? (web ? kOn : kOff) : kDisabled); // The "text", "extendedProperties" and "html" flags are only // meaningful if "web" is enabled. - bool is_web_enabled = is_native_enabled && web; + bool is_web_enabled = is_a11y_allowed && web; data.Set(kText, is_web_enabled ? (text ? kOn : kOff) : kDisabled); data.Set(kExtendedProperties, is_web_enabled ? (extendedProperties ? kOn : kOff) : kDisabled); @@ -245,7 +245,7 @@ void HandleAccessibilityRequestCallback( } base::Value::Dict descriptor = BuildTargetDescriptor(rvh); - descriptor.Set(kNative, is_native_enabled); + descriptor.Set(kNative, is_a11y_allowed); descriptor.Set(kExtendedProperties, is_web_enabled && extendedProperties); descriptor.Set(kWeb, is_web_enabled); page_list.Append(std::move(descriptor)); diff --git a/shell/common/api/electron_api_url_loader.h b/shell/common/api/electron_api_url_loader.h index 4fb39f3acdbd7..92e6ffb8c723d 100644 --- a/shell/common/api/electron_api_url_loader.h +++ b/shell/common/api/electron_api_url_loader.h @@ -106,6 +106,8 @@ class SimpleURLLoaderWrapper final const std::optional& private_network_device_id, const std::optional& private_network_device_name, OnPrivateNetworkAccessPermissionRequiredCallback callback) override {} + void OnLocalNetworkAccessPermissionRequired( + OnLocalNetworkAccessPermissionRequiredCallback callback) override {} void OnClearSiteData( const GURL& url, const std::string& header_value, @@ -134,6 +136,8 @@ class SimpleURLLoaderWrapper final network::mojom::IPAddressSpace response_address_space, network::mojom::IPAddressSpace client_address_space, network::mojom::IPAddressSpace target_address_space) override {} + void OnAdAuctionEventRecordHeaderReceived( + network::AdAuctionEventRecord event_record) override {} scoped_refptr GetURLLoaderFactoryForURL( const GURL& url); From 1046842f731ea317e7f2fb16098039a5df73d2f3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:29:37 -0500 Subject: [PATCH 122/339] refactor: migrate to `View::AddChildView(std::unique_ptr)` (#46541) * refactor: use AddChildView(std::unique_ptr) in OpaqueFrameView::CreateButton() Xref: https://issues.chromium.org/issues/40485510 Co-authored-by: Charles Kerr * refactor: use AddChildView(std::unique_ptr) in MenuBar::RebuildChildren() Co-authored-by: Charles Kerr * refactor: use AddChildView(std::unique_ptr) for ClientFrameViewLinux labels Co-authored-by: Charles Kerr * refactor: use AddChildView(std::unique_ptr) for ClientFrameViewLinux buttons Co-authored-by: Charles Kerr * refactor: use AddChildView(std::unique_ptr) in AutofillPopupView Co-authored-by: Charles Kerr * refactor: use AddChildViewRaw() to flag the edge cases that we still need to fix Co-authored-by: Charles Kerr * chore: use west coast const for consistency Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_mac.mm | 2 +- shell/browser/native_window_views.cc | 2 +- .../ui/inspectable_web_contents_view.cc | 4 +-- shell/browser/ui/views/autofill_popup_view.cc | 5 ++-- shell/browser/ui/views/autofill_popup_view.h | 1 - .../ui/views/client_frame_view_linux.cc | 25 +++++++++---------- .../ui/views/client_frame_view_linux.h | 4 +-- shell/browser/ui/views/menu_bar.cc | 4 +-- shell/browser/ui/views/opaque_frame_view.cc | 5 ++-- .../ui/views/win_caption_button_container.h | 10 ++++---- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 507a6b6efb56c..f021e6cc742cc 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -323,7 +323,7 @@ static bool FromV8(v8::Isolate* isolate, root_view->RemoveChildView(content_view()); set_content_view(view); - root_view->AddChildView(content_view()); + root_view->AddChildViewRaw(content_view()); root_view->DeprecatedLayoutImmediately(); } diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 5b6fda2a6d417..aaaa136565d9b 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -480,7 +480,7 @@ void NativeWindowViews::SetContentView(views::View* view) { } set_content_view(view); focused_view_ = view; - root_view_.GetMainView()->AddChildView(content_view()); + root_view_.GetMainView()->AddChildViewRaw(content_view()); root_view_.GetMainView()->DeprecatedLayoutImmediately(); } diff --git a/shell/browser/ui/inspectable_web_contents_view.cc b/shell/browser/ui/inspectable_web_contents_view.cc index e61f008414df4..7389adbf3f9bf 100644 --- a/shell/browser/ui/inspectable_web_contents_view.cc +++ b/shell/browser/ui/inspectable_web_contents_view.cc @@ -90,8 +90,8 @@ InspectableWebContentsView::InspectableWebContentsView( } devtools_web_view_->SetVisible(false); - AddChildView(devtools_web_view_.get()); - AddChildView(GetContentsView()); + AddChildViewRaw(devtools_web_view_.get()); + AddChildViewRaw(GetContentsView()); } InspectableWebContentsView::~InspectableWebContentsView() { diff --git a/shell/browser/ui/views/autofill_popup_view.cc b/shell/browser/ui/views/autofill_popup_view.cc index 289bba3f4c36e..b5c8f03e005cc 100644 --- a/shell/browser/ui/views/autofill_popup_view.cc +++ b/shell/browser/ui/views/autofill_popup_view.cc @@ -215,9 +215,10 @@ void AutofillPopupView::CreateChildViews() { RemoveAllChildViews(); for (int i = 0; i < popup_->line_count(); ++i) { - auto* child_view = new AutofillPopupChildView(popup_->value_at(i)); + auto child_view = + std::make_unique(popup_->value_at(i)); child_view->set_drag_controller(this); - AddChildView(child_view); + AddChildView(std::move(child_view)); } } diff --git a/shell/browser/ui/views/autofill_popup_view.h b/shell/browser/ui/views/autofill_popup_view.h index 2d312e0a6bd62..d27497013010d 100644 --- a/shell/browser/ui/views/autofill_popup_view.h +++ b/shell/browser/ui/views/autofill_popup_view.h @@ -51,7 +51,6 @@ class AutofillPopupChildView : public views::View { AutofillPopupChildView(const AutofillPopupChildView&) = delete; AutofillPopupChildView& operator=(const AutofillPopupChildView&) = delete; - private: ~AutofillPopupChildView() override = default; std::u16string suggestion_; diff --git a/shell/browser/ui/views/client_frame_view_linux.cc b/shell/browser/ui/views/client_frame_view_linux.cc index 98f6f0b785b21..5ec484813a11d 100644 --- a/shell/browser/ui/views/client_frame_view_linux.cc +++ b/shell/browser/ui/views/client_frame_view_linux.cc @@ -84,20 +84,20 @@ ClientFrameViewLinux::ClientFrameViewLinux() views::FrameButton::kMaximize, views::FrameButton::kClose} { for (auto& button : nav_buttons_) { - button.button = new views::ImageButton(); - button.button->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); - button.button->SetAccessibleName( + auto image_button = std::make_unique(); + image_button->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); + image_button->SetAccessibleName( l10n_util::GetStringUTF16(button.accessibility_id)); - AddChildView(button.button); + button.button = AddChildView(std::move(image_button)); } - title_ = new views::Label(); - title_->SetSubpixelRenderingEnabled(false); - title_->SetAutoColorReadabilityEnabled(false); - title_->SetHorizontalAlignment(gfx::ALIGN_CENTER); - title_->SetVerticalAlignment(gfx::ALIGN_MIDDLE); - title_->SetTextStyle(views::style::STYLE_TAB_ACTIVE); - AddChildView(title_); + auto title = std::make_unique(); + title->SetSubpixelRenderingEnabled(false); + title->SetAutoColorReadabilityEnabled(false); + title->SetHorizontalAlignment(gfx::ALIGN_CENTER); + title->SetVerticalAlignment(gfx::ALIGN_MIDDLE); + title->SetTextStyle(views::style::STYLE_TAB_ACTIVE); + title_ = AddChildView(std::move(title)); native_theme_observer_.Observe(theme_); @@ -293,8 +293,7 @@ void ClientFrameViewLinux::Layout(PassKey) { title_bounds.Inset(theme_values_.title_padding); title_->SetVisible(true); - title_->SetBounds(title_bounds.x(), title_bounds.y(), title_bounds.width(), - title_bounds.height()); + title_->SetBoundsRect(title_bounds); } void ClientFrameViewLinux::OnPaint(gfx::Canvas* canvas) { diff --git a/shell/browser/ui/views/client_frame_view_linux.h b/shell/browser/ui/views/client_frame_view_linux.h index 9bb03a2472ba5..0d8f4050b9500 100644 --- a/shell/browser/ui/views/client_frame_view_linux.h +++ b/shell/browser/ui/views/client_frame_view_linux.h @@ -95,7 +95,7 @@ class ClientFrameViewLinux : public FramelessView, void (views::Widget::*callback)(); int accessibility_id; int hit_test_id; - RAW_PTR_EXCLUSION views::ImageButton* button{nullptr}; + raw_ptr button = {}; }; struct ThemeValues { @@ -132,7 +132,7 @@ class ClientFrameViewLinux : public FramelessView, raw_ptr theme_; ThemeValues theme_values_; - RAW_PTR_EXCLUSION views::Label* title_; + raw_ptr title_; std::unique_ptr nav_button_provider_; std::array nav_buttons_; diff --git a/shell/browser/ui/views/menu_bar.cc b/shell/browser/ui/views/menu_bar.cc index 28a19854fe698..d7a67317c6ee8 100644 --- a/shell/browser/ui/views/menu_bar.cc +++ b/shell/browser/ui/views/menu_bar.cc @@ -229,11 +229,11 @@ void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) { void MenuBar::RebuildChildren() { RemoveAllChildViews(); for (size_t i = 0, n = GetItemCount(); i < n; ++i) { - auto* button = new SubmenuButton( + auto button = std::make_unique( base::BindRepeating(&MenuBar::ButtonPressed, base::Unretained(this), i), menu_model_->GetLabelAt(i), background_color_); button->SetID(i); - AddChildView(button); + AddChildView(std::move(button)); } UpdateViewColors(); } diff --git a/shell/browser/ui/views/opaque_frame_view.cc b/shell/browser/ui/views/opaque_frame_view.cc index 6aef44bdeedf9..2639daba9d82e 100644 --- a/shell/browser/ui/views/opaque_frame_view.cc +++ b/shell/browser/ui/views/opaque_frame_view.cc @@ -297,7 +297,7 @@ views::Button* OpaqueFrameView::CreateButton( int ht_component, const gfx::VectorIcon& icon_image, views::Button::PressedCallback callback) { - views::FrameCaptionButton* button = new views::FrameCaptionButton( + auto button = std::make_unique( views::Button::PressedCallback(), icon_type, ht_component); button->SetImage(button->GetIcon(), views::FrameCaptionButton::Animate::kNo, icon_image); @@ -306,12 +306,11 @@ views::Button* OpaqueFrameView::CreateButton( button->SetCallback(std::move(callback)); button->SetAccessibleName(l10n_util::GetStringUTF16(accessibility_string_id)); button->SetID(view_id); - AddChildView(button); button->SetPaintToLayer(); button->layer()->SetFillsBoundsOpaquely(false); - return button; + return AddChildView(std::move(button)); } gfx::Insets OpaqueFrameView::FrameBorderInsets(bool restored) const { diff --git a/shell/browser/ui/views/win_caption_button_container.h b/shell/browser/ui/views/win_caption_button_container.h index 7c2390b7da20d..bef48521a0944 100644 --- a/shell/browser/ui/views/win_caption_button_container.h +++ b/shell/browser/ui/views/win_caption_button_container.h @@ -67,11 +67,11 @@ class WinCaptionButtonContainer : public views::View, void OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) override; - raw_ptr const frame_view_; - raw_ptr const minimize_button_; - raw_ptr const maximize_button_; - raw_ptr const restore_button_; - raw_ptr const close_button_; + const raw_ptr frame_view_; + const raw_ptr minimize_button_; + const raw_ptr maximize_button_; + const raw_ptr restore_button_; + const raw_ptr close_button_; base::ScopedObservation widget_observation_{this}; From de933233e62c87fa57506cd2d828a47f2a5b4ead Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 07:08:15 -0500 Subject: [PATCH 123/339] refactor: make `TrackableObjectBase::weak_map_id_` const (#46557) refactor: make TrackableObjectBase::weak_map_id_ const simplify declaration and initialization Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/common/gin_helper/trackable_object.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/shell/common/gin_helper/trackable_object.h b/shell/common/gin_helper/trackable_object.h index 7a7e6350dd995..aa040aec58891 100644 --- a/shell/common/gin_helper/trackable_object.h +++ b/shell/common/gin_helper/trackable_object.h @@ -42,11 +42,12 @@ class TrackableObjectBase : public CleanedUpAtExit { // Returns a closure that can destroy the native class. base::OnceClosure GetDestroyClosure(); - int32_t weak_map_id_ = 0; - private: void Destroy(); + static inline int32_t next_id_ = 0; + const int32_t weak_map_id_ = ++next_id_; + base::WeakPtrFactory weak_factory_{this}; }; @@ -111,26 +112,21 @@ class TrackableObject : public TrackableObjectBase, public EventEmitter { } protected: - TrackableObject() { weak_map_id_ = ++next_id_; } - + TrackableObject() = default; ~TrackableObject() override { RemoveFromWeakMap(); } void InitWith(v8::Isolate* isolate, v8::Local wrapper) override { if (!weak_map_) { weak_map_ = new electron::KeyWeakMap; } - weak_map_->Set(isolate, weak_map_id_, wrapper); + weak_map_->Set(isolate, weak_map_id(), wrapper); gin_helper::WrappableBase::InitWith(isolate, wrapper); } private: - static int32_t next_id_; static electron::KeyWeakMap* weak_map_; // leaked on purpose }; -template -int32_t TrackableObject::next_id_ = 0; - template electron::KeyWeakMap* TrackableObject::weak_map_ = nullptr; From feaa9ec5b9d1c18e2d51c79ffb7d20d0bfbd8b19 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:53:17 -0500 Subject: [PATCH 124/339] refactor: make `api::View` methods const, private (#46551) * refactor: make api::View::GetBounds() const Co-authored-by: Charles Kerr * refactor: make api::View::OnViewBoundsChanged() private refactor: make api::View::OnViewIsDeleting() private refactor: make api::View::OnChildViewRemoved() private Co-authored-by: Charles Kerr * refactor: make ChildPair private Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_view.cc | 2 +- shell/browser/api/electron_api_view.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/shell/browser/api/electron_api_view.cc b/shell/browser/api/electron_api_view.cc index ef37cb63c2db1..661347396826b 100644 --- a/shell/browser/api/electron_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -280,7 +280,7 @@ void View::SetBounds(const gfx::Rect& bounds) { view_->SetBoundsRect(bounds); } -gfx::Rect View::GetBounds() { +gfx::Rect View::GetBounds() const { if (!view_) return {}; return view_->bounds(); diff --git a/shell/browser/api/electron_api_view.h b/shell/browser/api/electron_api_view.h index d63f5abc83b76..298a5772a74ee 100644 --- a/shell/browser/api/electron_api_view.h +++ b/shell/browser/api/electron_api_view.h @@ -21,8 +21,6 @@ class Handle; namespace electron::api { -using ChildPair = std::pair, v8::Global>; - class View : public gin_helper::EventEmitter, private views::ViewObserver { public: @@ -39,7 +37,7 @@ class View : public gin_helper::EventEmitter, void RemoveChildView(gin::Handle child); void SetBounds(const gfx::Rect& bounds); - gfx::Rect GetBounds(); + gfx::Rect GetBounds() const; void SetLayout(v8::Isolate* isolate, v8::Local value); std::vector> GetChildren(); void SetBackgroundColor(std::optional color); @@ -47,12 +45,6 @@ class View : public gin_helper::EventEmitter, void SetVisible(bool visible); bool GetVisible() const; - // views::ViewObserver - void OnViewBoundsChanged(views::View* observed_view) override; - void OnViewIsDeleting(views::View* observed_view) override; - void OnChildViewRemoved(views::View* observed_view, - views::View* child) override; - views::View* view() const { return view_; } std::optional border_radius() const { return border_radius_; } @@ -69,6 +61,14 @@ class View : public gin_helper::EventEmitter, void set_delete_view(bool should) { delete_view_ = should; } private: + using ChildPair = std::pair, v8::Global>; + + // views::ViewObserver + void OnViewBoundsChanged(views::View* observed_view) override; + void OnViewIsDeleting(views::View* observed_view) override; + void OnChildViewRemoved(views::View* observed_view, + views::View* child) override; + void ApplyBorderRadius(); void ReorderChildView(gin::Handle child, size_t index); From 3fdcd38b8b270b091920a0e1cd2fc347a346fd49 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:44:15 -0500 Subject: [PATCH 125/339] refactor: simplify some `BaseWindow` JS getters (#46565) * refactor: return a std::array from BaseWindow::GetMaximumSize() Co-authored-by: Charles Kerr * refactor: return a std::array from BaseWindow::GetMinimumSize() Co-authored-by: Charles Kerr * refactor: return a std::array from BaseWindow::GetPosition() Co-authored-by: Charles Kerr * refactor: return a std::array from BaseWindow::GetSize() Co-authored-by: Charles Kerr * refactor: return a std::array from BaseWindow::GetContentSize() Co-authored-by: Charles Kerr * refactor: extract helper method ToArray(const gfx::Size) Co-authored-by: Charles Kerr * refactor: #include correctness Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_base_window.cc | 48 +++++++------------ shell/browser/api/electron_api_base_window.h | 11 +++-- shell/common/gin_converters/std_converter.h | 1 + 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index f5e7e39f80b5c..848cc6cc2c9f8 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -79,6 +79,14 @@ v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { return buffer.ToLocalChecked(); } +[[nodiscard]] constexpr std::array ToArray(const gfx::Size size) { + return {size.width(), size.height()}; +} + +[[nodiscard]] constexpr std::array ToArray(const gfx::Point point) { + return {point.x(), point.y()}; +} + } // namespace BaseWindow::BaseWindow(v8::Isolate* isolate, @@ -467,12 +475,8 @@ void BaseWindow::SetSize(int width, int height, gin_helper::Arguments* args) { window_->SetSize(size, animate); } -std::vector BaseWindow::GetSize() const { - std::vector result(2); - gfx::Size size = window_->GetSize(); - result[0] = size.width(); - result[1] = size.height(); - return result; +std::array BaseWindow::GetSize() const { + return ToArray(window_->GetSize()); } void BaseWindow::SetContentSize(int width, @@ -483,36 +487,24 @@ void BaseWindow::SetContentSize(int width, window_->SetContentSize(gfx::Size(width, height), animate); } -std::vector BaseWindow::GetContentSize() const { - std::vector result(2); - gfx::Size size = window_->GetContentSize(); - result[0] = size.width(); - result[1] = size.height(); - return result; +std::array BaseWindow::GetContentSize() const { + return ToArray(window_->GetContentSize()); } void BaseWindow::SetMinimumSize(int width, int height) { window_->SetMinimumSize(gfx::Size(width, height)); } -std::vector BaseWindow::GetMinimumSize() const { - std::vector result(2); - gfx::Size size = window_->GetMinimumSize(); - result[0] = size.width(); - result[1] = size.height(); - return result; +std::array BaseWindow::GetMinimumSize() const { + return ToArray(window_->GetMinimumSize()); } void BaseWindow::SetMaximumSize(int width, int height) { window_->SetMaximumSize(gfx::Size(width, height)); } -std::vector BaseWindow::GetMaximumSize() const { - std::vector result(2); - gfx::Size size = window_->GetMaximumSize(); - result[0] = size.width(); - result[1] = size.height(); - return result; +std::array BaseWindow::GetMaximumSize() const { + return ToArray(window_->GetMaximumSize()); } void BaseWindow::SetSheetOffset(double offsetY, gin_helper::Arguments* args) { @@ -594,12 +586,8 @@ void BaseWindow::SetPosition(int x, int y, gin_helper::Arguments* args) { window_->SetPosition(gfx::Point(x, y), animate); } -std::vector BaseWindow::GetPosition() const { - std::vector result(2); - gfx::Point pos = window_->GetPosition(); - result[0] = pos.x(); - result[1] = pos.y(); - return result; +std::array BaseWindow::GetPosition() const { + return ToArray(window_->GetPosition()); } void BaseWindow::MoveAbove(const std::string& sourceId, gin_helper::Arguments* args) { diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 52e0285a6530d..1ee4443f9cdb2 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BASE_WINDOW_H_ #define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BASE_WINDOW_H_ +#include #include #include #include @@ -119,17 +120,17 @@ class BaseWindow : public gin_helper::TrackableObject, void SetBounds(const gfx::Rect& bounds, gin_helper::Arguments* args); gfx::Rect GetBounds() const; void SetSize(int width, int height, gin_helper::Arguments* args); - std::vector GetSize() const; + std::array GetSize() const; void SetContentSize(int width, int height, gin_helper::Arguments* args); - std::vector GetContentSize() const; + std::array GetContentSize() const; void SetContentBounds(const gfx::Rect& bounds, gin_helper::Arguments* args); gfx::Rect GetContentBounds() const; bool IsNormal() const; gfx::Rect GetNormalBounds() const; void SetMinimumSize(int width, int height); - std::vector GetMinimumSize() const; + std::array GetMinimumSize() const; void SetMaximumSize(int width, int height); - std::vector GetMaximumSize() const; + std::array GetMaximumSize() const; void SetSheetOffset(double offsetY, gin_helper::Arguments* args); void SetResizable(bool resizable); bool IsResizable() const; @@ -149,7 +150,7 @@ class BaseWindow : public gin_helper::TrackableObject, bool IsAlwaysOnTop() const; void Center(); void SetPosition(int x, int y, gin_helper::Arguments* args); - std::vector GetPosition() const; + std::array GetPosition() const; void SetTitle(const std::string& title); std::string GetTitle() const; void SetAccessibleTitle(const std::string& title); diff --git a/shell/common/gin_converters/std_converter.h b/shell/common/gin_converters/std_converter.h index 4ea06b487de7a..08704341a8a22 100644 --- a/shell/common/gin_converters/std_converter.h +++ b/shell/common/gin_converters/std_converter.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_STD_CONVERTER_H_ #define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_STD_CONVERTER_H_ +#include #include #include #include From 4f54c91ee2b7f6cc7e4ee2b979dec14f2820f223 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:49:48 -0700 Subject: [PATCH 126/339] chore: bump chromium to 136.0.7103.17 (36-x-y) (#46546) * chore: bump chromium in DEPS to 136.0.7103.17 * chore: update patches * chore: update filenames.libcxx.gni --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- filenames.libcxx.gni | 7 ++++ ..._scheduler_throttling_per_renderview.patch | 6 ++-- patches/chromium/blink_local_frame.patch | 8 ++--- .../build_add_electron_tracing_category.patch | 2 +- ..._depend_on_packed_resource_integrity.patch | 12 +++---- .../build_libc_as_static_library.patch | 6 ++-- patches/chromium/can_create_window.patch | 18 +++++----- ...hore_add_electron_deps_to_gitignores.patch | 2 +- ...fy_chromium_handling_of_mouse_events.patch | 12 +++---- .../chromium/chore_partial_revert_of.patch | 4 +-- ...screationoverridden_with_full_params.patch | 12 +++---- ..._conflicting_allow_unsafe_libc_calls.patch | 2 +- ...e_browser_v8_snapshot_file_name_fuse.patch | 8 ++--- .../chromium/enable_reset_aspect_ratio.patch | 8 ++--- .../extend_apply_webpreferences.patch | 6 ++-- ...e_launch_options_for_service_process.patch | 6 ++-- ...moothing_css_rule_and_blink_painting.patch | 8 ++--- ...screen_rendering_with_viz_compositor.patch | 18 +++++----- ..._raw_response_headers_from_urlloader.patch | 4 +-- ...ivate_background_material_on_windows.patch | 6 ++-- ...ables_headless_mode_on_native_widget.patch | 4 +-- .../fix_aspect_ratio_with_max_size.patch | 4 +-- ...ding_non-standard_schemes_in_iframes.patch | 4 +-- ..._background_throttling_in_compositor.patch | 14 ++++---- ...ingshelper_behind_branding_buildflag.patch | 2 +- ...board_hides_on_input_blur_in_webview.patch | 4 +-- ...x_remove_caption-removing_style_call.patch | 4 +-- ...from_localframe_requestexecutescript.patch | 14 ++++---- ...ated_generic_capturer_when_available.patch | 6 ++-- patches/chromium/frame_host_manager.patch | 8 ++--- .../chromium/gritsettings_resource_ids.patch | 4 +-- .../load_v8_snapshot_in_browser_process.patch | 4 +-- ..._avoid_private_macos_api_usage.patch.patch | 34 +++++++++---------- .../chromium/notification_provenance.patch | 6 ++-- ...xture_remove_keyed_mutex_on_win_dxgi.patch | 8 ++--- ...eated_to_allow_for_browser_initiated.patch | 2 +- patches/chromium/picture-in-picture.patch | 2 +- patches/chromium/printing.patch | 4 +-- ...r_changes_to_the_webcontentsobserver.patch | 6 ++-- ...efactor_unfilter_unresponsive_events.patch | 4 +-- .../render_widget_host_view_base.patch | 6 ++-- ...revert_enable_crel_for_arm32_targets.patch | 4 +-- patches/chromium/scroll_bounce_flag.patch | 4 +-- .../support_mixed_sandbox_with_zygote.patch | 4 +-- patches/chromium/web_contents.patch | 6 ++-- patches/chromium/webview_fullscreen.patch | 10 +++--- .../worker_context_will_destroy.patch | 12 +++---- ...feat_add_hook_to_notify_script_ready.patch | 12 +++---- 49 files changed, 180 insertions(+), 173 deletions(-) diff --git a/DEPS b/DEPS index bd8ba726c01c1..79b39a83017ac 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7095.0', + '136.0.7103.17', 'node_version': 'v22.14.0', 'nan_version': diff --git a/filenames.libcxx.gni b/filenames.libcxx.gni index 914055bfb6820..4f1f700d04ce4 100644 --- a/filenames.libcxx.gni +++ b/filenames.libcxx.gni @@ -1390,6 +1390,8 @@ libcxx_headers = [ "//third_party/libc++/src/include/__flat_map/sorted_equivalent.h", "//third_party/libc++/src/include/__flat_map/sorted_unique.h", "//third_party/libc++/src/include/__flat_map/utils.h", + "//third_party/libc++/src/include/__flat_set/flat_set.h", + "//third_party/libc++/src/include/__flat_set/ra_iterator.h", "//third_party/libc++/src/include/__format/buffer.h", "//third_party/libc++/src/include/__format/concepts.h", "//third_party/libc++/src/include/__format/container_adaptor.h", @@ -1460,12 +1462,14 @@ libcxx_headers = [ "//third_party/libc++/src/include/__fwd/get.h", "//third_party/libc++/src/include/__fwd/ios.h", "//third_party/libc++/src/include/__fwd/istream.h", + "//third_party/libc++/src/include/__fwd/map.h", "//third_party/libc++/src/include/__fwd/mdspan.h", "//third_party/libc++/src/include/__fwd/memory.h", "//third_party/libc++/src/include/__fwd/memory_resource.h", "//third_party/libc++/src/include/__fwd/ostream.h", "//third_party/libc++/src/include/__fwd/pair.h", "//third_party/libc++/src/include/__fwd/queue.h", + "//third_party/libc++/src/include/__fwd/set.h", "//third_party/libc++/src/include/__fwd/span.h", "//third_party/libc++/src/include/__fwd/sstream.h", "//third_party/libc++/src/include/__fwd/stack.h", @@ -1888,6 +1892,8 @@ libcxx_headers = [ "//third_party/libc++/src/include/__type_traits/negation.h", "//third_party/libc++/src/include/__type_traits/promote.h", "//third_party/libc++/src/include/__type_traits/rank.h", + "//third_party/libc++/src/include/__type_traits/reference_constructs_from_temporary.h", + "//third_party/libc++/src/include/__type_traits/reference_converts_from_temporary.h", "//third_party/libc++/src/include/__type_traits/remove_all_extents.h", "//third_party/libc++/src/include/__type_traits/remove_const.h", "//third_party/libc++/src/include/__type_traits/remove_const_ref.h", @@ -2012,6 +2018,7 @@ libcxx_headers = [ "//third_party/libc++/src/include/fenv.h", "//third_party/libc++/src/include/filesystem", "//third_party/libc++/src/include/flat_map", + "//third_party/libc++/src/include/flat_set", "//third_party/libc++/src/include/float.h", "//third_party/libc++/src/include/format", "//third_party/libc++/src/include/forward_list", diff --git a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch index 510696788770e..827f1787f99c7 100644 --- a/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch +++ b/patches/chromium/allow_disabling_blink_scheduler_throttling_per_renderview.patch @@ -116,10 +116,10 @@ index b1689844282d6917b9750fbc6a875848ddf84b70..f1cc159b7c3448a33a6d9e213f8fbd3b // Visibility ----------------------------------------------------------- diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index c1af09d0a80ffba9b1265e59cf3d066ad2a5bfa0..fbfc0af8a6f29da9ee2d5fd6ae5a02958a7e3a81 100644 +index 1af1fa035b3da8967526d704e362ddbc5c3194ac..f470ada1bf84938427fb89f4508e5f56aaeebc1f 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -2465,6 +2465,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( +@@ -2466,6 +2466,10 @@ void WebViewImpl::SetPageLifecycleStateInternal( TRACE_EVENT2("navigation", "WebViewImpl::SetPageLifecycleStateInternal", "old_state", old_state, "new_state", new_state); @@ -130,7 +130,7 @@ index c1af09d0a80ffba9b1265e59cf3d066ad2a5bfa0..fbfc0af8a6f29da9ee2d5fd6ae5a0295 bool storing_in_bfcache = new_state->is_in_back_forward_cache && !old_state->is_in_back_forward_cache; bool restoring_from_bfcache = !new_state->is_in_back_forward_cache && -@@ -3988,10 +3992,23 @@ PageScheduler* WebViewImpl::Scheduler() const { +@@ -3989,10 +3993,23 @@ PageScheduler* WebViewImpl::Scheduler() const { return GetPage()->GetPageScheduler(); } diff --git a/patches/chromium/blink_local_frame.patch b/patches/chromium/blink_local_frame.patch index da9ec05ec8e12..7ad0670a049b0 100644 --- a/patches/chromium/blink_local_frame.patch +++ b/patches/chromium/blink_local_frame.patch @@ -15,7 +15,7 @@ Refs changes in: This patch reverts the changes to fix associated crashes in Electron. diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc -index 9dc450bc20744463c8898bc822a558be38486493..576421cb9600625ad8b9eda25cb99954092a7a37 100644 +index 2072f6b14289b1f3a76dbccc98f29aa178c1c35c..d7017437a7e7e6ac130677e52731d0482bf20664 100644 --- a/third_party/blink/renderer/core/frame/frame.cc +++ b/third_party/blink/renderer/core/frame/frame.cc @@ -134,14 +134,6 @@ bool Frame::Detach(FrameDetachType type) { @@ -49,10 +49,10 @@ index 9dc450bc20744463c8898bc822a558be38486493..576421cb9600625ad8b9eda25cb99954 // its owning reference back to our owning LocalFrame. client_->Detached(type); diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 1a032c3a8eec55e98da745e7f82b311b1e0bd8ba..19efdf1f30eb5409f9d8a64f008891a2f4bda47c 100644 +index 84ce7a810816d8229f15b23a2f8bf36c00989378..f721051f30f517c863a935765d5dcd4760ab461e 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -748,10 +748,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -750,10 +750,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { } DCHECK(!view_ || !view_->IsAttached()); @@ -63,7 +63,7 @@ index 1a032c3a8eec55e98da745e7f82b311b1e0bd8ba..19efdf1f30eb5409f9d8a64f008891a2 if (!Client()) return false; -@@ -805,6 +801,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { +@@ -807,6 +803,11 @@ bool LocalFrame::DetachImpl(FrameDetachType type) { DCHECK(!view_->IsAttached()); Client()->WillBeDetached(); diff --git a/patches/chromium/build_add_electron_tracing_category.patch b/patches/chromium/build_add_electron_tracing_category.patch index 9b087aa5845bb..fe61ffc3df307 100644 --- a/patches/chromium/build_add_electron_tracing_category.patch +++ b/patches/chromium/build_add_electron_tracing_category.patch @@ -8,7 +8,7 @@ categories in use are known / declared. This patch is required for us to introduce a new Electron category for Electron-specific tracing. diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h -index 45c518185da68419e0f482acba359f02c2152f88..a0e35f118f0b1f767b41676d651a575e8b5fddb4 100644 +index 19e3f2fc7b3e08f690eb8c3b56c5b5222f645691..b0b7af16b6719a08ab7908ab4d96a5d2c3ea0340 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h @@ -91,6 +91,7 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index d424e470323e0..5373ec0788055 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -33,10 +33,10 @@ index 95c73dcb082999d0a85d82f1fe330d27c88055ca..64cd976eba849435779b280b2941f915 "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index fd02559a88b339ebd70e08d11d294975f1ef3148..c304e3d04364b071465a513a7953b3d1f0d3a38e 100644 +index 2af2794b353d89f01d84b6ba28201f025ae124c7..b656ba6241b6a61d772219d1e80fb6b601aba224 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4591,7 +4591,7 @@ static_library("browser") { +@@ -4593,7 +4593,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index fd02559a88b339ebd70e08d11d294975f1ef3148..c304e3d04364b071465a513a7953b3d1 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index 48488b2b31ff39e3fe822aa388e1c89578eb2575..9ba26245243d37683f3161c89411cacdcfaeceb7 100644 +index 5de0d083bbbeddd5f394ac0154789566789099fb..692e44b75ac164a6f88398a4177913c47f49959e 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7086,9 +7086,12 @@ test("unit_tests") { +@@ -7092,9 +7092,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index 48488b2b31ff39e3fe822aa388e1c89578eb2575..9ba26245243d37683f3161c89411cacd "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -8054,6 +8057,10 @@ test("unit_tests") { +@@ -8063,6 +8066,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index 48488b2b31ff39e3fe822aa388e1c89578eb2575..9ba26245243d37683f3161c89411cacd sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8109,7 +8116,6 @@ test("unit_tests") { +@@ -8118,7 +8125,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/build_libc_as_static_library.patch b/patches/chromium/build_libc_as_static_library.patch index f22d990128a0e..2b4ec2e0833e8 100644 --- a/patches/chromium/build_libc_as_static_library.patch +++ b/patches/chromium/build_libc_as_static_library.patch @@ -7,10 +7,10 @@ Build libc++ as static library to compile and pass nan tests diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn -index fd41146966f8cd559e99eb58c0831acdce9fbc6a..acc60f67732179ac6ed7161478686b6e3778731c 100644 +index 29e8bbcb5e7ee2fb310ce00b0c20913424adc9c8..649330dde1e2b717e0af5a15834bf96ae4e19245 100644 --- a/buildtools/third_party/libc++/BUILD.gn +++ b/buildtools/third_party/libc++/BUILD.gn -@@ -265,7 +265,11 @@ libcxx_modules("std_wctype_h") { +@@ -269,7 +269,11 @@ libcxx_modules("std_wctype_h") { if (libcxx_is_shared) { _libcxx_target_type = "shared_library" } else { @@ -23,7 +23,7 @@ index fd41146966f8cd559e99eb58c0831acdce9fbc6a..acc60f67732179ac6ed7161478686b6e } target(_libcxx_target_type, "libc++") { -@@ -274,6 +278,7 @@ target(_libcxx_target_type, "libc++") { +@@ -278,6 +282,7 @@ target(_libcxx_target_type, "libc++") { # need to explicitly depend on libc++. visibility = [ "//build/config:common_deps", diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index 9efd3bd665c18..eac254856e771 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 9e95917b0808ddcb6e0b1359fb5a86a516e4a071..ea6ba910234659d1213b1f3f624da0f49b851725 100644 +index 7d8baae182adb7664912c930463aa8b461ccbdc8..71fbe83a33fa247f82d7315767ff9a4aee9d3fca 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9638,6 +9638,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9644,6 +9644,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,10 +21,10 @@ index 9e95917b0808ddcb6e0b1359fb5a86a516e4a071..ea6ba910234659d1213b1f3f624da0f4 &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9c0ef713978f40d8ddda6772004fc81f37eafb6f..d7bf74c206cedc628d0b92fff25c29393f6c9d95 100644 +index f80d8ecc385df65610608ee78d6b09f2824a53fb..17009e944d6f3d23c721be521bf7843dbce1320e 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5087,6 +5087,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5095,6 +5095,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( SetPartitionedPopinOpenerOnNewWindowIfNeeded(new_contents_impl, params, opener); @@ -37,7 +37,7 @@ index 9c0ef713978f40d8ddda6772004fc81f37eafb6f..d7bf74c206cedc628d0b92fff25c2939 // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses -@@ -5128,12 +5134,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5136,12 +5142,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( AddWebContentsDestructionObserver(new_contents_impl); } @@ -66,7 +66,7 @@ index 55bb4ae3bab4cdf20b3e1dde9450a5c0e4e62b37..fe444c7fa140166a1b65c7a8a2676e2d // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index c5b548159e64ef3b8d27e1d7e8147289064f6e8a..1c4dd9d0c16751f34d59088e68e23317b8d25437 100644 +index e2382e9769cb79d676715187a2b8b00392e5445c..22400e44369b8ffb4b3fd886c981afea7a90da02 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc @@ -820,6 +820,8 @@ bool ContentBrowserClient::CanCreateWindow( @@ -79,10 +79,10 @@ index c5b548159e64ef3b8d27e1d7e8147289064f6e8a..1c4dd9d0c16751f34d59088e68e23317 bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 921a6a8f7ecbdd2e3fa8a9888e4a34f1b0cf7e09..7cfc3331a004fd52d9863a097271f4d892480933 100644 +index 9b75a6c3c222a93319e0f26f351aed3dbe6ead28..8a7ea32eb4ea4a78662b96a2eef5a0de4229d214 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -198,6 +198,7 @@ class NetworkService; +@@ -199,6 +199,7 @@ class NetworkService; class TrustedURLLoaderHeaderClient; } // namespace mojom struct ResourceRequest; @@ -90,7 +90,7 @@ index 921a6a8f7ecbdd2e3fa8a9888e4a34f1b0cf7e09..7cfc3331a004fd52d9863a097271f4d8 } // namespace network namespace sandbox { -@@ -1372,6 +1373,8 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -1376,6 +1377,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, diff --git a/patches/chromium/chore_add_electron_deps_to_gitignores.patch b/patches/chromium/chore_add_electron_deps_to_gitignores.patch index b45633da1f135..da245d70f50f9 100644 --- a/patches/chromium/chore_add_electron_deps_to_gitignores.patch +++ b/patches/chromium/chore_add_electron_deps_to_gitignores.patch @@ -6,7 +6,7 @@ Subject: chore: add electron deps to gitignores Makes things like "git status" quicker when developing electron locally diff --git a/.gitignore b/.gitignore -index 375db3d02ce2837ba69e3e93c38cde7153ba079f..fc3f4f71c43facf695d326f0bc64e0ad920aa9ac 100644 +index 0a0f0118d5c1a5a2f3ad28b068bebb849eba7246..5ca6d03b709ef119ccd6482b2f305f8a3aeb7438 100644 --- a/.gitignore +++ b/.gitignore @@ -217,6 +217,7 @@ vs-chromium-project.txt diff --git a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch index 7c3a128ce7dc5..537678be5c611 100644 --- a/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch +++ b/patches/chromium/chore_modify_chromium_handling_of_mouse_events.patch @@ -34,10 +34,10 @@ index 39b5a8fdd165efd74b00256552b51b5413107958..bfc4ef4f50efff4a77f2aef64335bb7e class ScrollEvent; 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 19826b92b39c812a3170bfa470a08192b2a712bc..d97262cc28622de335bfe5a82c26779e9dccd586 100644 +index 1d298f66a2c2a0f02668af797b3a421fe5578d56..4780229a4453ad5ca2c89b786459f64c58bf2272 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 -@@ -1387,6 +1387,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( +@@ -1386,6 +1386,10 @@ void DesktopWindowTreeHostWin::HandleHeadlessWindowBoundsChanged( window()->SetProperty(aura::client::kHeadlessBoundsKey, bounds); } @@ -61,10 +61,10 @@ index 2ee5e4b4673f4f18880dddecc48118c89823fd3f..37109b8d3d439073b5c9e2ea3597c36f // Overridden from WidgetObserver. void OnWidgetThemeChanged(Widget* widget) override; diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 1cf98eadc8cbc1ada481c709a873dc1dd443de66..cec234006cbcacff953ce9ff4175006b057aa341 100644 +index 701f89b053674be1bad0a1a3612340a65c1ccfe7..ac7e189f26da75f6f8b40e69cf4bfee94acddd38 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3177,15 +3177,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3186,15 +3186,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, } // We must let Windows handle the caption buttons if it's drawing them, or // they won't work. @@ -86,7 +86,7 @@ index 1cf98eadc8cbc1ada481c709a873dc1dd443de66..cec234006cbcacff953ce9ff4175006b return 0; } } -@@ -3208,6 +3212,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3217,6 +3221,7 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, // handle alt-space, or in the frame itself. is_right_mouse_pressed_on_caption_ = false; ReleaseCapture(); @@ -94,7 +94,7 @@ index 1cf98eadc8cbc1ada481c709a873dc1dd443de66..cec234006cbcacff953ce9ff4175006b // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() // expect screen coordinates. POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); -@@ -3215,7 +3220,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, +@@ -3224,7 +3229,17 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, w_param = static_cast(SendMessage( hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); if (w_param == HTCAPTION || w_param == HTSYSMENU) { diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index af244fb88baf1..14d92201cd07f 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,10 +14,10 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 51355738262d80afaf1f319b5d90c8a74d435ffd..2a17aa6a3f687d60a7ca0e839e59f637819a9376 100644 +index 3b43dcfef507b98f8af6f0c14c4fa9af0a7b9593..9c301542a54c3aeb5ec7ce408e312b48b33d22e3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5006,7 +5006,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5014,7 +5014,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( : IsGuest(); // While some guest types do not have a guest SiteInstance, the ones that // don't all override WebContents creation above. diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index 6dbe4f0b55e6a..7a9f3b4f86e1b 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -80,10 +80,10 @@ index 4fd8dff1089cd6afa6a66dc185734d7671657281..0a1f4268ea771a3d5d4a2668928c6e5d content::WebContents* source, const content::OpenURLParams& params, diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc -index 9ccf763fcbc717480c4dfce735f273dfdbf5cd4a..381cda59465f34a46f55d03ef6ccd53fadec6e6e 100644 +index d45f8f1713c875f2d430f9155f0cbe626a5f233f..00c7db5afa636d5d1da4e5d2cb01ebcbec70304b 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc -@@ -2267,12 +2267,11 @@ bool Browser::IsWebContentsCreationOverridden( +@@ -2289,12 +2289,11 @@ bool Browser::IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -99,10 +99,10 @@ index 9ccf763fcbc717480c4dfce735f273dfdbf5cd4a..381cda59465f34a46f55d03ef6ccd53f WebContents* Browser::CreateCustomWebContents( diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h -index 288d4f6d7dfdcf7e518f78990f061f4215f1b975..3d8899e313906a0d3a273d43cab872243fa6faea 100644 +index ce1e404e45a60e43de9580fe8d60fa72ec90b8f4..9267e93fdd03fad3848a45a1c6d090566cac317c 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h -@@ -983,8 +983,7 @@ class Browser : public TabStripModelObserver, +@@ -1005,8 +1005,7 @@ class Browser : public TabStripModelObserver, content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, @@ -218,10 +218,10 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 30593ec9ecd2b135a6054ed50e95c849c09b47bd..b6870d74a761a847e9952d409b37ddb778790b08 100644 +index 09c1b87b58fb2bf99c2a3b81b300e0cc1b9a0115..a1ba9ea494a6b2da2b3fdfeb02491d37b3da9074 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4969,8 +4969,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4977,8 +4977,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( // TODO(crbug.com/40202416): Support a way for MPArch guests to support this. if (delegate_ && delegate_->IsWebContentsCreationOverridden( source_site_instance, params.window_container_type, diff --git a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch index 8715b6c8872b9..ce4957d9e66f9 100644 --- a/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch +++ b/patches/chromium/chore_remove_conflicting_allow_unsafe_libc_calls.patch @@ -32,7 +32,7 @@ index 7d5d0106a3675b3fa21b0e00a755f5c0ed11c87b..d26c645d70b54b31815c8140954ee6d0 // (netwerk/cookie/src/nsCookieService.cpp) /* ***** BEGIN LICENSE BLOCK ***** diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc -index 2235a264ff87ef400734a7308c2735886c63c92c..2310a951c5b4d34447a39c7585bdf1027a9953cc 100644 +index 11572f927d97da20627dec498b92e445b5db4589..9b02d35a19d8610ade71b96c11551a1050847b49 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -2,11 +2,6 @@ diff --git a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch index 3b9e069bb1261..6bd2adcffa567 100644 --- a/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch +++ b/patches/chromium/create_browser_v8_snapshot_file_name_fuse.patch @@ -7,10 +7,10 @@ By default, chromium sets up one v8 snapshot to be used in all v8 contexts. This to have a dedicated browser process v8 snapshot defined by the file `browser_v8_context_snapshot.bin`. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 2474e8c608956bcf50e0c7204d728854fe498d31..32f3a69ea003d7c6a1f6a15622ff2acb9b5a063b 100644 +index 7550d3eb796059aeb108f25071c8028bddebb100..db59a147bd6b4fb596170d9b86c5e71a20b62abc 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -272,8 +272,13 @@ void AsanProcessInfoCB(const char*, bool*) { +@@ -273,8 +273,13 @@ void AsanProcessInfoCB(const char*, bool*) { } #endif // defined(ADDRESS_SANITIZER) @@ -25,7 +25,7 @@ index 2474e8c608956bcf50e0c7204d728854fe498d31..32f3a69ea003d7c6a1f6a15622ff2acb #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) base::FileDescriptorStore& file_descriptor_store = base::FileDescriptorStore::GetInstance(); -@@ -302,11 +307,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, +@@ -303,11 +308,12 @@ bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, #endif // V8_USE_EXTERNAL_STARTUP_DATA @@ -40,7 +40,7 @@ index 2474e8c608956bcf50e0c7204d728854fe498d31..32f3a69ea003d7c6a1f6a15622ff2acb #endif // V8_USE_EXTERNAL_STARTUP_DATA } -@@ -979,7 +985,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { +@@ -980,7 +986,7 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { return TerminateForFatalInitializationError(); #endif // BUILDFLAG(IS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) diff --git a/patches/chromium/enable_reset_aspect_ratio.patch b/patches/chromium/enable_reset_aspect_ratio.patch index 409ced0aa6c13..cb93c6b2261b3 100644 --- a/patches/chromium/enable_reset_aspect_ratio.patch +++ b/patches/chromium/enable_reset_aspect_ratio.patch @@ -6,10 +6,10 @@ Subject: feat: enable setting aspect ratio to 0 Make SetAspectRatio accept 0 as valid input, which would reset to null. 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 65e4957aa08923200923f91cadcf47eb403bb8a8..19826b92b39c812a3170bfa470a08192b2a712bc 100644 +index 82e6f7c91bac6e8a973947dace1a33de8078a86b..1d298f66a2c2a0f02668af797b3a421fe5578d56 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 -@@ -637,7 +637,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { +@@ -636,7 +636,7 @@ void DesktopWindowTreeHostWin::SetOpacity(float opacity) { void DesktopWindowTreeHostWin::SetAspectRatio( const gfx::SizeF& aspect_ratio, const gfx::Size& excluded_margin) { @@ -19,10 +19,10 @@ index 65e4957aa08923200923f91cadcf47eb403bb8a8..19826b92b39c812a3170bfa470a08192 excluded_margin); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index a0e6a63f7b2da396ba5d300a9eb6ab94825f677a..539f5920661010139dc69afb3ff7dd4c9bae762a 100644 +index a183cfe07b357e0b17e3781e85b79f7c371cbea8..ea67a60d78f4adec960bb4954486420bce423c14 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1008,8 +1008,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, +@@ -1010,8 +1010,11 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, void HWNDMessageHandler::SetAspectRatio(float aspect_ratio, const gfx::Size& excluded_margin) { diff --git a/patches/chromium/extend_apply_webpreferences.patch b/patches/chromium/extend_apply_webpreferences.patch index 278fb134b48f2..42e76b532e058 100644 --- a/patches/chromium/extend_apply_webpreferences.patch +++ b/patches/chromium/extend_apply_webpreferences.patch @@ -12,10 +12,10 @@ Ideally we could add an embedder observer pattern here but that can be done in future work. diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index fbfc0af8a6f29da9ee2d5fd6ae5a02958a7e3a81..cf80d64963a2a25afbaa6060b4dfda425638858b 100644 +index f470ada1bf84938427fb89f4508e5f56aaeebc1f..524928729e34cc6b2ae427181e2ac4f8c1128688 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -170,6 +170,7 @@ +@@ -171,6 +171,7 @@ #include "third_party/blink/renderer/core/view_transition/view_transition_supplement.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" #include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h" @@ -23,7 +23,7 @@ index fbfc0af8a6f29da9ee2d5fd6ae5a02958a7e3a81..cf80d64963a2a25afbaa6060b4dfda42 #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" -@@ -1860,6 +1861,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, +@@ -1861,6 +1862,7 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, #if BUILDFLAG(IS_MAC) web_view_impl->SetMaximumLegibleScale( prefs.default_maximum_page_scale_factor); diff --git a/patches/chromium/feat_configure_launch_options_for_service_process.patch b/patches/chromium/feat_configure_launch_options_for_service_process.patch index bafd2c57636be..6e276d6c05e77 100644 --- a/patches/chromium/feat_configure_launch_options_for_service_process.patch +++ b/patches/chromium/feat_configure_launch_options_for_service_process.patch @@ -19,7 +19,7 @@ to STDOUT_FILENO/STD_OUTPUT_HANDLE and STDERR_FILENO/STD_ERROR_HANDLE allowing t parent process to read from the pipe. diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h -index 32b0b480d2195e5b166a473c2b58c960a0b5d052..cbf593e5405b384dbbdf763ebc4319cf25fc5c4a 100644 +index ac6f72c99800d5437ddc4aa203870242fb9220b9..93055bda5478f4b7b401ae06dcddce36a26f5ad7 100644 --- a/content/browser/child_process_launcher.h +++ b/content/browser/child_process_launcher.h @@ -33,6 +33,7 @@ @@ -30,7 +30,7 @@ index 32b0b480d2195e5b166a473c2b58c960a0b5d052..cbf593e5405b384dbbdf763ebc4319cf #endif #if BUILDFLAG(IS_POSIX) -@@ -196,7 +197,10 @@ struct ChildProcessLauncherFileData { +@@ -188,7 +189,10 @@ struct ChildProcessLauncherFileData { delete; ~ChildProcessLauncherFileData(); @@ -42,7 +42,7 @@ index 32b0b480d2195e5b166a473c2b58c960a0b5d052..cbf593e5405b384dbbdf763ebc4319cf // Files opened by the browser and passed as corresponding file descriptors // in the child process. If a FilePath is provided, the file will be opened // and the descriptor cached for future process launches. If a ScopedFD is -@@ -211,6 +215,15 @@ struct ChildProcessLauncherFileData { +@@ -203,6 +207,15 @@ struct ChildProcessLauncherFileData { std::map> files_to_preload; #endif diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch index 4f97d4fa29c1b..f3bd3e35e44b9 100644 --- a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -154,10 +154,10 @@ index 10ab8a458fb3348476a6e904f684af77c55b103a..37339df2a5601b59abc50fe75fb844e1 return a.EmptyCells() == b.EmptyCells(); case CSSPropertyID::kFill: diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc -index 663ce4469a595e2c5a7b8ed992bdc0e22ab101a9..acdbd7ab75b12630356777a475be794091be25ad 100644 +index 3fb84db022f14819239cd55da79f43cb9b0bcbc7..c4b7ff8ea240054d9394a0691e978e9b10eecc50 100644 --- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc -@@ -12035,5 +12035,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( +@@ -12047,5 +12047,25 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue( CSSValueID::kNone>(stream); } @@ -239,10 +239,10 @@ index 5e8d2bfbccd0625c2598544a9cba3d71373eded2..e68a97ee75754fc7196f11cf5c731550 bool RenderVSyncNotificationEnabled() const { return render_v_sync_notification_enabled_; diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc -index cf80d64963a2a25afbaa6060b4dfda425638858b..5f3a6dba6fe4f93698a8e18afa07efdac73eabee 100644 +index 524928729e34cc6b2ae427181e2ac4f8c1128688..9ce184f279b11a90c7d6f9b9ab7b20da09122a40 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc -@@ -3575,6 +3575,9 @@ void WebViewImpl::UpdateRendererPreferences( +@@ -3576,6 +3576,9 @@ void WebViewImpl::UpdateRendererPreferences( CanvasNoiseToken::Set(renderer_preferences_.canvas_noise_token); MaybePreloadSystemFonts(GetPage()); diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch index 199d092ebd06e..4dd2eec78d4cd 100644 --- a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -90,7 +90,7 @@ index 8af69cac78b7488d28f1f05ccb174793fe5148cd..9f74e511c263d147b5fbe81fe100d217 private: const HWND hwnd_; diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index 41c3a5128b9936f48587d6fc6ddd2050bffc9c36..4dbcba1150645596532c53b99e309c0e52d4759f 100644 +index 067b3cc0d6a8b5990dc4bad0f15e47bd4922a26a..4aaf506c9fadb3f4c5a79560bf4558462d2c8705 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn @@ -178,6 +178,8 @@ viz_component("service") { @@ -509,10 +509,10 @@ index 0000000000000000000000000000000000000000..e1a22ee881c0fd679ac2d2d4d11a3c93 + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_ diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc -index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109ccda35248f 100644 +index f0aca972c4a81c3dfb536e14244daafae21ee716..a15afbc1a3519e657121b4952444d2f47d872e42 100644 --- a/components/viz/service/display_embedder/software_output_device_win.cc +++ b/components/viz/service/display_embedder/software_output_device_win.cc -@@ -149,7 +149,7 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated( +@@ -156,7 +156,7 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated( if (!canvas_) return; @@ -522,10 +522,10 @@ index 4d6cc977ed5000d93918336a0dd57f60c0e95bbb..54d936e86b60f0538c70c4ee69e109cc waiting_on_draw_ack_ = true; diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -index 6de3d8b4cdaf2721a160c6271561b8fd8872e82e..890b95b4aee9210f13ee192a6ea86e072619fcf4 100644 +index 2b6ceac536ca667caad8f1aa1d8ab6c5139083d8..869468a0ab7708fc300a098c47d5d490f17190a8 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc -@@ -113,7 +113,8 @@ RootCompositorFrameSinkImpl::Create( +@@ -115,7 +115,8 @@ RootCompositorFrameSinkImpl::Create( params->gpu_compositing, params->widget); auto output_surface = output_surface_provider->CreateOutputSurface( params->widget, params->gpu_compositing, display_client.get(), @@ -620,7 +620,7 @@ index 2f462f0deb5fc8a637457243fb5d5849fc214d14..695869b83cefaa24af93a2e11b39de05 + Draw(gfx.mojom.Rect damage_rect) => (); }; diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index bd5213795191e5947b6c70778ae01a669ab50dd7..3f52b93eab20108e40131472d8abdf9ff60bd191 100644 +index c802f9aae242c1be364890e0e6968d09be87f946..38b6c47f526b428562f340966352d32ba918f2d5 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h @@ -88,6 +88,7 @@ class DisplayPrivate; @@ -631,7 +631,7 @@ index bd5213795191e5947b6c70778ae01a669ab50dd7..3f52b93eab20108e40131472d8abdf9f class HostFrameSinkManager; class LocalSurfaceId; class RasterContextProvider; -@@ -147,6 +148,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { +@@ -146,6 +147,15 @@ class COMPOSITOR_EXPORT ExternalBeginFrameControllerClientFactory { viz::mojom::ExternalBeginFrameControllerClient> CreateExternalBeginFrameControllerClient() = 0; }; @@ -647,7 +647,7 @@ index bd5213795191e5947b6c70778ae01a669ab50dd7..3f52b93eab20108e40131472d8abdf9f // Compositor object to take care of GPU painting. // A Browser compositor object is responsible for generating the final -@@ -191,6 +201,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -190,6 +200,9 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // Schedules a redraw of the layer tree associated with this compositor. void ScheduleDraw(); @@ -657,7 +657,7 @@ index bd5213795191e5947b6c70778ae01a669ab50dd7..3f52b93eab20108e40131472d8abdf9f // Sets the root of the layer tree drawn by this Compositor. The root layer // must have no parent. The compositor's root layer is reset if the root layer // is destroyed. NULL can be passed to reset the root layer, in which case the -@@ -609,6 +622,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -629,6 +642,8 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, simple_begin_frame_observers_; std::unique_ptr host_begin_frame_observer_; diff --git a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch index de5d2e3eb8ee7..8a9d441e780e3 100644 --- a/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch +++ b/patches/chromium/feat_expose_raw_response_headers_from_urlloader.patch @@ -112,7 +112,7 @@ index 72188dae668f2bd91b8b7f181448f459600a82c2..b3dae05e160160cc8f2decee5af78c21 string mime_type; diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc -index 54501620f428c1e539a8172054180c9c58145766..bbde6ebfb8f051622b1f499d472b00fac2093be7 100644 +index 01ddddb7f8f3723814cca8c38ed7150ee1270d57..9a79c530f3b9bfec8ce7307cf202efba8f14c905 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc @@ -673,6 +673,9 @@ URLLoader::URLLoader( @@ -134,7 +134,7 @@ index 54501620f428c1e539a8172054180c9c58145766..bbde6ebfb8f051622b1f499d472b00fa url_request_->SetResponseHeadersCallback(base::BindRepeating( &URLLoader::SetRawResponseHeaders, base::Unretained(this))); } -@@ -2142,6 +2145,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { +@@ -2146,6 +2149,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { } response_ = BuildResponseHead(); diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index 96ed54b14c4c6..4f4fc4263db01 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,10 +14,10 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 29829e282edfa8821bd366a9e9a3755d7f3f8643..9feff09aa83eb88460dce786ab2514f0a9b21c6e 100644 +index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..96cf055f1144887c0b4b4aef83898508016b310f 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -952,13 +952,13 @@ void HWNDMessageHandler::FrameTypeChanged() { +@@ -954,13 +954,13 @@ void HWNDMessageHandler::FrameTypeChanged() { void HWNDMessageHandler::PaintAsActiveChanged() { if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || @@ -33,7 +33,7 @@ index 29829e282edfa8821bd366a9e9a3755d7f3f8643..9feff09aa83eb88460dce786ab2514f0 } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -2356,17 +2356,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, +@@ -2365,17 +2365,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, delegate_->SchedulePaint(); } diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch index ef21bc45bbe47..e36a893d7b0f6 100644 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch @@ -12,10 +12,10 @@ ui problems (like dissapearing popup during typing in html's input list. diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index 00113c5a91e4285a102afd37c6c08736d056faf6..f6a719ba09ed9aedc1bdc8322e9bee8d2014fc39 100644 +index 4d1c7d95c338b247f7c63315d536fcc7a7e4b637..55bb9b36f8d0894c972c05ba162fd5b41048013d 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h -@@ -1211,6 +1211,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, +@@ -1223,6 +1223,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // True if widget was created in headless mode. bool is_headless() const { return is_headless_; } diff --git a/patches/chromium/fix_aspect_ratio_with_max_size.patch b/patches/chromium/fix_aspect_ratio_with_max_size.patch index 0ad82581aa514..8068f9cf6aab7 100644 --- a/patches/chromium/fix_aspect_ratio_with_max_size.patch +++ b/patches/chromium/fix_aspect_ratio_with_max_size.patch @@ -11,10 +11,10 @@ enlarge window above dimensions set during creation of the BrowserWindow. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 539f5920661010139dc69afb3ff7dd4c9bae762a..1cf98eadc8cbc1ada481c709a873dc1dd443de66 100644 +index ea67a60d78f4adec960bb4954486420bce423c14..701f89b053674be1bad0a1a3612340a65c1ccfe7 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -3781,15 +3781,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, +@@ -3790,15 +3790,30 @@ void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param, delegate_->GetMinMaxSize(&min_window_size, &max_window_size); min_window_size = delegate_->DIPToScreenSize(min_window_size); max_window_size = delegate_->DIPToScreenSize(max_window_size); diff --git a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch index 13162b7e04374..35edda432b118 100644 --- a/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch +++ b/patches/chromium/fix_crash_loading_non-standard_schemes_in_iframes.patch @@ -28,10 +28,10 @@ The patch should be removed in favor of either: Upstream bug https://bugs.chromium.org/p/chromium/issues/detail?id=1081397. diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc -index 005c18b0685e504a7c323cb2a622e893955fe738..c9e55ca1da0e3f37777cc989511942e622ad05a8 100644 +index 29740121f137118836c6ace05f302cc8aa671d90..7ae2270bc89c4a77f56055283f77d44c7474031b 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc -@@ -11083,6 +11083,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { +@@ -11099,6 +11099,12 @@ NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() { "blob"); } diff --git a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch index 475f205dbbc6c..1ead859337555 100644 --- a/patches/chromium/fix_disabling_background_throttling_in_compositor.patch +++ b/patches/chromium/fix_disabling_background_throttling_in_compositor.patch @@ -12,10 +12,10 @@ invisible state of the `viz::DisplayScheduler` owned by the `ui::Compositor`. diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc -index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb594514a3 100644 +index 633e5c77e410404053f46ce92a37f30cf743cac0..2b3688df2b5b89c931a84a0e91048ae80a814ac0 100644 --- a/ui/compositor/compositor.cc +++ b/ui/compositor/compositor.cc -@@ -361,7 +361,8 @@ void Compositor::SetLayerTreeFrameSink( +@@ -357,7 +357,8 @@ void Compositor::SetLayerTreeFrameSink( if (display_private_) { disabled_swap_until_resize_ = false; display_private_->Resize(size()); @@ -25,7 +25,7 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb display_private_->SetDisplayColorSpaces(display_color_spaces_); display_private_->SetDisplayColorMatrix( gfx::SkM44ToTransform(display_color_matrix_)); -@@ -581,7 +582,9 @@ void Compositor::SetVisible(bool visible) { +@@ -582,7 +583,9 @@ void Compositor::SetVisible(bool visible) { // updated then. We need to call this even if the visibility hasn't changed, // for the same reason. if (display_private_) @@ -36,7 +36,7 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb if (changed) { observer_list_.Notify(&CompositorObserver::OnCompositorVisibilityChanged, -@@ -1034,6 +1037,15 @@ void Compositor::MaybeUpdateObserveBeginFrame() { +@@ -1046,6 +1049,15 @@ void Compositor::MaybeUpdateObserveBeginFrame() { host_begin_frame_observer_->GetBoundRemote()); } @@ -53,10 +53,10 @@ index 008df596834faccfa19525dcae0239ffed7b2c3f..aecfa059c6ac53a3377e1f60b3c66bbb void Compositor::SetSeamlessRefreshRates( const std::vector& seamless_refresh_rates) { diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h -index 3f52b93eab20108e40131472d8abdf9ff60bd191..ee19a847cf6d446a927734b9fdd3f1547a9f2d14 100644 +index 38b6c47f526b428562f340966352d32ba918f2d5..4816d7377b39427c0bed9b4c7bab3b410699f5cf 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h -@@ -512,6 +512,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -515,6 +515,10 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, const cc::LayerTreeSettings& GetLayerTreeSettings() const; @@ -67,7 +67,7 @@ index 3f52b93eab20108e40131472d8abdf9ff60bd191..ee19a847cf6d446a927734b9fdd3f154 size_t saved_events_metrics_count_for_testing() const { return host_->saved_events_metrics_count_for_testing(); } -@@ -703,6 +707,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, +@@ -723,6 +727,12 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, // See go/report-ux-metrics-at-painting for details. bool animation_started_ = false; diff --git a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch index 36992bfed42b1..cec6714770139 100644 --- a/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch +++ b/patches/chromium/fix_move_autopipsettingshelper_behind_branding_buildflag.patch @@ -75,7 +75,7 @@ index 9f82c7f92f735ed9f9569a8d299d23c7cb83e596..9b7598a26781da8b1e1c736460657402 PictureInPictureOcclusionTracker* diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index e8524bb6706ae06a0feabccbe44250580098e937..87b28c59d5a3abc49c7f5ed084ad78dcae02b39a 100644 +index 531b183bcc017daeefcf158fd58570a889c4b7df..62fbbaa37d07d24e410778e155a7f013479722b3 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -430,11 +430,13 @@ std::unique_ptr VideoOverlayWindowViews::Create( diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index 262ec694fc975..725961ddd8861 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -87,10 +87,10 @@ index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac4 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 1e6ee8c65d56f2821485f855bbf4e8bb8212058c..51355738262d80afaf1f319b5d90c8a74d435ffd 100644 +index a793aa22e549e0fa8fee4665775761db97496eb2..3b43dcfef507b98f8af6f0c14c4fa9af0a7b9593 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9834,7 +9834,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9843,7 +9843,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/fix_remove_caption-removing_style_call.patch b/patches/chromium/fix_remove_caption-removing_style_call.patch index 3bc12bffae486..1982b96c4bdae 100644 --- a/patches/chromium/fix_remove_caption-removing_style_call.patch +++ b/patches/chromium/fix_remove_caption-removing_style_call.patch @@ -18,10 +18,10 @@ or resizing, but Electron does not seem to run into that issue for opaque frameless windows even with that block commented out. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index cec234006cbcacff953ce9ff4175006b057aa341..29829e282edfa8821bd366a9e9a3755d7f3f8643 100644 +index ac7e189f26da75f6f8b40e69cf4bfee94acddd38..ecab7d7f95998a055f01d7b5180a8fb80eddb32c 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -1816,7 +1816,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { +@@ -1825,7 +1825,23 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); diff --git a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch index 7820ed391296e..4617953a5c613 100644 --- a/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch +++ b/patches/chromium/fix_return_v8_value_from_localframe_requestexecutescript.patch @@ -59,10 +59,10 @@ index cba373664bec3a32abad6fe0396bd67b53b7e67f..a54f1b3351efd2d8f324436f7f35cd43 #endif // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_EXECUTION_CALLBACK_H_ diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 19efdf1f30eb5409f9d8a64f008891a2f4bda47c..0e1212d227ee5ffc536bd0349708a31db6ddfe28 100644 +index f721051f30f517c863a935765d5dcd4760ab461e..29270267b7884c51064a4618bbddc01c192ff39d 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -3099,6 +3099,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3101,6 +3101,7 @@ void LocalFrame::RequestExecuteScript( mojom::blink::EvaluationTiming evaluation_timing, mojom::blink::LoadEventBlockingOption blocking_option, WebScriptExecutionCallback callback, @@ -70,7 +70,7 @@ index 19efdf1f30eb5409f9d8a64f008891a2f4bda47c..0e1212d227ee5ffc536bd0349708a31d BackForwardCacheAware back_forward_cache_aware, mojom::blink::WantResultOption want_result_option, mojom::blink::PromiseResultOption promise_behavior) { -@@ -3131,7 +3132,7 @@ void LocalFrame::RequestExecuteScript( +@@ -3133,7 +3134,7 @@ void LocalFrame::RequestExecuteScript( PausableScriptExecutor::CreateAndRun( script_state, std::move(script_sources), execute_script_policy, user_gesture, evaluation_timing, blocking_option, want_result_option, @@ -80,10 +80,10 @@ index 19efdf1f30eb5409f9d8a64f008891a2f4bda47c..0e1212d227ee5ffc536bd0349708a31d void LocalFrame::SetEvictCachedSessionStorageOnFreezeOrUnload() { diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h -index 5cdeaa531babca965bed7e1e18ee993f1ba0d847..651e64c0ea39ec28db117aa3a61ea87d3f24117d 100644 +index 73d924f3675bc84bf0efad5a06abbf0af62ae336..070af1deb56dcd9399915ac436ecce96da19a36c 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h -@@ -815,6 +815,7 @@ class CORE_EXPORT LocalFrame final +@@ -823,6 +823,7 @@ class CORE_EXPORT LocalFrame final mojom::blink::EvaluationTiming, mojom::blink::LoadEventBlockingOption, WebScriptExecutionCallback, @@ -92,10 +92,10 @@ index 5cdeaa531babca965bed7e1e18ee993f1ba0d847..651e64c0ea39ec28db117aa3a61ea87d mojom::blink::WantResultOption, mojom::blink::PromiseResultOption); diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc -index ff24f4607190bc127a3da3aba0544cb6f67dde3f..9aa4f9d0356c078f5f42b06dc166686dc0c64b81 100644 +index 58b8f5b22c01d41cca3ec0088341d036917d5838..930d9fa36a616fe27d7b5b5a39436cbe375140bc 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc -@@ -964,6 +964,7 @@ void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld( +@@ -969,6 +969,7 @@ void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld( std::move(callback).Run(value ? std::move(*value) : base::Value()); }, std::move(callback)), diff --git a/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch index 28efacb95da22..cabe355bafb3c 100644 --- a/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch +++ b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch @@ -15,10 +15,10 @@ capturer was window or screen-specific, as the IDs remain valid for generic capturer as well. diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc -index 010deff30b050c1de9324f969588385958bcb14e..b4b1e9ee805a8565a04737e0898ad8e46709c4d8 100644 +index 93823d191f63f81f79d370f85991f51747c39b46..715a761e3bea87381ccf43b86dddba9bc03bbe9e 100644 --- a/content/browser/media/capture/desktop_capture_device.cc +++ b/content/browser/media/capture/desktop_capture_device.cc -@@ -902,8 +902,14 @@ std::unique_ptr DesktopCaptureDevice::Create( +@@ -908,8 +908,14 @@ std::unique_ptr DesktopCaptureDevice::Create( switch (source.type) { case DesktopMediaID::TYPE_SCREEN: { @@ -35,7 +35,7 @@ index 010deff30b050c1de9324f969588385958bcb14e..b4b1e9ee805a8565a04737e0898ad8e4 if (screen_capturer && screen_capturer->SelectSource(source.id)) { capturer = std::make_unique( std::move(screen_capturer), options); -@@ -916,8 +922,14 @@ std::unique_ptr DesktopCaptureDevice::Create( +@@ -922,8 +928,14 @@ std::unique_ptr DesktopCaptureDevice::Create( } case DesktopMediaID::TYPE_WINDOW: { diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index 71c8533d01373..2199c77ff9e7c 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -6,10 +6,10 @@ Subject: frame_host_manager.patch Allows embedder to intercept site instances created by chromium. diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc -index 9bf65e1e0c893e64cf6dc378fd9b8ae9363d954c..c7ea4d6a44a1c4cce129e9e269f2bcc04838272d 100644 +index 301c950ed4f58cc80a6c51584e54c51dd800f25d..758822cc1685c000e6ecc1894b991eb2ccd9afcc 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc -@@ -4752,6 +4752,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( +@@ -4785,6 +4785,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( request->ResetStateForSiteInstanceChange(); } @@ -20,10 +20,10 @@ index 9bf65e1e0c893e64cf6dc378fd9b8ae9363d954c..c7ea4d6a44a1c4cce129e9e269f2bcc0 } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 7cfc3331a004fd52d9863a097271f4d892480933..55b0cae39e7aac22315d75b821a8b1123e762e15 100644 +index 8a7ea32eb4ea4a78662b96a2eef5a0de4229d214..c7f14050af11356b5a985c6fb27570b96f4104c7 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -339,6 +339,11 @@ class CONTENT_EXPORT ContentBrowserClient { +@@ -340,6 +340,11 @@ class CONTENT_EXPORT ContentBrowserClient { virtual ~ContentBrowserClient() = default; diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index a0f30ca98fb90..d5b4842d3089e 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,10 +6,10 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec -index a0d9ecaa8da4b79fcb920c1d0ff10361aeb75b50..d93cdbd2451ace33a7d4af7771d1ef746fb674ac 100644 +index 2f5be98233528436a8a2ecf91aaca42ebb045921..43bee48208ac3084bcfc32c53caf76058554d7cf 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec -@@ -1500,6 +1500,11 @@ +@@ -1504,6 +1504,11 @@ "<(SHARED_INTERMEDIATE_DIR)/third_party/blink/public/strings/permission_element_generated_strings.grd": { "META": {"sizes": {"messages": [2000],}}, "messages": [10080], diff --git a/patches/chromium/load_v8_snapshot_in_browser_process.patch b/patches/chromium/load_v8_snapshot_in_browser_process.patch index ece0963c33e4d..a6820dea05d63 100644 --- a/patches/chromium/load_v8_snapshot_in_browser_process.patch +++ b/patches/chromium/load_v8_snapshot_in_browser_process.patch @@ -9,10 +9,10 @@ but due to the nature of electron, we need to load the v8 snapshot in the browser process. diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc -index 4e7e5c5f506144f8d523dc60f5a1ff94de9aad2e..2474e8c608956bcf50e0c7204d728854fe498d31 100644 +index e81e89be0e190b3400023b3105b7774c19428f72..7550d3eb796059aeb108f25071c8028bddebb100 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc -@@ -293,11 +293,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { +@@ -294,11 +294,8 @@ void LoadV8SnapshotFile(const base::CommandLine& command_line) { bool ShouldLoadV8Snapshot(const base::CommandLine& command_line, const std::string& process_type) { diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 2cebf3e864d80..5e149d33def9e 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,7 +35,7 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index 70444961a612c3340e8a689f2cf002918be75366..aa055d6e5f9b8a72587cd8c9fbc7203a15352553 100644 +index 50893c5653a42160c25f3f92aeb2164b292c8364..36928057b408d76fa6fc8d0131bf47f860fabe1a 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -1040,6 +1040,7 @@ component("base") { @@ -382,7 +382,7 @@ index 127a2829fafa04bfbab0b883304dfb815d7e1c22..61d7946e52862f3586b1e098d7d44a12 // The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that // can only be accomplished by overriding methods. diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm -index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec89873549 100644 +index dcaa590375f7cc42996a95ceb0804733d382f60b..e80557dfa4992823ffca81e77aaeef1afcabae09 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm @@ -26,6 +26,7 @@ @@ -412,7 +412,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec - (BOOL)hasKeyAppearance; - (long long)_resizeDirectionForMouseLocation:(CGPoint)location; - (BOOL)_isConsideredOpenForPersistentState; -@@ -159,6 +164,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event { +@@ -169,6 +174,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event { } @end @@ -421,7 +421,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec @implementation NativeWidgetMacNSWindowTitledFrame - (void)mouseDown:(NSEvent*)event { if (self.window.isMovable) -@@ -186,6 +193,8 @@ - (BOOL)usesCustomDrawing { +@@ -196,6 +203,8 @@ - (BOOL)usesCustomDrawing { } @end @@ -430,7 +430,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec @implementation NativeWidgetMacNSWindow { @private CommandDispatcher* __strong _commandDispatcher; -@@ -383,6 +392,8 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -393,6 +402,8 @@ - (NSAccessibilityRole)accessibilityRole { // NSWindow overrides. @@ -439,7 +439,7 @@ index 2b50e3c3750c9ac6dd84a514663062a5d754b43e..49ced9aa87d3bcb00cd3d76ac32d4eec + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { if (windowStyle & NSWindowStyleMaskTitled) { if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class]) -@@ -394,6 +405,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { +@@ -404,6 +415,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { return [super frameViewClassForStyleMask:windowStyle]; } @@ -474,18 +474,18 @@ index 5c0a53152095c2f799251f78b3c3464504104bfc..2c81ecef35d40036111cbd3dbdb44f70 // Beware: This view was briefly removed (in favor of a bare CALayer) in // https://crrev.com/c/1236675. The ordering of unassociated layers relative diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn -index 3c4ab01a87d5011925e08ca296722d99ac7f33d4..41c3a5128b9936f48587d6fc6ddd2050bffc9c36 100644 +index 2abb9b96124ad7867204dc5f6c678080472f28d7..067b3cc0d6a8b5990dc4bad0f15e47bd4922a26a 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn -@@ -384,6 +384,7 @@ viz_component("service") { +@@ -383,6 +383,7 @@ viz_component("service") { "frame_sinks/external_begin_frame_source_mac.h", ] } + deps += ["//electron/build/config:generate_mas_config"] } - if (is_android || use_ozone) { -@@ -684,6 +685,7 @@ viz_source_set("unit_tests") { + if (is_ios) { +@@ -695,6 +696,7 @@ viz_source_set("unit_tests") { "display_embedder/software_output_device_mac_unittest.mm", ] frameworks = [ "IOSurface.framework" ] @@ -579,10 +579,10 @@ index 4017ee032569466f5311e5c9612c82c086eab935..f2499bc084312a09b2324567d270fc1b return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index e0c49c4984509685acbcb3718bbf04b2909e3f16..5b8ea681dfee77a08bb451ec19f6e5d7ddf5b6d8 100644 +index 6655d0e6c01afc6e27045a288c65cd4cd4358094..dbebdc2cc87d7ebca0ea0388fea496108359d2c9 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -339,6 +339,7 @@ source_set("browser") { +@@ -340,6 +340,7 @@ source_set("browser") { "//ui/webui/resources", "//v8", "//v8:v8_version", @@ -792,7 +792,7 @@ index a1068589ad844518038ee7bc15a3de9bc5cba525..1ff781c49f086ec8015c7d3c44567dbe } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn -index 2e308872830d0e4105d381dc5bd702942b11de9a..201258c8995de5e8d9f2beadba0f7b34c86bcb6f 100644 +index 34297736b07e3f76d1768131f4f0e1db5e68c7fd..2686d23798d4d0dff2608a0c411d154aa91c12be 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -660,6 +660,7 @@ static_library("test_support") { @@ -811,7 +811,7 @@ index 2e308872830d0e4105d381dc5bd702942b11de9a..201258c8995de5e8d9f2beadba0f7b34 } mojom("content_test_mojo_bindings") { -@@ -1950,6 +1952,7 @@ test("content_browsertests") { +@@ -1953,6 +1955,7 @@ test("content_browsertests") { "//ui/shell_dialogs", "//ui/snapshot", "//ui/webui:test_support", @@ -819,7 +819,7 @@ index 2e308872830d0e4105d381dc5bd702942b11de9a..201258c8995de5e8d9f2beadba0f7b34 ] if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) { -@@ -3268,6 +3271,7 @@ test("content_unittests") { +@@ -3273,6 +3276,7 @@ test("content_unittests") { "//ui/shell_dialogs:shell_dialogs", "//ui/webui:test_support", "//url", @@ -932,10 +932,10 @@ index ae040bbac8755b677dc6e19383a2390df407e5a6..e95ca30f49506c66a37d6d5269929f43 namespace ui { diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn -index 7122165cf5ddb39056369e32fc29107f29e9f425..f11417e459e067f4976f59f1c58d4a5197520902 100644 +index 8bcbb663710445e977a3080209cd518d3fc2c6e0..0680d3af1f219235e20c0e83b80c0ea9cbf7b3a1 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn -@@ -196,6 +196,7 @@ source_set("audio") { +@@ -198,6 +198,7 @@ source_set("audio") { "CoreMedia.framework", ] weak_frameworks = [ "ScreenCaptureKit.framework" ] # macOS 13.0 diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index 407a93b96c0eb..d85e492ebc66c 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -133,10 +133,10 @@ index 05d3a12dd84c7005d46cc73b312f97ef418d96f5..4765de982802541b3efc7211d106acc7 const GURL& document_url, const WeakDocumentPtr& weak_document_ptr, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 6e46bfbd8ed2d9842f3ba7b82aa8e6eac1c4c12a..28e952d9766566e0a5213492aaf606aa56dc23f6 100644 +index 58a495e02e9298771fdb0542a40dbf99184a70d6..797d90b03331fc0de38b597ec1c29b5e8e58a05f 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -2206,7 +2206,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2207,7 +2207,7 @@ void RenderProcessHostImpl::CreateNotificationService( case RenderProcessHost::NotificationServiceCreatorType::kSharedWorker: case RenderProcessHost::NotificationServiceCreatorType::kDedicatedWorker: { storage_partition_impl_->GetPlatformNotificationContext()->CreateService( @@ -145,7 +145,7 @@ index 6e46bfbd8ed2d9842f3ba7b82aa8e6eac1c4c12a..28e952d9766566e0a5213492aaf606aa creator_type, std::move(receiver)); break; } -@@ -2214,7 +2214,7 @@ void RenderProcessHostImpl::CreateNotificationService( +@@ -2215,7 +2215,7 @@ void RenderProcessHostImpl::CreateNotificationService( CHECK(rfh); storage_partition_impl_->GetPlatformNotificationContext()->CreateService( diff --git a/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch b/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch index 93df0398cfba7..f3aafed22d11f 100644 --- a/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch +++ b/patches/chromium/osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch @@ -36,10 +36,10 @@ index f51591d21a0ce44028a3711cba72ceebb55b3567..31e01429e26f2856587b98a0213f1a5f Microsoft::WRL::ComPtr d3d11_texture; diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc -index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b03f02d11c 100644 +index 7c90eca71d39521ec1968a946b6c68c9a564d225..9c40397be51f1ce30cbe73d8c6fc0bb279c95d7a 100644 --- a/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc +++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool.cc -@@ -194,7 +194,7 @@ gfx::Size GetBufferSizeInPixelsForVideoPixelFormat( +@@ -196,7 +196,7 @@ gfx::Size GetBufferSizeInPixelsForVideoPixelFormat( bool FrameResources::Initialize() { auto* context = pool_->GetContext(); @@ -48,7 +48,7 @@ index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b0 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) gfx::BufferUsage::SCANOUT_VEA_CPU_READ #else -@@ -208,6 +208,23 @@ bool FrameResources::Initialize() { +@@ -210,6 +210,23 @@ bool FrameResources::Initialize() { const gfx::Size buffer_size_in_pixels = GetBufferSizeInPixelsForVideoPixelFormat(format_, coded_size_); @@ -72,7 +72,7 @@ index 4f216eb88e51c929468abf52049524177c3f0bb3..e216283028864b48516f690209b0e6b0 gpu::SharedImageUsageSet usage = #if BUILDFLAG(IS_MAC) gpu::SHARED_IMAGE_USAGE_MACOS_VIDEO_TOOLBOX | -@@ -248,7 +265,7 @@ bool FrameResources::Initialize() { +@@ -250,7 +267,7 @@ bool FrameResources::Initialize() { viz::GetSharedImageFormat(buffer_format); shared_image_ = diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index d61f71763f6ef..7a43bedae2fe0 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,7 +10,7 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 314ef160662faf6d7c00d9d6999abb1db8bbebe5..6cae6b280bfab3f1cbacbb4a4d71fa1d78b54619 100644 +index f3ea037e823cca68c1bd471d3f7d6566e35d232a..3737f0b72242c94a18a41f32cc7fab98de62e53b 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -808,8 +808,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( diff --git a/patches/chromium/picture-in-picture.patch b/patches/chromium/picture-in-picture.patch index f294c6286406d..2d9c53d3e2f24 100644 --- a/patches/chromium/picture-in-picture.patch +++ b/patches/chromium/picture-in-picture.patch @@ -38,7 +38,7 @@ index 8168b4cfbafd42fa93a5aa9a3691c2552fabfb86..ba49212bd76d209f99c1cee649fc1466 ui::ImageModel::FromVectorIcon(*icon, kColorPipWindowForeground, kCloseButtonIconSize)); diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc -index f246f7ea6b39a881721171515049dda8d3f2ac0b..e8524bb6706ae06a0feabccbe44250580098e937 100644 +index 3b53115eb59e81b690cdef96a2e303c0c8407a8d..531b183bcc017daeefcf158fd58570a889c4b7df 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.cc @@ -18,9 +18,11 @@ diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index 8dada6327322e..69423cfc8bf46 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -881,10 +881,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 5b8ea681dfee77a08bb451ec19f6e5d7ddf5b6d8..15ad186308981ddfbbe32f47ef9127ab051132e3 100644 +index dbebdc2cc87d7ebca0ea0388fea496108359d2c9..900b563fef7e97676097806a59bc2633026f852f 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3134,8 +3134,9 @@ source_set("browser") { +@@ -3137,8 +3137,9 @@ source_set("browser") { "//ppapi/shared_impl", ] diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index d571ca89d8840..0333718125f9e 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -44,10 +44,10 @@ index 20fcda4eb20459b69247003c51c2a3ed37c7b1e8..9bcab4e1e8a0fa429488555f4f7bd1c5 void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index d7bf74c206cedc628d0b92fff25c29393f6c9d95..30593ec9ecd2b135a6054ed50e95c849c09b47bd 100644 +index 17009e944d6f3d23c721be521bf7843dbce1320e..09c1b87b58fb2bf99c2a3b81b300e0cc1b9a0115 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5839,6 +5839,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { +@@ -5847,6 +5847,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { return text_input_manager_.get(); } @@ -60,7 +60,7 @@ index d7bf74c206cedc628d0b92fff25c29393f6c9d95..30593ec9ecd2b135a6054ed50e95c849 RenderWidgetHostImpl* render_widget_host) { return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index a35dc028fbcf202a7ba1aa7213f8c815e9a31e3f..fe3b8cf40ea7c26aade59c7224416594509f6308 100644 +index b3b274099fbe0df8892e686236e9a1f759c4d0e8..2baa178ed1d147c92de0ed32e94d9e2dbfc98310 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -1178,6 +1178,7 @@ class CONTENT_EXPORT WebContentsImpl diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index bf60e0c2a1249..e2db2163f2e0c 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 2a17aa6a3f687d60a7ca0e839e59f637819a9376..95a714ca4a0023dc31b212611f91fc1d03543656 100644 +index 9c301542a54c3aeb5ec7ce408e312b48b33d22e3..d41390bf10906e6998b2c66476032541bfb97141 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9971,25 +9971,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9980,25 +9980,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/render_widget_host_view_base.patch b/patches/chromium/render_widget_host_view_base.patch index 2ef6f6e7fd7a5..d0485ef878047 100644 --- a/patches/chromium/render_widget_host_view_base.patch +++ b/patches/chromium/render_widget_host_view_base.patch @@ -24,7 +24,7 @@ index ce00b0540a7ac7f7c7b4c65f1a1343f72ae21c42..cc3b694431f14b166a305a446a48c25d const blink::WebMouseEvent& event, const ui::LatencyInfo& latency) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h -index ee54e91c031849307e51905fbdcdf53c5f17bf8f..72e12f6910e581c3a6f19241523322dcb6959c61 100644 +index d451701b81bf2c89474c7d1cd22e781d870128a4..959fd197e65bac94b3f3c55059dccb147001943e 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -30,6 +30,8 @@ @@ -61,8 +61,8 @@ index ee54e91c031849307e51905fbdcdf53c5f17bf8f..72e12f6910e581c3a6f19241523322dc void OnAutoscrollStart() override; const viz::DisplayHitTestQueryMap& GetDisplayHitTestQuery() const override; -@@ -195,6 +203,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase - const gfx::Rect& keyboard_rect) override {} +@@ -196,6 +204,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase + void NotifyContextMenuInsetsObservers(const gfx::Rect&) override {} bool IsHTMLFormPopup() const override; + virtual void InitAsGuest(RenderWidgetHostView* parent_host_view, diff --git a/patches/chromium/revert_enable_crel_for_arm32_targets.patch b/patches/chromium/revert_enable_crel_for_arm32_targets.patch index 08f5f6afbb8cb..79b7b6b8545f7 100644 --- a/patches/chromium/revert_enable_crel_for_arm32_targets.patch +++ b/patches/chromium/revert_enable_crel_for_arm32_targets.patch @@ -8,10 +8,10 @@ as its one of our supported platforms. https://chromium-review.googlesource.com/q/I3a62f02f564f07be63173b0773b4ecaffbe939b9 diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index 6cbc6b8cfae044c36e83cc54c23dc500b445e5da..1d9798b5bffb4fb3166b72911cea5ba1282a757f 100644 +index e7a60d56b5425eae1e1a121189dc49390327cf8a..d63b933b095524e0e4b75b890eda95824c580c0d 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -628,7 +628,8 @@ config("compiler") { +@@ -617,7 +617,8 @@ config("compiler") { # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF # (excluding toolchains that use an older version of LLVM). diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index d60a15c9a8bb2..bd18c208f5d1f 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,7 +6,7 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index f1ef5e0d6c1c659506656df5f98a8637729009a0..5f2e9766fe7e8a0ced2a15af7f8fcd529c02c1bb 100644 +index 0a5556cb6eac8be8af3a1691687205e683157794..a88f184db56cc3aab5d67cd1a0f73dff0f002a34 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -1324,7 +1324,7 @@ bool RenderThreadImpl::IsLcdTextEnabled() { @@ -17,4 +17,4 @@ index f1ef5e0d6c1c659506656df5f98a8637729009a0..5f2e9766fe7e8a0ced2a15af7f8fcd52 + return base::CommandLine::ForCurrentProcess()->HasSwitch("scroll-bounce"); } - gpu::GpuMemoryBufferManager* RenderThreadImpl::GetGpuMemoryBufferManager() { + blink::scheduler::WebThreadScheduler* diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index 05a9c39728926..bcb11e528c7ed 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,10 +22,10 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 28e952d9766566e0a5213492aaf606aa56dc23f6..c0d553dcbb37db7657c2374d4a6b978d7bfee88c 100644 +index 797d90b03331fc0de38b597ec1c29b5e8e58a05f..d771acfb686270b232e2705e9ca4a7ea03d3bcc2 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -1814,6 +1814,10 @@ bool RenderProcessHostImpl::Init() { +@@ -1815,6 +1815,10 @@ bool RenderProcessHostImpl::Init() { std::unique_ptr sandbox_delegate = std::make_unique( *cmd_line, IsPdf(), IsJitDisabled()); diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index 3c76a5553a504..f4ac29b0edba8 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,10 +9,10 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index b6870d74a761a847e9952d409b37ddb778790b08..052e518e42c7c2d7f0e17ebc8dc4e8ec78215802 100644 +index a1ba9ea494a6b2da2b3fdfeb02491d37b3da9074..59fb6d36a047c2ce43dd4de3c3c4492275d5f0cb 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -3908,6 +3908,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3916,6 +3916,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, params.main_frame_name, GetOpener(), primary_main_frame_policy, base::UnguessableToken::Create()); @@ -26,7 +26,7 @@ index b6870d74a761a847e9952d409b37ddb778790b08..052e518e42c7c2d7f0e17ebc8dc4e8ec std::unique_ptr delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -3918,6 +3925,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3926,6 +3933,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, view_ = CreateWebContentsView(this, std::move(delegate), &render_view_host_delegate_view_); } diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index 5702e57110afd..82e9e2a080941 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index ea6ba910234659d1213b1f3f624da0f49b851725..314ef160662faf6d7c00d9d6999abb1db8bbebe5 100644 +index 71fbe83a33fa247f82d7315767ff9a4aee9d3fca..f3ea037e823cca68c1bd471d3f7d6566e35d232a 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8748,6 +8748,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8754,6 +8754,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,10 +37,10 @@ index ea6ba910234659d1213b1f3f624da0f49b851725..314ef160662faf6d7c00d9d6999abb1d if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 052e518e42c7c2d7f0e17ebc8dc4e8ec78215802..1e6ee8c65d56f2821485f855bbf4e8bb8212058c 100644 +index 59fb6d36a047c2ce43dd4de3c3c4492275d5f0cb..a793aa22e549e0fa8fee4665775761db97496eb2 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4183,21 +4183,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( +@@ -4191,21 +4191,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"), "WebContentsImpl::PreHandleKeyboardEvent"); @@ -78,7 +78,7 @@ index 052e518e42c7c2d7f0e17ebc8dc4e8ec78215802..1e6ee8c65d56f2821485f855bbf4e8bb } bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { -@@ -4356,7 +4360,7 @@ void WebContentsImpl::EnterFullscreenMode( +@@ -4364,7 +4368,7 @@ void WebContentsImpl::EnterFullscreenMode( OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode"); DCHECK(CanEnterFullscreenMode(requesting_frame)); DCHECK(requesting_frame->IsActive()); diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index 81333a7cd013f..c8bf328c6faea 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -26,10 +26,10 @@ index 7a2d251ba2d13d0a34df176111e6524a27b87f55..cbbe0fbdd25a0f7859b113fdb3dcd9ce // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index ac71ba108e5085aadc3e7f22e7bf690c38870f2b..d0c7bd3bf1c20e505548c7146ab71f2af2e57b43 100644 +index c1d1c8a094a9161856d12945945c31bb187fe5a2..45bd68c7c54c4465831a146ed5d4e72414c18770 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -903,6 +903,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -895,6 +895,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } @@ -43,10 +43,10 @@ index ac71ba108e5085aadc3e7f22e7bf690c38870f2b..d0c7bd3bf1c20e505548c7146ab71f2a const v8::Local& worker) { GetContentClient()->renderer()->DidInitializeWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index a3aaa495c754de62dc1970eebd9bba80bdbfb869..a5c668a7fd8426eb409b6577cf1b6ce5d24b9fde 100644 +index ce5aee7746ba05cdf185899f0b30e1f11f2ba45f..1b8af76a25eb06626ed7f211552eb5658d03429d 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -199,6 +199,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -55,10 +55,10 @@ index a3aaa495c754de62dc1970eebd9bba80bdbfb869..a5c668a7fd8426eb409b6577cf1b6ce5 const blink::WebSecurityOrigin& script_origin) override; blink::ProtocolHandlerSecurityLevel GetProtocolHandlerSecurityLevel( diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index 336f771d0587751bd826c1288b4f77b2d672f95b..6a843c144b8f602e8232b2cae7954824c4ad6bc3 100644 +index 617b7a582f1331796129f78c12558ebc81850bf9..ae2de15efaae470ec8b137323c1ad543c465c5ad 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -677,6 +677,7 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -672,6 +672,7 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} diff --git a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch index fe7a511bf53aa..d1ea746900d19 100644 --- a/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch +++ b/patches/chromium/worker_feat_add_hook_to_notify_script_ready.patch @@ -35,10 +35,10 @@ index cbbe0fbdd25a0f7859b113fdb3dcd9ce57e597d6..1345bb5008e1b4fc3a450f7e353d52ec // from the worker thread. virtual void WillDestroyWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index d0c7bd3bf1c20e505548c7146ab71f2af2e57b43..020a2cc8125eea8a017950fdcdec4a2810201e21 100644 +index 45bd68c7c54c4465831a146ed5d4e72414c18770..e6c7f23da99c63e44d14c4eb16f6a647ad9f750e 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -915,6 +915,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( +@@ -907,6 +907,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated( worker); } @@ -52,10 +52,10 @@ index d0c7bd3bf1c20e505548c7146ab71f2af2e57b43..020a2cc8125eea8a017950fdcdec4a28 const blink::WebSecurityOrigin& script_origin) { return GetContentClient()->renderer()->AllowScriptExtensionForServiceWorker( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index a5c668a7fd8426eb409b6577cf1b6ce5d24b9fde..a53bfe2f7566828433c3c6c8e95cbd12cd6189a9 100644 +index 1b8af76a25eb06626ed7f211552eb5658d03429d..cbaf3450fc2f56cc66413c48e178879074ea072d 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -199,6 +199,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -198,6 +198,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -65,10 +65,10 @@ index a5c668a7fd8426eb409b6577cf1b6ce5d24b9fde..a53bfe2f7566828433c3c6c8e95cbd12 bool AllowScriptExtensionForServiceWorker( const blink::WebSecurityOrigin& script_origin) override; diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index 6a843c144b8f602e8232b2cae7954824c4ad6bc3..ede4908eaad7f1c22fac0fe5071189c50fb5d71a 100644 +index ae2de15efaae470ec8b137323c1ad543c465c5ad..852e76537ebcabd37c22c17def182295e7e61bdd 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -677,6 +677,8 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -672,6 +672,8 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} From 33964528e1b1d94be0f275592940627b1644c0b6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:47:52 -0500 Subject: [PATCH 127/339] fix: `assert.ok` in the renderer process (#46561) fix: assert.ok in the renderer process Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- ...om_error_callback_in_node_isolatesettings.patch | 2 +- ...fix_assert_module_in_the_renderer_process.patch | 14 -------------- ..._attach_cppgc_heap_on_v8_isolate_creation.patch | 6 +++--- spec/node-spec.ts | 11 +++++++++++ 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch b/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch index 2e9c3f9840dff..3eecb039f5d7e 100644 --- a/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch +++ b/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch @@ -13,7 +13,7 @@ To fix this issue, provide the interface oom_error_callback to enable a custom oom error callback set from Electron. diff --git a/src/api/environment.cc b/src/api/environment.cc -index 32fc075e97eebca6c47e796ac5308915746ffa2a..e72bee385865c7d34e9eea6b90c6d911d592f8af 100644 +index fc9b056d2f7e25109100fbde5f3ab0aebc8c619a..9b155213ce301df7e396a4a113992499fc7e9910 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -241,7 +241,10 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { diff --git a/patches/node/fix_assert_module_in_the_renderer_process.patch b/patches/node/fix_assert_module_in_the_renderer_process.patch index 8efa5f5b272df..86cfb0a302ffe 100644 --- a/patches/node/fix_assert_module_in_the_renderer_process.patch +++ b/patches/node/fix_assert_module_in_the_renderer_process.patch @@ -43,20 +43,6 @@ index 59b5a16f1309a5e4055bccfdb7a529045ad30402..bfdaf6211466a01b64b7942f7b16c480 let filename = call.getFileName(); const line = call.getLineNumber() - 1; -diff --git a/src/api/environment.cc b/src/api/environment.cc -index fc9b056d2f7e25109100fbde5f3ab0aebc8c619a..32fc075e97eebca6c47e796ac5308915746ffa2a 100644 ---- a/src/api/environment.cc -+++ b/src/api/environment.cc -@@ -247,6 +247,9 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { - auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ? - s.prepare_stack_trace_callback : PrepareStackTraceCallback; - isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb); -+ } else { -+ auto env = Environment::GetCurrent(isolate); -+ env->set_prepare_stack_trace_callback(Local()); - } - } - diff --git a/src/node_options.cc b/src/node_options.cc index 3608ab2b4aeb09e985ca98e23f2dff23567ade71..620776c06d835eb1bfeed060751c570e8d435b29 100644 --- a/src/node_options.cc diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch index 1dce7f1de16f0..7ed48324d3c39 100644 --- a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -18,10 +18,10 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs from the above issue. diff --git a/src/api/environment.cc b/src/api/environment.cc -index e72bee385865c7d34e9eea6b90c6d911d592f8af..d3d1040b7a1a6b9c4a1fa2399e9235ec3b0b2990 100644 +index 9b155213ce301df7e396a4a113992499fc7e9910..8fe560014216f1fcea7f6e804816765999cebaa2 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc -@@ -315,6 +315,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, +@@ -312,6 +312,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, MultiIsolatePlatform* platform, const SnapshotData* snapshot_data, const IsolateSettings& settings) { @@ -32,7 +32,7 @@ index e72bee385865c7d34e9eea6b90c6d911d592f8af..d3d1040b7a1a6b9c4a1fa2399e9235ec Isolate* isolate = Isolate::Allocate(); if (isolate == nullptr) return nullptr; -@@ -358,9 +362,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, +@@ -355,9 +359,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const EmbedderSnapshotData* snapshot_data, diff --git a/spec/node-spec.ts b/spec/node-spec.ts index 420698f88c702..78c4905d9cd8a 100644 --- a/spec/node-spec.ts +++ b/spec/node-spec.ts @@ -983,6 +983,17 @@ describe('node feature', () => { }); }); + itremote('handles assert module assertions as expected', () => { + const assert = require('node:assert'); + try { + assert.ok(false); + expect.fail('assert.ok(false) should throw'); + } catch (err) { + console.log(err); + expect(err).to.be.instanceOf(assert.AssertionError); + } + }); + it('Can find a module using a package.json main field', () => { const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')], { stdio: 'inherit' }); expect(result.status).to.equal(0); From ffb4f0f812b0ccc37ae759ef8f42fc9c61d36637 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:40:15 -0500 Subject: [PATCH 128/339] refactor: use v8::True(isolate) and v8::False(isolate) (#46571) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/common/gin_helper/callback.cc | 3 +-- shell/common/gin_helper/event_emitter_caller.cc | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/shell/common/gin_helper/callback.cc b/shell/common/gin_helper/callback.cc index cd5a0a0952e60..b5342ebcb7238 100644 --- a/shell/common/gin_helper/callback.cc +++ b/shell/common/gin_helper/callback.cc @@ -54,8 +54,7 @@ void CallTranslator(v8::Local external, args->ThrowTypeError("One-time callback was called more than once"); return; } else { - state->Set(context, called_symbol, v8::Boolean::New(isolate, true)) - .ToChecked(); + state->Set(context, called_symbol, v8::True(isolate)).ToChecked(); } } diff --git a/shell/common/gin_helper/event_emitter_caller.cc b/shell/common/gin_helper/event_emitter_caller.cc index 360c84bebffcd..cc10c742a653d 100644 --- a/shell/common/gin_helper/event_emitter_caller.cc +++ b/shell/common/gin_helper/event_emitter_caller.cc @@ -18,7 +18,7 @@ v8::Local CallMethodWithArgs( // CallbackScope and MakeCallback both require an active node::Environment if (!node::Environment::GetCurrent(isolate)) - return handle_scope.Escape(v8::Boolean::New(isolate, false)); + return handle_scope.Escape(v8::False(isolate)); node::CallbackScope callback_scope{isolate, v8::Object::New(isolate), node::async_context{0, 0}}; @@ -39,7 +39,7 @@ v8::Local CallMethodWithArgs( if (v8::Local localRet; ret.ToLocal(&localRet)) return handle_scope.Escape(localRet); - return handle_scope.Escape(v8::Boolean::New(isolate, false)); + return handle_scope.Escape(v8::False(isolate)); } } // namespace gin_helper::internal From b23473355554db94262804541d132709af3b00cd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:12:51 -0500 Subject: [PATCH 129/339] refactor: remove unnecessary `const_cast` (#46567) refactor: remove unnecessary const_cast unnecessary since July 2019 in 50b9c70 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_web_ui_controller_factory.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_web_ui_controller_factory.cc b/shell/browser/electron_web_ui_controller_factory.cc index 183a9bb6dad25..d4b7d022e2aa3 100644 --- a/shell/browser/electron_web_ui_controller_factory.cc +++ b/shell/browser/electron_web_ui_controller_factory.cc @@ -28,7 +28,7 @@ content::WebUI::TypeID ElectronWebUIControllerFactory::GetWebUIType( if (const std::string_view host = url.host_piece(); host == chrome::kChromeUIDevToolsHost || host == chrome::kChromeUIAccessibilityHost) { - return const_cast(this); + return this; } return content::WebUI::kNoWebUI; From 28eb0e6f3dfb706701826d70d56eff0fe05294b5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:19:20 -0500 Subject: [PATCH 130/339] fix: crash on parent window close and focur/blur (#46580) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_browser_window.cc | 4 ++-- spec/api-browser-window-spec.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index eb30d2fea8042..8d92652b033e6 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -219,14 +219,14 @@ void BrowserWindow::CloseImmediately() { } void BrowserWindow::Focus() { - if (api_web_contents_->IsOffScreen()) + if (api_web_contents_ && api_web_contents_->IsOffScreen()) FocusOnWebView(); else BaseWindow::Focus(); } void BrowserWindow::Blur() { - if (api_web_contents_->IsOffScreen()) + if (api_web_contents_ && api_web_contents_->IsOffScreen()) BlurWebView(); else BaseWindow::Blur(); diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index a1d90f628e21a..2e6c7a7995507 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -4930,6 +4930,18 @@ describe('BrowserWindow module', () => { expect(w.getChildWindows().length).to.equal(0); }); + it('can handle parent window close with focus or blur events', (done) => { + const w = new BrowserWindow({ show: false }); + const c = new BrowserWindow({ show: false, parent: w }); + + c.on('closed', () => { + w.focus(); + done(); + }); + + w.close(); + }); + ifit(process.platform === 'darwin')('only shows the intended window when a child with siblings is shown', async () => { const w = new BrowserWindow({ show: false }); const childOne = new BrowserWindow({ show: false, parent: w }); From 33fafd508c853008304a2dad875a5be64739aac7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:20:47 -0500 Subject: [PATCH 131/339] docs: updated tray doc with tooltip removing (#46592) added tooltip removing doc Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Yuri --- docs/api/tray.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/tray.md b/docs/api/tray.md index 6d11505ac5602..27c1d948a1246 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -238,7 +238,7 @@ Sets the `image` associated with this tray icon when pressed on macOS. * `toolTip` string -Sets the hover text for this tray icon. +Sets the hover text for this tray icon. Setting the text to an empty string will remove the tooltip. #### `tray.setTitle(title[, options])` _macOS_ From 8b3c52f242c4cd09571e1bc527c5717f4b2abf14 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:16:48 +0200 Subject: [PATCH 132/339] fix: remove obsoleted `--inspect-brk` logic (#46584) fix: remove obsoleted --inspect-brk logic Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- lib/browser/init.ts | 1 - ...ode_entrypoint_to_be_a_builtin_module.patch | 18 ------------------ typings/internal-ambient.d.ts | 1 - 3 files changed, 20 deletions(-) diff --git a/lib/browser/init.ts b/lib/browser/init.ts index 3f50212555db4..ddca14dbb9fe8 100644 --- a/lib/browser/init.ts +++ b/lib/browser/init.ts @@ -218,7 +218,6 @@ if (packagePath) { } else { // Call appCodeLoaded before just for safety, it doesn't matter here as _load is synchronous appCodeLoaded!(); - process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false); Module._load(path.join(packagePath, mainStartupScript), Module, true); } } else { diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch index db45eab11fe45..d8640574d1cea 100644 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch @@ -7,24 +7,6 @@ This allows embedders to tell Node.js what the first "real" file is when they use themselves as the entry point. We should try to upstream some form of this. -diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 9b5772fe9b8babbb892c7a5ec79258472da55a76..3568fd6ea0816f62d97d46f5d497bb1b23bf4e25 100644 ---- a/lib/internal/modules/cjs/loader.js -+++ b/lib/internal/modules/cjs/loader.js -@@ -1555,6 +1555,13 @@ Module.prototype._compile = function(content, filename, format) { - this[kIsExecuting] = true; - if (this[kIsMainSymbol] && getOptionValue('--inspect-brk')) { - const { callAndPauseOnStart } = internalBinding('inspector'); -+ // process._firstFileName is used by Embedders to tell node what -+ // the first "real" file is when they use themselves as the entry -+ // point -+ if (process._firstFileName) { -+ resolvedArgv = process._firstFileName; -+ delete process._firstFileName; -+ } - result = callAndPauseOnStart(compiledWrapper, thisValue, exports, - require, module, filename, dirname, - process, localGlobal, localBuffer); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index d1c05d1717cdc825c4e48885c963c9ed65bcf51c..278665921c5160ff10b3178db27d4df319fab6b3 100644 --- a/lib/internal/process/pre_execution.js diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index c0f5c030526b5..21e652df5e453 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -255,7 +255,6 @@ declare namespace NodeJS { once(event: 'document-end', listener: () => any): this; // Additional properties - _firstFileName?: string; _serviceStartupScript: string; _getOrCreateArchive?: (path: string) => NodeJS.AsarArchive | null; From 70fd706ea48de584df92faa548f9eab99351768b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:28:58 +0200 Subject: [PATCH 133/339] fix: `NativeWindow.window_id()` returns same value for all windows (#46590) fix: NativeWindow.window_id() returns same value for all windows Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 5 ----- shell/browser/native_window.h | 5 +++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 241577383e1ff..d8a0e49c83f69 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -94,8 +94,6 @@ gfx::Size GetExpandedWindowSize(const NativeWindow* window, gfx::Size size) { NativeWindow::NativeWindow(const gin_helper::Dictionary& options, NativeWindow* parent) : widget_(std::make_unique()), parent_(parent) { - ++next_id_; - options.Get(options::kFrame, &has_frame_); options.Get(options::kTransparent, &transparent_); options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_); @@ -820,9 +818,6 @@ void NativeWindow::HandlePendingFullscreenTransitions() { SetFullScreen(next_transition); } -// static -int32_t NativeWindow::next_id_ = 0; - bool NativeWindow::IsTranslucent() const { // Transparent windows are translucent if (transparent()) { diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 0d675b4f70dc6..cc4d150ca72b1 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -409,7 +409,7 @@ class NativeWindow : public base::SupportsUserData, NativeWindow* parent() const { return parent_; } bool is_modal() const { return is_modal_; } - int32_t window_id() const { return next_id_; } + int32_t window_id() const { return window_id_; } void add_child_window(NativeWindow* child) { child_windows_.push_back(child); @@ -472,7 +472,8 @@ class NativeWindow : public base::SupportsUserData, private: std::unique_ptr widget_; - static int32_t next_id_; + static inline int32_t next_id_ = 0; + const int32_t window_id_ = ++next_id_; // The content view, weak ref. raw_ptr content_view_ = nullptr; From 22b6eefc50a96b3ef0029c5d0ca7c9b7034147a3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:21:20 +0200 Subject: [PATCH 134/339] build: ignore files in .git when running markdownlint-cli2 (#46611) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- script/lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/lint.js b/script/lint.js index bf9109cb7e6f0..06f0b7ac305cf 100755 --- a/script/lint.js +++ b/script/lint.js @@ -278,7 +278,7 @@ const LINTERS = [{ }, { key: 'md', roots: ['.'], - ignoreRoots: ['node_modules', 'spec/node_modules'], + ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'], test: filename => filename.endsWith('.md'), run: async (opts, filenames) => { let errors = false; From 9d3696e4b3fc16951ab04d83a85595986db44aef Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:01:09 -0400 Subject: [PATCH 135/339] chore: bump chromium to 136.0.7103.25 (36-x-y) (#46603) * chore: bump chromium in DEPS to 136.0.7103.25 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- patches/chromium/add_didinstallconditionalfeatures.patch | 4 ++-- patches/chromium/can_create_window.patch | 6 +++--- .../chromium/expose_setuseragent_on_networkcontext.patch | 2 +- .../feat_configure_launch_options_for_service_process.patch | 4 ++-- .../feat_corner_smoothing_css_rule_and_blink_painting.patch | 4 ++-- ..._enable_passing_exit_code_on_service_process_crash.patch | 4 ++-- patches/chromium/make_gtk_getlibgtk_public.patch | 2 +- .../chromium/mas_avoid_private_macos_api_usage.patch.patch | 2 +- ...ervice_allow_remote_certificate_verification_logic.patch | 2 +- ...rt_is_newly_created_to_allow_for_browser_initiated.patch | 2 +- patches/chromium/printing.patch | 4 ++-- patches/chromium/webview_fullscreen.patch | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/DEPS b/DEPS index 79b39a83017ac..84c9d9f31b22d 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.17', + '136.0.7103.25', 'node_version': 'v22.14.0', 'nan_version': diff --git a/patches/chromium/add_didinstallconditionalfeatures.patch b/patches/chromium/add_didinstallconditionalfeatures.patch index fe920c58a4766..3ab0381ec2c43 100644 --- a/patches/chromium/add_didinstallconditionalfeatures.patch +++ b/patches/chromium/add_didinstallconditionalfeatures.patch @@ -23,10 +23,10 @@ index 44da0544b778d6ff4c14b6f4e8463cb8260d2f0d..8ae8939af4141a684b7a6d50a43e1abb int32_t world_id) {} virtual void DidClearWindowObject() {} diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 4185aa3e07df8f2a3061d18e87f39cd5d79baead..81261373a194210d97c723ed525cb75a2bbeafad 100644 +index b78c7f64fd5e21a8d89318791bf639ab367d2d48..e8850312da428bcb9ec45b7c8605be1e516b22de 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -4802,6 +4802,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, +@@ -4800,6 +4800,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local context, observer.DidCreateScriptContext(context, world_id); } diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index eac254856e771..48655e4a29fc7 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,7 +9,7 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 7d8baae182adb7664912c930463aa8b461ccbdc8..71fbe83a33fa247f82d7315767ff9a4aee9d3fca 100644 +index d905ce52d71b2e2a4163fda68f584b3a3f305716..57a450929264e3f7592b5eab5126d176dbff8a58 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -9644,6 +9644,7 @@ void RenderFrameHostImpl::CreateNewWindow( @@ -148,10 +148,10 @@ index da319cb20733150366d85bee95609f0f2d9def7f..8a18958035cc1dd26be558349f64f772 // typically happens when popups are created. virtual void WebContentsCreated(WebContents* source_contents, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc -index 0755a49ee7558ee0c5e76f602c514bb8a7c3a019..4185aa3e07df8f2a3061d18e87f39cd5d79baead 100644 +index b46ceca2998e6f3706aff3705b9639948eebf60b..b78c7f64fd5e21a8d89318791bf639ab367d2d48 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc -@@ -6927,6 +6927,10 @@ WebView* RenderFrameImpl::CreateNewWindow( +@@ -6925,6 +6925,10 @@ WebView* RenderFrameImpl::CreateNewWindow( request.HasUserGesture(), GetWebFrame()->IsAdFrame(), GetWebFrame()->IsAdScriptInStack()); diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index 64a0872ed743c..29b6dc16658b6 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -63,7 +63,7 @@ index 930e0bd987c48d111b2c8d71147c09e4418bda6c..9373a53c5cac879c689fcea77f1dbbb3 void SetEnableReferrers(bool enable_referrers) override; #if BUILDFLAG(IS_CT_SUPPORTED) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 6d79f4a4cb1a40be907ef52e35f0e5a0acab31fb..95ff46a7ca2654166d18027b986f6ec6c2a3d361 100644 +index 399d0363749e9b79749185bcc1dcd746b026231e..1abd312f068e9326e4a9f95ed58bdb04e35afea8 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom @@ -1275,6 +1275,9 @@ interface NetworkContext { diff --git a/patches/chromium/feat_configure_launch_options_for_service_process.patch b/patches/chromium/feat_configure_launch_options_for_service_process.patch index 6e276d6c05e77..558d8392f093f 100644 --- a/patches/chromium/feat_configure_launch_options_for_service_process.patch +++ b/patches/chromium/feat_configure_launch_options_for_service_process.patch @@ -187,7 +187,7 @@ index 96c9563aac5847e742de5d9c9236f78bcb6cfd9c..73c9d585579ad5bdc407687b8becd0b7 host->GetChildProcess()->BindServiceInterface(std::move(receiver)); } diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index 8bb497717298886bc6db04ebcba9be1f3b29ab73..60c4a301c32a46cdc332cccd1cd4ae78802ee8b9 100644 +index 9e01e61bf1fce448a93eaa3d5f363fc835b78538..d210af6fb317c922a8415a67a7ccd1d8a4a88ea1 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc @@ -190,11 +190,13 @@ const ChildProcessData& UtilityProcessHost::GetData() { @@ -237,7 +237,7 @@ index 8bb497717298886bc6db04ebcba9be1f3b29ab73..60c4a301c32a46cdc332cccd1cd4ae78 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() { return static_cast(process_->GetHost()) ->child_process(); -@@ -455,9 +481,26 @@ bool UtilityProcessHost::StartProcess() { +@@ -456,9 +482,26 @@ bool UtilityProcessHost::StartProcess() { } #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN) diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch index f3bd3e35e44b9..41283b3c18ae1 100644 --- a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -112,10 +112,10 @@ index aa3a6b93cceae8c8bfbefdd7a043ae576f921979..17544d7ed4757fb51e2f82fd1b906381 property_.sorting_key = (-property_.priority, internal_visited_order, diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 -index b44da8add10fdeadd24653441115df035a93dd50..ce7c7bd32f3a8ea262f01c408474ff4ffe7fe0aa 100644 +index 15ed205144fbb058bcc88953acb8380574cb7044..1795bceb2e7548c6e072c1bd2ce6abd0b3bd8188 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5 -@@ -8795,6 +8795,24 @@ +@@ -8796,6 +8796,24 @@ property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], }, diff --git a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch index 643757d18684e..de4299dc23aca 100644 --- a/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch +++ b/patches/chromium/feat_enable_passing_exit_code_on_service_process_crash.patch @@ -80,10 +80,10 @@ index 801db538979ba62facdcf3a472dade56723ca639..7abac9a5b13b393713534ae51664c2e5 private: const std::string service_interface_name_; diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc -index 60c4a301c32a46cdc332cccd1cd4ae78802ee8b9..85fc14b2ef520abb191f926dc44a272f4c9e874b 100644 +index d210af6fb317c922a8415a67a7ccd1d8a4a88ea1..0de01879f618555030e87ea79a94d41232811c2c 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc -@@ -540,7 +540,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { +@@ -541,7 +541,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) { // Take ownership of |client_| so the destructor doesn't notify it of // termination. auto client = std::move(client_); diff --git a/patches/chromium/make_gtk_getlibgtk_public.patch b/patches/chromium/make_gtk_getlibgtk_public.patch index ef7434072eeda..9a0c214c6a368 100644 --- a/patches/chromium/make_gtk_getlibgtk_public.patch +++ b/patches/chromium/make_gtk_getlibgtk_public.patch @@ -7,7 +7,7 @@ Allows embedders to get a handle to the gdk_pixbuf library already loaded in the process. diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc -index d6ac3233de3165d2be3e5d4fb3e221dca8b8d415..4f15069af07b7adc3e3c45e67e23ec8525668c21 100644 +index f28cf275db0205186fe6143b7e1550b6c30a4435..346992e202c507eeac454d657507e1bd336175fc 100644 --- a/ui/gtk/gtk_compat.cc +++ b/ui/gtk/gtk_compat.cc @@ -69,11 +69,6 @@ void* GetLibGio() { diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 5e149d33def9e..80fc10643c7b7 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -579,7 +579,7 @@ index 4017ee032569466f5311e5c9612c82c086eab935..f2499bc084312a09b2324567d270fc1b return kAttributes; } diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index 6655d0e6c01afc6e27045a288c65cd4cd4358094..dbebdc2cc87d7ebca0ea0388fea496108359d2c9 100644 +index 9ed48bb42381690b8a5c1a5d3aef7f82cb59ac65..56118dba5d55a937290a1234bd1c70d4d301de25 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn @@ -340,6 +340,7 @@ source_set("browser") { diff --git a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch index b79aaef2ab0ae..0db6c1d159029 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -178,7 +178,7 @@ index f2dcec57a22d95892a08f1fa43696d6eea46a820..930e0bd987c48d111b2c8d71147c09e4 std::unique_ptr internal_host_resolver_; std::set, base::UniquePtrComparator> diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index a9c82bcf9a4ebf946791eccc35a0dcfff0c18288..6d79f4a4cb1a40be907ef52e35f0e5a0acab31fb 100644 +index feed273c8c77d6c1e21cd82547860f945b651ae9..399d0363749e9b79749185bcc1dcd746b026231e 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom @@ -308,6 +308,17 @@ struct SocketBrokerRemotes { diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 7a43bedae2fe0..63d244ca9ba91 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,7 +10,7 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index f3ea037e823cca68c1bd471d3f7d6566e35d232a..3737f0b72242c94a18a41f32cc7fab98de62e53b 100644 +index 6372615eecc862325e88ebb9b189d380459163c7..38e360457b2fcb658db8c94fe26169a3f61a9cbc 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -808,8 +808,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index 69423cfc8bf46..bd6e2546cd567 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -881,10 +881,10 @@ index 97cb6458bc9eec767db89b56abfc5f4b4136ff7b..d9a0b343158b8464b5c9aa8e0e655c0b ScriptingThrottler scripting_throttler_; diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn -index dbebdc2cc87d7ebca0ea0388fea496108359d2c9..900b563fef7e97676097806a59bc2633026f852f 100644 +index 56118dba5d55a937290a1234bd1c70d4d301de25..2086718aba634ffed079c108b04cdd7993133961 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn -@@ -3137,8 +3137,9 @@ source_set("browser") { +@@ -3139,8 +3139,9 @@ source_set("browser") { "//ppapi/shared_impl", ] diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index 82e9e2a080941..fae5f9ef2e997 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,7 +15,7 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 71fbe83a33fa247f82d7315767ff9a4aee9d3fca..f3ea037e823cca68c1bd471d3f7d6566e35d232a 100644 +index 57a450929264e3f7592b5eab5126d176dbff8a58..6372615eecc862325e88ebb9b189d380459163c7 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -8754,6 +8754,17 @@ void RenderFrameHostImpl::EnterFullscreen( From b518e4ad364fbae823a7ee20d7bf179fc8951124 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:25:15 -0400 Subject: [PATCH 136/339] fix: re-enable OOP printing on Linux (#46585) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/chromium/printing.patch | 16 ++++++++++++++++ shell/browser/feature_list.cc | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index bd6e2546cd567..c28f38927c2be 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -932,3 +932,19 @@ index 63f170c95050416c595e62f4c460c4cd6b7dbd1c..157e3d046889f9c63fdf0fd5d503890f // Does bookkeeping when an error occurs. virtual mojom::ResultCode OnError(); +diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc +index fa55d2a1c5f4207cb8eda9305e8bf2af29352c91..13ca65c2c7c3c24882061d8e224bf93899d9a7ab 100644 +--- a/printing/printing_context_linux.cc ++++ b/printing/printing_context_linux.cc +@@ -52,9 +52,8 @@ void PrintingContextLinux::AskUserForSettings(int max_pages, + bool is_scripted, + PrintSettingsCallback callback) { + if (!print_dialog_) { +- // Can only get here if the renderer is sending bad messages. +- // http://crbug.com/341777 +- NOTREACHED(); ++ print_dialog_ = ui::LinuxUi::instance()->CreatePrintDialog(this); ++ print_dialog_->UseDefaultSettings(); + } + + print_dialog_->ShowDialog(delegate_->GetParentView(), has_selection, diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index e6249b383fda2..676a2fae9e0d5 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -61,14 +61,6 @@ void InitializeFeatureList() { std::string(",") + features::kMacWebContentsOcclusion.name; #endif -#if BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_PRINTING) - disable_features += - // EnableOopPrintDrivers is still a bit half-baked on Linux and - // causes crashes when trying to show dialogs. - // TODO(codebytere): figure out how to re-enable this with our patches. - std::string(",") + printing::features::kEnableOopPrintDrivers.name; -#endif - #if BUILDFLAG(ENABLE_PDF_VIEWER) // Enable window.showSaveFilePicker api for saving pdf files. // Refs https://issues.chromium.org/issues/373852607 From a2ee5547e08204ac5b9abdfa893eb92889c840a5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:29:32 -0400 Subject: [PATCH 137/339] fix: handle potential missing close event property (#46621) fix: handle missing close event property Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- lib/browser/api/browser-window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/browser-window.ts b/lib/browser/api/browser-window.ts index 46882f151474b..909b442c3d27c 100644 --- a/lib/browser/api/browser-window.ts +++ b/lib/browser/api/browser-window.ts @@ -54,7 +54,7 @@ BrowserWindow.prototype._init = function (this: BWT) { }); this.on('close', (event) => { queueMicrotask(() => { - if (!unresponsiveEvent && !event.defaultPrevented) { + if (!unresponsiveEvent && !event?.defaultPrevented) { unresponsiveEvent = setTimeout(emitUnresponsiveEvent, 5000); } }); From 285d347b5149e70e2ec050fd42098e4b7d590406 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:50:06 -0500 Subject: [PATCH 138/339] refactor: use default printing path when no user options (#46617) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- lib/browser/api/web-contents.ts | 24 +++++++++---------- .../browser/api/electron_api_web_contents.cc | 14 +++++++++-- shell/common/gin_helper/dictionary.h | 10 ++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index a81dd7d5ae7e4..14879f3fdd7d5 100644 --- a/lib/browser/api/web-contents.ts +++ b/lib/browser/api/web-contents.ts @@ -263,8 +263,17 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri throw new TypeError('webContents.print(): Invalid print settings specified.'); } - const pageSize = options.pageSize ?? 'A4'; - if (typeof pageSize === 'object') { + const { pageSize } = options; + if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) { + const mediaSize = PDFPageSizes[pageSize]; + options.mediaSize = { + ...mediaSize, + imageable_area_left_microns: 0, + imageable_area_bottom_microns: 0, + imageable_area_right_microns: mediaSize.width_microns, + imageable_area_top_microns: mediaSize.height_microns + }; + } else if (typeof pageSize === 'object') { if (!pageSize.height || !pageSize.width) { throw new Error('height and width properties are required for pageSize'); } @@ -286,16 +295,7 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri imageable_area_right_microns: width, imageable_area_top_microns: height }; - } else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) { - const mediaSize = PDFPageSizes[pageSize]; - options.mediaSize = { - ...mediaSize, - imageable_area_left_microns: 0, - imageable_area_bottom_microns: 0, - imageable_area_right_microns: mediaSize.width_microns, - imageable_area_top_microns: mediaSize.height_microns - }; - } else { + } else if (pageSize !== undefined) { throw new Error(`Unsupported pageSize: ${pageSize}`); } diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 349eb5d1f7ab0..0779c4919b29a 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2944,9 +2944,8 @@ void OnGetDeviceNameToUse(base::WeakPtr web_contents, return; } - // If the user has passed a deviceName use it, otherwise use default printer. + // Use user-passed deviceName, otherwise default printer. print_settings.Set(printing::kSettingDeviceName, info.second); - if (!print_settings.FindInt(printing::kSettingDpiHorizontal)) { gfx::Size dpi = GetDefaultPrinterDPI(info.second); print_settings.Set(printing::kSettingDpiHorizontal, dpi.width()); @@ -3005,6 +3004,17 @@ void WebContents::Print(gin::Arguments* args) { return; } + if (options.IsEmptyObject()) { + auto* print_view_manager = + PrintViewManagerElectron::FromWebContents(web_contents()); + if (!print_view_manager) + return; + + content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents()); + print_view_manager->PrintNow(rfh, std::move(settings), std::move(callback)); + return; + } + // Set optional silent printing. bool silent = false; options.Get("silent", &silent); diff --git a/shell/common/gin_helper/dictionary.h b/shell/common/gin_helper/dictionary.h index e51ddd623a46a..589c24d1f7346 100644 --- a/shell/common/gin_helper/dictionary.h +++ b/shell/common/gin_helper/dictionary.h @@ -183,6 +183,16 @@ class Dictionary : public gin::Dictionary { bool IsEmpty() const { return isolate() == nullptr || GetHandle().IsEmpty(); } + bool IsEmptyObject() const { + if (IsEmpty()) + return true; + + v8::Local context = isolate()->GetCurrentContext(); + v8::Local props = + GetHandle()->GetOwnPropertyNames(context).ToLocalChecked(); + return props->Length() == 0; + } + v8::Local GetHandle() const { return gin::ConvertToV8(isolate(), *static_cast(this)) From 53633fcaeb9c9d14177540b7eaf772e9df9941ee Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:24:01 +0200 Subject: [PATCH 139/339] fix: fallback to old MacOS context menu behavior if no frame is present (#46619) * fix: fallback to old MacOS context menu behavior if no frame is present Co-authored-by: georgexu99 Co-authored-by: erickzhao Co-authored-by: clavin Co-authored-by: Keeley Hammond * docs: add additional option for focusedFrame Co-authored-by: Keeley Hammond * fix: handle frame found, but no view update from rfh Co-authored-by: Keeley Hammond * fix: fix conditional Co-authored-by: Calvin Co-authored-by: Keeley Hammond --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond Co-authored-by: Keeley Hammond --- docs/api/menu.md | 2 +- shell/browser/api/electron_api_menu_mac.mm | 37 ++++++++++++++++------ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/docs/api/menu.md b/docs/api/menu.md index 949720dc0e8c8..fef600f68dd62 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -74,7 +74,7 @@ The `menu` object has the following instance methods: * `options` Object (optional) * `window` [BaseWindow](base-window.md) (optional) - Default is the focused window. * `frame` [WebFrameMain](web-frame-main.md) (optional) - Provide the relevant frame - if you want certain OS-level features such as Writing Tools on macOS to function correctly. Typically, this should be `params.frame` from the [`context-menu` event](web-contents.md#event-context-menu) on a WebContents. + if you want certain OS-level features such as Writing Tools on macOS to function correctly. Typically, this should be `params.frame` from the [`context-menu` event](web-contents.md#event-context-menu) on a WebContents, or the [`focusedFrame` property](web-contents.md#contentsfocusedframe-readonly) of a WebContents. * `x` number (optional) - Default is the current mouse cursor position. Must be declared if `y` is declared. * `y` number (optional) - Default is the current mouse cursor position. diff --git a/shell/browser/api/electron_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm index f5dffd4492e2b..76a34b1a6a7f8 100644 --- a/shell/browser/api/electron_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -164,19 +164,36 @@ static_cast(rfh->GetView()); RenderWidgetHostViewCocoa* cocoa_view = rwhvm->GetInProcessNSView(); view = cocoa_view; + + // TODO: ui::ShowContextMenu does not dispatch the event correctly + // if no frame is found. Fix this to remove if/else condition. + NSEvent* dummy_event = + [NSEvent mouseEventWithType:NSEventTypeRightMouseDown + location:position + modifierFlags:0 + timestamp:0 + windowNumber:nswindow.windowNumber + context:nil + eventNumber:0 + clickCount:1 + pressure:0]; + ui::ShowContextMenu(menu, dummy_event, view, true); + return; } } - NSEvent* dummy_event = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown - location:position - modifierFlags:0 - timestamp:0 - windowNumber:nswindow.windowNumber - context:nil - eventNumber:0 - clickCount:1 - pressure:0]; - ui::ShowContextMenu(menu, dummy_event, view, true); + // Make sure events can be pumped while the menu is up. + base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; + + // One of the events that could be pumped is |window.close()|. + // User-initiated event-tracking loops protect against this by + // setting flags in -[CrApplication sendEvent:], but since + // web-content menus are initiated by IPC message the setup has to + // be done manually. + base::mac::ScopedSendingEvent sendingEventScoper; + + // Don't emit unresponsive event when showing menu. + [menu popUpMenuPositioningItem:item atLocation:position inView:view]; } void MenuMac::ClosePopupAt(int32_t window_id) { From e825107f8afea322f5af80caa57f8743c90025bf Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 10:44:18 +0200 Subject: [PATCH 140/339] build: roll build-images (#46634) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .devcontainer/docker-compose.yml | 2 +- .github/workflows/build.yml | 4 ++-- .github/workflows/linux-publish.yml | 2 +- .github/workflows/macos-publish.yml | 2 +- .github/workflows/windows-publish.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index b422e8a938ed7..4eaf46700b472 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: buildtools: - image: ghcr.io/electron/devcontainer:9f11982e806f439d0a0a8ebbbf566cd5e0d9e952 + image: ghcr.io/electron/devcontainer:424eedbf277ad9749ffa9219068aa72ed4a5e373 volumes: - ..:/workspaces/gclient/src/electron:cached diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9d393575956c..bf578ebe26100 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true skip-macos: type: boolean @@ -64,7 +64,7 @@ jobs: id: set-output run: | if [ -z "${{ inputs.build-image-sha }}" ]; then - echo "build-image-sha=9f11982e806f439d0a0a8ebbbf566cd5e0d9e952" >> "$GITHUB_OUTPUT" + echo "build-image-sha=424eedbf277ad9749ffa9219068aa72ed4a5e373" >> "$GITHUB_OUTPUT" else echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT" fi diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index 0d5a2a2349521..8cadd26d23bcc 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' upload-to-storage: description: 'Uploads to Azure storage' required: false diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index 1620305893dfa..c7241b6a3bb00 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true upload-to-storage: description: 'Uploads to Azure storage' diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index d4fd36139fd74..e8b7c6172fdd8 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -6,7 +6,7 @@ on: build-image-sha: type: string description: 'SHA for electron/build image' - default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952' + default: '424eedbf277ad9749ffa9219068aa72ed4a5e373' required: true upload-to-storage: description: 'Uploads to Azure storage' From 339e0040ef99262e67a43058f7391921d73f825f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 12:56:20 +0200 Subject: [PATCH 141/339] fix: paint and flash issues on macOS (#46629) --- patches/chromium/.patches | 2 +- ...less_mode_handling_in_native_widget.patch} | 23 +++++++++++++++-- .../api/electron_api_browser_window.cc | 2 +- shell/browser/native_window_mac.mm | 7 ++++-- shell/common/options_switches.h | 4 +++ spec/api-browser-window-spec.ts | 25 +++++++++++++++++++ 6 files changed, 57 insertions(+), 6 deletions(-) rename patches/chromium/{fix_add_method_which_disables_headless_mode_on_native_widget.patch => fix_adjust_headless_mode_handling_in_native_widget.patch} (51%) diff --git a/patches/chromium/.patches b/patches/chromium/.patches index d982f77f50033..db2f7faefb4b7 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -131,7 +131,7 @@ osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch chore_partial_revert_of.patch fix_software_compositing_infinite_loop.patch -fix_add_method_which_disables_headless_mode_on_native_widget.patch +fix_adjust_headless_mode_handling_in_native_widget.patch refactor_unfilter_unresponsive_events.patch build_disable_thin_lto_mac.patch feat_corner_smoothing_css_rule_and_blink_painting.patch diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch similarity index 51% rename from patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch rename to patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch index e36a893d7b0f6..89ff145669506 100644 --- a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch +++ b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Cezary Kulakowski Date: Mon, 22 Jul 2024 16:23:13 +0200 -Subject: fix: add method which disables headless mode on native widget +Subject: fix: adjust headless mode handling in native widget We need this method as we create window in headless mode and we switch it back to normal mode only after inital paint is done in @@ -9,8 +9,27 @@ order to get some events like WebContents.beginFrameSubscription. If we don't set `is_headless_` to false then some child windows e.g. autofill popups will be created in headless mode leading to ui problems (like dissapearing popup during typing in html's -input list. +input list). +We also need to ensure that an initial paint is scheduled when +the compositor is unsuspended in headles mode. + +diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +index 7bda1853d47034c80a4e416b9839e8d18c5a8e2c..e5df49253103c44f01195fa93988753fb163d13e 100644 +--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm ++++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +@@ -659,9 +659,10 @@ void HandleAccelerator(const ui::Accelerator& accelerator, + // case it will never become visible but we want its compositor to produce + // frames for screenshooting and screencasting. + UpdateCompositorProperties(); +- layer()->SetVisible(is_visible_); ++ layer()->SetVisible(is_visible_ || is_headless_mode_window_); + if (is_visible_ || is_headless_mode_window_) { + compositor_->Unsuspend(); ++ layer()->SchedulePaint(layer()->bounds()); + } + + // Register the CGWindowID (used to identify this window for video capture) diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index 4d1c7d95c338b247f7c63315d536fcc7a7e4b637..55bb9b36f8d0894c972c05ba162fd5b41048013d 100644 --- a/ui/views/widget/widget.h diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 8d92652b033e6..742ca07976da6 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -47,7 +47,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args, // Copy the show setting to webContents, but only if we don't want to paint // when initially hidden bool paint_when_initially_hidden = true; - options.Get("paintWhenInitiallyHidden", &paint_when_initially_hidden); + options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden); if (!paint_when_initially_hidden) { bool show = true; options.Get(options::kShow, &show); diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index f021e6cc742cc..8bb7d5b51fa43 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -154,6 +154,9 @@ static bool FromV8(v8::Isolate* isolate, bool hiddenInMissionControl = false; options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl); + bool paint_when_initially_hidden = true; + options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden); + // The window without titlebar is treated the same with frameless window. if (title_bar_style_ != TitleBarStyle::kNormal) set_has_frame(false); @@ -194,8 +197,8 @@ static bool FromV8(v8::Isolate* isolate, params.bounds = bounds; params.delegate = this; params.type = views::Widget::InitParams::TYPE_WINDOW; - // Allow painting before shown, to be later disabled in ElectronNSWindow. - params.headless_mode = true; + // Possibly allow painting before shown - later disabled in ElectronNSWindow. + params.headless_mode = paint_when_initially_hidden; if (IsTranslucent()) { params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; } diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 6d0895d4cbb36..5bb2284acb4e6 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -33,6 +33,10 @@ inline constexpr std::string_view kMaximizable = "maximizable"; inline constexpr std::string_view kFullScreenable = "fullscreenable"; inline constexpr std::string_view kClosable = "closable"; +// Whether to paint when the window is initially hidden. +inline constexpr std::string_view kPaintWhenInitiallyHidden = + "paintWhenInitiallyHidden"; + // whether to keep the window out of mission control inline constexpr std::string_view kHiddenInMissionControl = "hiddenInMissionControl"; diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 2e6c7a7995507..f90078f9a391f 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -6475,6 +6475,31 @@ describe('BrowserWindow module', () => { w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html')); }); + // TODO(codebytere): fix on Windows and Linux too + ifdescribe(process.platform === 'darwin')('window.webContents initial paint', () => { + afterEach(closeAllWindows); + it('paints when a window is initially hidden', async () => { + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixtures, 'pages', 'a.html')); + + const entries = await w.webContents.executeJavaScript(` + new Promise((resolve) => { + const observer = new PerformanceObserver((performance) => { + observer.disconnect(); + resolve(performance.getEntries()); + }); + observer.observe({ entryTypes: ['paint'] }); + }); + + const header = document.createElement('h1'); + header.innerText = 'Paint me!!'; + document.getElementById('div').appendChild(header); + `); + + expect(JSON.stringify(entries)).to.eq('{}'); + }); + }); + describe('window.webContents.focus()', () => { afterEach(closeAllWindows); it('focuses window', async () => { From c785b4070359f8c453a8e741c09c09f7e6d35284 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:33:23 +0200 Subject: [PATCH 142/339] fix: window border on Gnome Wayland (#46642) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/electron_desktop_window_tree_host_linux.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc index c58e7726e903f..7e8f06c2b1ce8 100644 --- a/shell/browser/ui/electron_desktop_window_tree_host_linux.cc +++ b/shell/browser/ui/electron_desktop_window_tree_host_linux.cc @@ -195,7 +195,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() { if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) { // Set the opaque region. std::vector opaque_region; - if (!IsShowingFrame()) { + if (IsShowingFrame()) { // The opaque region is a list of rectangles that contain only fully // opaque pixels of the window. We need to convert the clipping // rounded-rect into this format. From f8a55100cc1427e230cf4c38f729fd7f291dbf05 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 17:01:53 -0400 Subject: [PATCH 143/339] refactor: migrate to non-deprecated `allowedContentTypes` on macOS (#46646) refactor: migrated to non-deprecated allowedContentTypes on macOS Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/file_dialog_mac.mm | 71 +++++++++++++++++------------ 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index bd6d15ce5adbd..99c46738256cc 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -11,6 +11,7 @@ #import #import +#import #include "base/apple/foundation_util.h" #include "base/apple/scoped_cftyperef.h" @@ -29,7 +30,7 @@ @interface PopUpButtonHandler : NSObject @property(nonatomic, assign) NSSavePanel* savePanel; -@property(nonatomic, strong) NSArray* fileTypesList; +@property(nonatomic, strong) NSArray* contentTypesList; - (instancetype)initWithPanel:(NSSavePanel*)panel andTypesList:(NSArray*)typesList; @@ -40,14 +41,14 @@ - (void)selectFormat:(id)sender; @implementation PopUpButtonHandler @synthesize savePanel; -@synthesize fileTypesList; +@synthesize contentTypesList; - (instancetype)initWithPanel:(NSSavePanel*)panel andTypesList:(NSArray*)typesList { self = [super init]; if (self) { [self setSavePanel:panel]; - [self setFileTypesList:typesList]; + [self setContentTypesList:typesList]; } return self; } @@ -55,15 +56,19 @@ - (instancetype)initWithPanel:(NSSavePanel*)panel - (void)selectFormat:(id)sender { NSPopUpButton* button = (NSPopUpButton*)sender; NSInteger selectedItemIndex = [button indexOfSelectedItem]; - NSArray* list = [self fileTypesList]; - NSArray* fileTypes = [list objectAtIndex:selectedItemIndex]; - - // If we meet a '*' file extension, we allow all the file types and no - // need to set the specified file types. - if ([fileTypes count] == 0 || [fileTypes containsObject:@"*"]) - [[self savePanel] setAllowedFileTypes:nil]; - else - [[self savePanel] setAllowedFileTypes:fileTypes]; + NSArray* list = [self contentTypesList]; + NSArray* content_types = [list objectAtIndex:selectedItemIndex]; + + __block BOOL allowAllFiles = NO; + [content_types + enumerateObjectsUsingBlock:^(UTType* type, NSUInteger idx, BOOL* stop) { + if ([[type preferredFilenameExtension] isEqual:@"*"]) { + allowAllFiles = YES; + *stop = YES; + } + }]; + + [[self savePanel] setAllowedContentTypes:allowAllFiles ? @[] : content_types]; } @end @@ -100,9 +105,10 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { // Create array to keep file types and their name. for (const Filter& filter : filters) { - NSMutableOrderedSet* file_type_set = + NSMutableOrderedSet* content_types_set = [NSMutableOrderedSet orderedSetWithCapacity:filters.size()]; [filter_names addObject:@(filter.first.c_str())]; + for (std::string ext : filter.second) { // macOS is incapable of understanding multiple file extensions, // so we need to tokenize the extension that's been passed in. @@ -113,25 +119,34 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { ext.erase(0, pos + 1); } - [file_type_set addObject:@(ext.c_str())]; + if (ext == "*") { + [content_types_set addObject:[UTType typeWithFilenameExtension:@"*"]]; + break; + } else { + if (UTType* utt = [UTType typeWithFilenameExtension:@(ext.c_str())]) + [content_types_set addObject:utt]; + } } - [file_types_list addObject:[file_type_set array]]; - } - // Passing empty array to setAllowedFileTypes will cause exception. - NSArray* file_types = nil; - NSUInteger count = [file_types_list count]; - if (count > 0) { - file_types = [[file_types_list objectAtIndex:0] allObjects]; - // If we meet a '*' file extension, we allow all the file types and no - // need to set the specified file types. - if ([file_types count] == 0 || [file_types containsObject:@"*"]) - file_types = nil; + [file_types_list addObject:content_types_set]; } - [dialog setAllowedFileTypes:file_types]; - if (count <= 1) - return; // don't add file format picker + // Don't add file format picker. + if ([file_types_list count] <= 1) + return; + + NSArray* content_types = [file_types_list objectAtIndex:0]; + + __block BOOL allowAllFiles = NO; + [content_types + enumerateObjectsUsingBlock:^(UTType* type, NSUInteger idx, BOOL* stop) { + if ([[type preferredFilenameExtension] isEqual:@"*"]) { + allowAllFiles = YES; + *stop = YES; + } + }]; + + [dialog setAllowedContentTypes:allowAllFiles ? @[] : content_types]; // Add file format picker. ElectronAccessoryView* accessoryView = [[ElectronAccessoryView alloc] From 7b66361ca8f14a5008aa4a327305d977ccf817f1 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:40:55 +0200 Subject: [PATCH 144/339] feat: expose `nativeTheme.shouldUseDarkColorsForSystemIntegratedUI` (#46598) feat: expose shouldUseDarkColorsForSystemIntegratedUI Closes https://github.com/electron/electron/issues/46429. Refs https://github.com/electron/electron/pull/19735. This PR adds a new API `shouldUseDarkColorsForSystemIntegratedUI` to the `nativeTheme` module. This API returns a boolean indicating whether the system is using dark colors for system integrated UI elements. This is useful for applications that want to adapt their UI to match the system theme, especially for those that use system integrated UI elements like the shell theme or taskbar appearance. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/native-theme.md | 8 ++++++++ shell/browser/api/electron_api_native_theme.cc | 6 ++++++ shell/browser/api/electron_api_native_theme.h | 1 + spec/api-native-theme-spec.ts | 6 ++++++ 4 files changed, 21 insertions(+) diff --git a/docs/api/native-theme.md b/docs/api/native-theme.md index e6bdd6c806067..7860c870307b4 100644 --- a/docs/api/native-theme.md +++ b/docs/api/native-theme.md @@ -63,6 +63,14 @@ Your application should then always use `shouldUseDarkColors` to determine what A `boolean` for if the OS / Chromium currently has high-contrast mode enabled or is being instructed to show a high-contrast UI. +### `nativeTheme.shouldUseDarkColorsForSystemIntegratedUI` _macOS_ _Windows_ _Readonly_ + +A `boolean` property indicating whether or not the system theme has been set to dark or light. + +On Windows this property distinguishes between system and app light/dark theme, returning +`true` if the system theme is set to dark theme and `false` otherwise. On macOS the return +value will be the same as `nativeTheme.shouldUseDarkColors`. + ### `nativeTheme.shouldUseInvertedColorScheme` _macOS_ _Windows_ _Readonly_ A `boolean` for if the OS / Chromium currently has an inverted color scheme diff --git a/shell/browser/api/electron_api_native_theme.cc b/shell/browser/api/electron_api_native_theme.cc index a86481e4d67a5..01e470c227c22 100644 --- a/shell/browser/api/electron_api_native_theme.cc +++ b/shell/browser/api/electron_api_native_theme.cc @@ -63,6 +63,10 @@ bool NativeTheme::ShouldUseHighContrastColors() { return ui_theme_->UserHasContrastPreference(); } +bool NativeTheme::ShouldUseDarkColorsForSystemIntegratedUI() { + return ui_theme_->ShouldUseDarkColorsForSystemIntegratedUI(); +} + bool NativeTheme::InForcedColorsMode() { return ui_theme_->InForcedColorsMode(); } @@ -109,6 +113,8 @@ gin::ObjectTemplateBuilder NativeTheme::GetObjectTemplateBuilder( &NativeTheme::SetThemeSource) .SetProperty("shouldUseHighContrastColors", &NativeTheme::ShouldUseHighContrastColors) + .SetProperty("shouldUseDarkColorsForSystemIntegratedUI", + &NativeTheme::ShouldUseDarkColorsForSystemIntegratedUI) .SetProperty("shouldUseInvertedColorScheme", &NativeTheme::ShouldUseInvertedColorScheme) .SetProperty("inForcedColorsMode", &NativeTheme::InForcedColorsMode) diff --git a/shell/browser/api/electron_api_native_theme.h b/shell/browser/api/electron_api_native_theme.h index d94f148ec5315..86cc3412d3852 100644 --- a/shell/browser/api/electron_api_native_theme.h +++ b/shell/browser/api/electron_api_native_theme.h @@ -48,6 +48,7 @@ class NativeTheme final : public gin::Wrappable, ui::NativeTheme::ThemeSource GetThemeSource() const; bool ShouldUseDarkColors(); bool ShouldUseHighContrastColors(); + bool ShouldUseDarkColorsForSystemIntegratedUI(); bool ShouldUseInvertedColorScheme(); bool InForcedColorsMode(); bool GetPrefersReducedTransparency(); diff --git a/spec/api-native-theme-spec.ts b/spec/api-native-theme-spec.ts index 2d2a3a3b00d6a..d68caf06fb81d 100644 --- a/spec/api-native-theme-spec.ts +++ b/spec/api-native-theme-spec.ts @@ -102,6 +102,12 @@ describe('nativeTheme module', () => { }); }); + describe('nativeTheme.shouldUseDarkColorsForSystemIntegratedUI', () => { + it('returns a boolean', () => { + expect(nativeTheme.shouldUseDarkColorsForSystemIntegratedUI).to.be.a('boolean'); + }); + }); + describe('nativeTheme.inForcedColorsMode', () => { it('returns a boolean', () => { expect(nativeTheme.inForcedColorsMode).to.be.a('boolean'); From 301f7b4e6467ff15908fe646bc4c1c25da80a792 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:23:23 +0200 Subject: [PATCH 145/339] fix: `postMessage` crash with invalid transferrable (#46667) * fix: postMessage crash with invalid transferrable Co-authored-by: Shelley Vohr * chore: address review feedback Co-authored-by: Charles Kerr Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../api/electron_api_utility_process.cc | 23 ++++++++++- shell/browser/api/message_port.cc | 39 +++++-------------- shell/common/gin_helper/wrappable.cc | 17 ++++++++ shell/common/gin_helper/wrappable.h | 3 ++ spec/api-ipc-spec.ts | 31 +++++++++++++++ 5 files changed, 82 insertions(+), 31 deletions(-) diff --git a/shell/browser/api/electron_api_utility_process.cc b/shell/browser/api/electron_api_utility_process.cc index 573d38448e164..d9ff0996d91d8 100644 --- a/shell/browser/api/electron_api_utility_process.cc +++ b/shell/browser/api/electron_api_utility_process.cc @@ -330,6 +330,9 @@ void UtilityProcessWrapper::PostMessage(gin::Arguments* args) { return; blink::TransferableMessage transferable_message; + gin_helper::ErrorThrower thrower(args->isolate()); + + // |message| is any value that can be serialized to StructuredClone. v8::Local message_value; if (args->GetNext(&message_value)) { if (!electron::SerializeV8Value(args->isolate(), message_value, @@ -342,9 +345,25 @@ void UtilityProcessWrapper::PostMessage(gin::Arguments* args) { v8::Local transferables; std::vector> wrapped_ports; if (args->GetNext(&transferables)) { + std::vector> wrapped_port_values; + if (!gin::ConvertFromV8(args->isolate(), transferables, + &wrapped_port_values)) { + thrower.ThrowTypeError("transferables must be an array of MessagePorts"); + return; + } + + for (size_t i = 0; i < wrapped_port_values.size(); ++i) { + if (!gin_helper::IsValidWrappable(wrapped_port_values[i], + &MessagePort::kWrapperInfo)) { + thrower.ThrowTypeError( + base::StrCat({"Port at index ", base::NumberToString(i), + " is not a valid port"})); + return; + } + } + if (!gin::ConvertFromV8(args->isolate(), transferables, &wrapped_ports)) { - gin_helper::ErrorThrower(args->isolate()) - .ThrowTypeError("Invalid value for transfer"); + thrower.ThrowTypeError("Passed an invalid MessagePort"); return; } } diff --git a/shell/browser/api/message_port.cc b/shell/browser/api/message_port.cc index 7d48119484cb2..4ea7f19e8dd3a 100644 --- a/shell/browser/api/message_port.cc +++ b/shell/browser/api/message_port.cc @@ -17,6 +17,7 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/error_thrower.h" #include "shell/common/gin_helper/event_emitter_caller.h" +#include "shell/common/gin_helper/wrappable.h" #include "shell/common/node_includes.h" #include "shell/common/v8_util.h" #include "third_party/abseil-cpp/absl/container/flat_hash_set.h" @@ -26,25 +27,6 @@ namespace electron { -namespace { - -bool IsValidWrappable(const v8::Local& val) { - if (!val->IsObject()) - return false; - - v8::Local port = val.As(); - - if (port->InternalFieldCount() != gin::kNumberOfInternalFields) - return false; - - const auto* info = static_cast( - port->GetAlignedPointerFromInternalField(gin::kWrapperInfoIndex)); - - return info && info->embedder == gin::kEmbedderNativeGin; -} - -} // namespace - gin::WrapperInfo MessagePort::kWrapperInfo = {gin::kEmbedderNativeGin}; MessagePort::MessagePort() = default; @@ -77,16 +59,14 @@ void MessagePort::PostMessage(gin::Arguments* args) { blink::TransferableMessage transferable_message; gin_helper::ErrorThrower thrower(args->isolate()); + // |message| is any value that can be serialized to StructuredClone. v8::Local message_value; - if (!args->GetNext(&message_value)) { - thrower.ThrowTypeError("Expected at least one argument to postMessage"); - return; - } - - if (!electron::SerializeV8Value(args->isolate(), message_value, - &transferable_message)) { - // SerializeV8Value sets an exception. - return; + if (args->GetNext(&message_value)) { + if (!electron::SerializeV8Value(args->isolate(), message_value, + &transferable_message)) { + // SerializeV8Value sets an exception. + return; + } } v8::Local transferables; @@ -100,7 +80,8 @@ void MessagePort::PostMessage(gin::Arguments* args) { } for (unsigned i = 0; i < wrapped_port_values.size(); ++i) { - if (!IsValidWrappable(wrapped_port_values[i])) { + if (!gin_helper::IsValidWrappable(wrapped_port_values[i], + &MessagePort::kWrapperInfo)) { thrower.ThrowTypeError("Port at index " + base::NumberToString(i) + " is not a valid port"); return; diff --git a/shell/common/gin_helper/wrappable.cc b/shell/common/gin_helper/wrappable.cc index 1d0f45e004e48..5b4a0369441cc 100644 --- a/shell/common/gin_helper/wrappable.cc +++ b/shell/common/gin_helper/wrappable.cc @@ -10,6 +10,23 @@ namespace gin_helper { +bool IsValidWrappable(const v8::Local& val, + const gin::WrapperInfo* wrapper_info) { + if (!val->IsObject()) + return false; + + v8::Local port = val.As(); + if (port->InternalFieldCount() != gin::kNumberOfInternalFields) + return false; + + const gin::WrapperInfo* info = static_cast( + port->GetAlignedPointerFromInternalField(gin::kWrapperInfoIndex)); + if (info != wrapper_info) + return false; + + return true; +} + WrappableBase::WrappableBase() = default; WrappableBase::~WrappableBase() { diff --git a/shell/common/gin_helper/wrappable.h b/shell/common/gin_helper/wrappable.h index 531d446f7673d..76c0faeeebf00 100644 --- a/shell/common/gin_helper/wrappable.h +++ b/shell/common/gin_helper/wrappable.h @@ -11,6 +11,9 @@ namespace gin_helper { +bool IsValidWrappable(const v8::Local& obj, + const gin::WrapperInfo* wrapper_info); + namespace internal { void* FromV8Impl(v8::Isolate* isolate, v8::Local val); diff --git a/spec/api-ipc-spec.ts b/spec/api-ipc-spec.ts index 88eaaf4a0b307..ceda0926e261e 100644 --- a/spec/api-ipc-spec.ts +++ b/spec/api-ipc-spec.ts @@ -236,6 +236,23 @@ describe('ipc module', () => { expect(ev.senderFrame.routingId).to.equal(w.webContents.mainFrame.routingId); }); + it('throws when the transferable is invalid', async () => { + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); + w.loadURL('about:blank'); + const p = once(ipcMain, 'port'); + await w.webContents.executeJavaScript(`(${function () { + try { + const buffer = new ArrayBuffer(10); + // @ts-expect-error + require('electron').ipcRenderer.postMessage('port', '', [buffer]); + } catch (e) { + require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message }); + } + }})()`); + const [, msg] = await p; + expect(msg.error).to.eql('Invalid value for transfer'); + }); + it('can communicate between main and renderer', async () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); w.loadURL('about:blank'); @@ -411,6 +428,20 @@ describe('ipc module', () => { expect(port2).not.to.be.null(); }); + it('should not throw when supported values are passed as message', () => { + const { port1 } = new MessageChannelMain(); + + // @ts-expect-error - this shouldn't crash. + expect(() => { port1.postMessage(); }).to.not.throw(); + + expect(() => { port1.postMessage(undefined); }).to.not.throw(); + expect(() => { port1.postMessage(42); }).to.not.throw(); + expect(() => { port1.postMessage(false); }).to.not.throw(); + expect(() => { port1.postMessage([]); }).to.not.throw(); + expect(() => { port1.postMessage('hello'); }).to.not.throw(); + expect(() => { port1.postMessage({ hello: 'goodbye' }); }).to.not.throw(); + }); + it('throws an error when an invalid parameter is sent to postMessage', () => { const { port1 } = new MessageChannelMain(); From ec75ddd71542880f4633fba6bf4733a5388bdd40 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:23:34 +0200 Subject: [PATCH 146/339] build: update build tools (#46663) * build: update build tools Co-authored-by: John Kleinschmidt * chore: fix core.fscache Co-authored-by: Shelley Vohr * chore: fix core.preloadindex Co-authored-by: John Kleinschmidt --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt Co-authored-by: Shelley Vohr --- .github/actions/install-build-tools/action.yml | 4 +++- .github/workflows/pipeline-segment-electron-test.yml | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index 33cb28cfc8aa5..a897b6480e886 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -10,8 +10,10 @@ runs: git config --global core.filemode false git config --global core.autocrlf false git config --global branch.autosetuprebase always + git config --global core.fscache true + git config --global core.preloadindex true fi - export BUILD_TOOLS_SHA=8246e57791b0af4ae5975eb96f09855f9269b1cd + export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c npm i -g @electron/build-tools e auto-update disable e d auto-update disable diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 087e7af3460e6..060f9fbf45fb7 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -134,6 +134,8 @@ jobs: git config --global core.filemode false git config --global core.autocrlf false git config --global branch.autosetuprebase always + git config --global core.fscache true + git config --global core.preloadindex true git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git # Ensure depot_tools does not update. test -d depot_tools && cd depot_tools From 361753f61f1e1581b2f59123905cd63a7155fdea Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 20:01:25 -0400 Subject: [PATCH 147/339] chore: bump chromium to 136.0.7103.33 (36-x-y) (#46665) * chore: bump chromium in DEPS to 136.0.7103.33 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- ...build_do_not_depend_on_packed_resource_integrity.patch | 2 +- ...x_adjust_headless_mode_handling_in_native_widget.patch | 2 +- .../mas_avoid_private_macos_api_usage.patch.patch | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DEPS b/DEPS index 84c9d9f31b22d..31fd17451674e 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.25', + '136.0.7103.33', 'node_version': 'v22.14.0', 'nan_version': diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index 5373ec0788055..ccd60b62c4c0b 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -33,7 +33,7 @@ index 95c73dcb082999d0a85d82f1fe330d27c88055ca..64cd976eba849435779b280b2941f915 "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 2af2794b353d89f01d84b6ba28201f025ae124c7..b656ba6241b6a61d772219d1e80fb6b601aba224 100644 +index a3623400c3548b31e3e9bc8c15e6c5081e4990cc..db7e78e4b72de2af313741bb802b51f97b187357 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -4593,7 +4593,7 @@ static_library("browser") { diff --git a/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch index 89ff145669506..5428d36fdc299 100644 --- a/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch +++ b/patches/chromium/fix_adjust_headless_mode_handling_in_native_widget.patch @@ -31,7 +31,7 @@ index 7bda1853d47034c80a4e416b9839e8d18c5a8e2c..e5df49253103c44f01195fa93988753f // Register the CGWindowID (used to identify this window for video capture) diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h -index 4d1c7d95c338b247f7c63315d536fcc7a7e4b637..55bb9b36f8d0894c972c05ba162fd5b41048013d 100644 +index 44c9248daecd67ccbc32f9afdbb8e7449d435b6c..e7d43ffc134e76f54aa5a71c668ce725a3f9bf6a 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h @@ -1223,6 +1223,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 80fc10643c7b7..0fcc4487f1b9d 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -35,10 +35,10 @@ system font by checking if it's kCTFontPriorityAttribute is set to system priority. diff --git a/base/BUILD.gn b/base/BUILD.gn -index 50893c5653a42160c25f3f92aeb2164b292c8364..36928057b408d76fa6fc8d0131bf47f860fabe1a 100644 +index 57a6af006161508474e0647d22580e872168ef4e..08205acb929894d87fecaee2c155aef5715cf180 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn -@@ -1040,6 +1040,7 @@ component("base") { +@@ -1041,6 +1041,7 @@ component("base") { "//build:ios_buildflags", "//build/config/compiler:compiler_buildflags", "//third_party/modp_b64", @@ -449,7 +449,7 @@ index dcaa590375f7cc42996a95ceb0804733d382f60b..e80557dfa4992823ffca81e77aaeef1a bool shouldShowWindowTitle = YES; if (_bridge) diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm -index 5c0a53152095c2f799251f78b3c3464504104bfc..2c81ecef35d40036111cbd3dbdb44f7086d8946e 100644 +index 0b964e176e59e77a0743e2d347aa185bc7c80bc5..49a60b9aa0597c3397906303b73a4c1650cc60ce 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm @@ -42,6 +42,7 @@ @@ -460,7 +460,7 @@ index 5c0a53152095c2f799251f78b3c3464504104bfc..2c81ecef35d40036111cbd3dbdb44f70 #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/cert/x509_util_apple.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" -@@ -679,10 +680,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { +@@ -681,10 +682,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { // this should be treated as an error and caught early. CHECK(bridged_view_); From 2960f93ea5f8cb99a3e92cb7759321d33e625e91 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 19:09:54 -0500 Subject: [PATCH 148/339] refactor: remove `WebContentsPermissionHelper::PermissionTypes::KEYBOARD_LOCK` (#46677) refactor: remove electron::WebContentsPermissionHelper::PermissionTypes::KEYBOARD_LOCK This was added in 344aba0. In the time when this PR initially went up and when 344aba0 landed, upstream added blink::PermissionTypes::KEYBOARD_LOCK. Our duplicate copy can be removed. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/web_contents_permission_helper.h | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/browser/web_contents_permission_helper.h b/shell/browser/web_contents_permission_helper.h index 0bf7ea4965216..21a38abadf813 100644 --- a/shell/browser/web_contents_permission_helper.h +++ b/shell/browser/web_contents_permission_helper.h @@ -31,7 +31,6 @@ class WebContentsPermissionHelper SERIAL, HID, USB, - KEYBOARD_LOCK, FILE_SYSTEM, }; From e8188ef271188bc00717b45786766de1cb2bd0cd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:50:40 -0500 Subject: [PATCH 149/339] fix: do not run microtasks in V8Serializer in browser process (#46682) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: clavin --- shell/common/v8_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/common/v8_util.cc b/shell/common/v8_util.cc index 85ed35cf1ae1f..842e2a1cd36ea 100644 --- a/shell/common/v8_util.cc +++ b/shell/common/v8_util.cc @@ -36,7 +36,7 @@ class V8Serializer : public v8::ValueSerializer::Delegate { bool Serialize(v8::Local value, blink::CloneableMessage* out) { gin_helper::MicrotasksScope microtasks_scope{ - isolate_->GetCurrentContext(), false, + isolate_->GetCurrentContext(), true, v8::MicrotasksScope::kDoNotRunMicrotasks}; WriteBlinkEnvelope(19); From 3348ac08b4ccc56caf3a39fd6fde4e18b1ceb1b9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:51:27 -0500 Subject: [PATCH 150/339] perf: avoid triple map lookup in `ElectronHidDelegate::GetContextObserver()` (#46687) perf: avoid triple map lookup in ElectronHidDelegate::GetContextObserver() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/hid/electron_hid_delegate.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shell/browser/hid/electron_hid_delegate.cc b/shell/browser/hid/electron_hid_delegate.cc index 1299da0a544e3..1915d4e489393 100644 --- a/shell/browser/hid/electron_hid_delegate.cc +++ b/shell/browser/hid/electron_hid_delegate.cc @@ -221,11 +221,10 @@ bool ElectronHidDelegate::IsServiceWorkerAllowedForOrigin( ElectronHidDelegate::ContextObservation* ElectronHidDelegate::GetContextObserver( content::BrowserContext* browser_context) { - if (!observations_.contains(browser_context)) { - observations_.emplace(browser_context, std::make_unique( - this, browser_context)); - } - return observations_[browser_context].get(); + auto& observation = observations_[browser_context]; + if (!observation) + observation = std::make_unique(this, browser_context); + return observation.get(); } HidChooserController* ElectronHidDelegate::ControllerForFrame( From fdc6a3b10873d5bd99787434a9cecbc8a8c74b8c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 10:28:20 -0400 Subject: [PATCH 151/339] fix: crash on reconversion with google IME and editcontext on macOS (#46699) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- patches/chromium/.patches | 1 + ...ion_due_to_invalid_replacement_range.patch | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index db2f7faefb4b7..970ba996347a5 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -145,3 +145,4 @@ fix_win32_synchronous_spellcheck.patch chore_remove_conflicting_allow_unsafe_libc_calls.patch fix_linter_error.patch revert_enable_crel_for_arm32_targets.patch +mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch diff --git a/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch new file mode 100644 index 0000000000000..3298782f91e5b --- /dev/null +++ b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Keren Zhu +Date: Fri, 18 Apr 2025 11:02:46 -0700 +Subject: mac: fix CHECK on IME reconversion due to invalid replacement range +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It appears that Google Japanese IME can call -setMarkedText: with an +invalid replacement range when doing text reconversion (変換, i.e., +conversion between different text with same pronunciation). This range +is a NSRange and NSRange.location is supposed to be NSNotFound (2^31-1) +for invalid range, but the IME can pass in 2^32. Subsequently causing +CHECK error. + +This CL fixes the issue by converting such invalid NSRange to +gfx::InvalidRange using FromPossiblyInvalidNSRange(range). + +Fixed: 409864204 +Change-Id: I08ff426a933ef76aa81e33af59aa32e2ac0b674d +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6470915 +Commit-Queue: Keren Zhu +Reviewed-by: Marijn Kruisselbrink +Cr-Commit-Position: refs/heads/main@{#1448935} + +diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +index f2499bc084312a09b2324567d270fc1b899e7617..12ee7e75e437426f28002c7c9f4d5f5b5016ec53 100644 +--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm ++++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +@@ -2415,9 +2415,10 @@ - (void)setMarkedText:(id)string + if ([self isHandlingKeyDown] && !_isReconversionTriggered) { + _setMarkedTextReplacementRange = gfx::Range(replacementRange); + } else { +- _host->ImeSetComposition(_markedText, _imeTextSpans, +- gfx::Range(replacementRange), newSelRange.location, +- NSMaxRange(newSelRange)); ++ _host->ImeSetComposition( ++ _markedText, _imeTextSpans, ++ gfx::Range::FromPossiblyInvalidNSRange(replacementRange), ++ newSelRange.location, NSMaxRange(newSelRange)); + } + + [[self inputContext] invalidateCharacterCoordinates]; From 0e30f5e2a105663dd05eb9ba932c6487ed98ad50 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:16:10 -0500 Subject: [PATCH 152/339] docs: update timelines for E37 (#46705) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> --- docs/tutorial/electron-timelines.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/electron-timelines.md b/docs/tutorial/electron-timelines.md index 7d596fb3baf61..893b7891a0fd4 100644 --- a/docs/tutorial/electron-timelines.md +++ b/docs/tutorial/electron-timelines.md @@ -9,10 +9,11 @@ check out our [Electron Versioning](./electron-versioning.md) doc. | Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported | | ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- | -| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | TBD | ✅ | +| 37.0.0 | 2025-May-01 | 2025-May-28 | 2025-Jun-24 | 2026-Jan-13 | M138 | TBD | ✅ | +| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | v22.14 | ✅ | | 35.0.0 | 2025-Jan-16 | 2025-Feb-05 | 2025-Mar-04 | 2025-Sep-02 | M134 | v22.14 | ✅ | | 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2025-Jan-14 | 2025-Jun-24 | M132 | v20.18 | ✅ | -| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | ✅ | +| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | 🚫 | | 32.0.0 | 2024-Jun-14 | 2024-Jul-24 | 2024-Aug-20 | 2025-Mar-04 | M128 | v20.16 | 🚫 | | 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | 🚫 | | 30.0.0 | 2024-Feb-22 | 2024-Mar-20 | 2024-Apr-16 | 2024-Oct-15 | M124 | v20.11 | 🚫 | From 3f8340d9f37324606b0b7200b66ddda032ca6794 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:32:49 +0200 Subject: [PATCH 153/339] docs: cleanup docs/tutorial/custom-window-styles.md (#46713) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Milan Burda --- docs/tutorial/custom-window-styles.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tutorial/custom-window-styles.md b/docs/tutorial/custom-window-styles.md index 8e4503cbb5cc8..e07a06230bcf0 100644 --- a/docs/tutorial/custom-window-styles.md +++ b/docs/tutorial/custom-window-styles.md @@ -37,7 +37,6 @@ the illusion of a circular window. open on the user's system). * The window will not be transparent when DevTools is opened. * On _Windows_: - * Transparent windows will not work when DWM is disabled. * Transparent windows can not be maximized using the Windows system menu or by double clicking the title bar. The reasoning behind this can be seen on PR [#28207](https://github.com/electron/electron/pull/28207). From b821cf1adc60734f131a6d16ac6051cd437026d1 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:31:55 -0500 Subject: [PATCH 154/339] build: don't kill ssh sessions on checkout failure (#46718) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/checkout/action.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index a9fcd90022283..205eefde25816 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -179,3 +179,11 @@ runs: else echo "Cache key persisted in $final_cache_path" fi + - name: Wait for active SSH sessions + shell: bash + if: always() && !cancelled() + run: | + while [ -f /var/.ssh-lock ] + do + sleep 60 + done From 8f40f8126ef67d0976abbdb854782015081e6f96 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:32:15 -0500 Subject: [PATCH 155/339] fix: file dialog filters not working correctly (#46722) fix: fix file dialog filters not working correctly If someone sets an `All filter` with `*` at the start of the filters all upcoming filters will be shifted and thus labels won't fit to the extensions they actually filter. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Kolja Lampe --- shell/browser/ui/file_dialog_linux.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/browser/ui/file_dialog_linux.cc b/shell/browser/ui/file_dialog_linux.cc index ad65248778304..404aead64b5d9 100644 --- a/shell/browser/ui/file_dialog_linux.cc +++ b/shell/browser/ui/file_dialog_linux.cc @@ -44,14 +44,14 @@ ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) { ui::SelectFileDialog::FileTypeInfo file_type_info; for (const auto& [name, extension_group] : filters) { - file_type_info.extension_description_overrides.push_back( - base::UTF8ToUTF16(name)); - const bool has_all_files_wildcard = std::ranges::any_of( extension_group, [](const auto& ext) { return ext == "*"; }); + if (has_all_files_wildcard) { file_type_info.include_all_files = true; } else { + file_type_info.extension_description_overrides.push_back( + base::UTF8ToUTF16(name)); file_type_info.extensions.emplace_back(extension_group); } } From b0d4c122477cf36c5f9467975699f4cb2d1d07c6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:40:35 -0500 Subject: [PATCH 156/339] fix: osr stutter fix backport for electron. (#46709) * fix: osr stutter fix backport for electron. * nit: chromium upstream patch link Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: reito --- patches/chromium/.patches | 1 + ...sr_stutter_fix_backport_for_electron.patch | 290 ++++++++++++++++++ shell/browser/osr/osr_video_consumer.cc | 5 + 3 files changed, 296 insertions(+) create mode 100644 patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 970ba996347a5..a0203f5cba639 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -146,3 +146,4 @@ chore_remove_conflicting_allow_unsafe_libc_calls.patch fix_linter_error.patch revert_enable_crel_for_arm32_targets.patch mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch +fix_osr_stutter_fix_backport_for_electron.patch diff --git a/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch b/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch new file mode 100644 index 0000000000000..941dc93331bb1 --- /dev/null +++ b/patches/chromium/fix_osr_stutter_fix_backport_for_electron.patch @@ -0,0 +1,290 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: reito +Date: Wed, 16 Apr 2025 14:09:50 +0800 +Subject: fix: osr stutter fix backport for electron. + +The animated_content_sampler is used to detect the animation frame rate +and adjust the capture frame rate accordingly. However, the detection +algorithm is buggy and can cause output to stutter. This patch is a +upstream patch to allow opt-out the animated_content_sampler. +https://crrev.org/c/6438681 + +diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc +index 67aeb7222ae490cc62717bd7eb8aace022553e9c..11fe61903855b3ef52733aecc3288946bf572de3 100644 +--- a/components/viz/host/client_frame_sink_video_capturer.cc ++++ b/components/viz/host/client_frame_sink_video_capturer.cc +@@ -39,6 +39,17 @@ void ClientFrameSinkVideoCapturer::SetFormat(media::VideoPixelFormat format) { + capturer_remote_->SetFormat(format); + } + ++void ClientFrameSinkVideoCapturer::SetAnimationFpsLockIn( ++ bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ animated_content_sampler_enabled_ = enabled; ++ majority_damaged_pixel_min_ratio_ = majority_damaged_pixel_min_ratio; ++ capturer_remote_->SetAnimationFpsLockIn(enabled, ++ majority_damaged_pixel_min_ratio); ++} ++ + void ClientFrameSinkVideoCapturer::SetMinCapturePeriod( + base::TimeDelta min_capture_period) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +@@ -206,6 +217,10 @@ void ClientFrameSinkVideoCapturer::EstablishConnection() { + capturer_remote_->SetMinCapturePeriod(*min_capture_period_); + if (min_size_change_period_) + capturer_remote_->SetMinSizeChangePeriod(*min_size_change_period_); ++ if (animated_content_sampler_enabled_ && majority_damaged_pixel_min_ratio_) { ++ capturer_remote_->SetAnimationFpsLockIn(*animated_content_sampler_enabled_, ++ *majority_damaged_pixel_min_ratio_); ++ } + if (resolution_constraints_) { + capturer_remote_->SetResolutionConstraints( + resolution_constraints_->min_size, resolution_constraints_->max_size, +diff --git a/components/viz/host/client_frame_sink_video_capturer.h b/components/viz/host/client_frame_sink_video_capturer.h +index 8f0693b37dd0aa931a7fd77ddbdb515f0be5d64a..c0613570552072b3da219a920f902ad39a9f1cbc 100644 +--- a/components/viz/host/client_frame_sink_video_capturer.h ++++ b/components/viz/host/client_frame_sink_video_capturer.h +@@ -88,6 +88,8 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer + const gfx::Size& max_size, + bool use_fixed_aspect_ratio); + void SetAutoThrottlingEnabled(bool enabled); ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio); + void ChangeTarget(const std::optional& target); + void ChangeTarget(const std::optional& target, + uint32_t sub_capture_target_version); +@@ -158,6 +160,8 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer + std::optional resolution_constraints_; + std::optional auto_throttling_enabled_; + std::optional target_; ++ std::optional animated_content_sampler_enabled_; ++ std::optional majority_damaged_pixel_min_ratio_; + uint32_t sub_capture_target_version_ = 0; + // Overlays are owned by the callers of CreateOverlay(). + std::vector> overlays_; +diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +index db02d9ad523dd1471b5c88cf6b1eade0b592e24f..e8a329f013f39c49056c7eebc7412e84a1b2e98c 100644 +--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc ++++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +@@ -364,6 +364,18 @@ void FrameSinkVideoCapturerImpl::SetMinSizeChangePeriod( + oracle_->SetMinSizeChangePeriod(min_period); + } + ++void FrameSinkVideoCapturerImpl::SetAnimationFpsLockIn( ++ bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ TRACE_EVENT_INSTANT("gpu.capture", "SetAnimationFpsLockIn", "enabled", ++ enabled, "majority_damaged_pixel_min_ratio", ++ majority_damaged_pixel_min_ratio); ++ ++ oracle_->SetAnimationFpsLockIn(enabled, majority_damaged_pixel_min_ratio); ++} ++ + void FrameSinkVideoCapturerImpl::SetResolutionConstraints( + const gfx::Size& min_size, + const gfx::Size& max_size, +diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h +index 8d9036f835c5ced90872b66c8545c65097fd23cc..b387d22c31ab171cde19ceb4a4b5f2a4ce334d9e 100644 +--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h ++++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h +@@ -119,6 +119,8 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final + const gfx::Size& max_size, + bool use_fixed_aspect_ratio) final; + void SetAutoThrottlingEnabled(bool enabled) final; ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio) final; + void ChangeTarget(const std::optional& target, + uint32_t sub_capture_target_version) final; + void Start(mojo::PendingRemote consumer, +diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc +index 3420c52fc9bb85057242d25429b00a7a8ec620cd..83ce6ecf31f1bb38de98ff81960aa1948bb5a4e3 100644 +--- a/content/browser/devtools/devtools_video_consumer_unittest.cc ++++ b/content/browser/devtools/devtools_video_consumer_unittest.cc +@@ -70,6 +70,8 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + min_period_ = min_period; + MockSetMinSizeChangePeriod(min_period_); + } ++ MOCK_METHOD2(SetAnimationFpsLockIn, ++ void(bool enabled, float majority_damaged_pixel_min_ratio)); + MOCK_METHOD1(MockSetMinSizeChangePeriod, void(base::TimeDelta min_period)); + void SetResolutionConstraints(const gfx::Size& min_frame_size, + const gfx::Size& max_frame_size, +diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc +index b236a38f7f108f823598ae2bf8dc07e53a190141..46a8e24a3ec9ad2ec42fc50c2ac7ab326508a873 100644 +--- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc ++++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc +@@ -105,6 +105,8 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + MOCK_METHOD1(SetFormat, void(media::VideoPixelFormat format)); + MOCK_METHOD1(SetMinCapturePeriod, void(base::TimeDelta min_period)); + MOCK_METHOD1(SetMinSizeChangePeriod, void(base::TimeDelta)); ++ MOCK_METHOD2(SetAnimationFpsLockIn, ++ void(bool enabled, float majority_damaged_pixel_min_ratio)); + MOCK_METHOD3(SetResolutionConstraints, + void(const gfx::Size& min_size, + const gfx::Size& max_size, +diff --git a/media/capture/content/animated_content_sampler.cc b/media/capture/content/animated_content_sampler.cc +index 6fe67268c1dcb23188200e49c100e985406a9feb..0fadaa2b768a42d3bad511224ad9d6e3ce337e70 100644 +--- a/media/capture/content/animated_content_sampler.cc ++++ b/media/capture/content/animated_content_sampler.cc +@@ -46,7 +46,10 @@ constexpr auto kDriftCorrection = base::Seconds(2); + + AnimatedContentSampler::AnimatedContentSampler( + base::TimeDelta min_capture_period) +- : min_capture_period_(min_capture_period), sampling_state_(NOT_SAMPLING) { ++ : min_capture_period_(min_capture_period), ++ sampling_state_(NOT_SAMPLING), ++ enabled_(true), ++ majority_damaged_pixel_min_ratio_(2.0f / 3) { + DCHECK_GT(min_capture_period_, base::TimeDelta()); + } + +@@ -64,6 +67,10 @@ void AnimatedContentSampler::SetTargetSamplingPeriod(base::TimeDelta period) { + void AnimatedContentSampler::ConsiderPresentationEvent( + const gfx::Rect& damage_rect, + base::TimeTicks event_time) { ++ if (!enabled_) { ++ return; // The sampler is disabled. ++ } ++ + // Analyze the current event and recent history to determine whether animating + // content is detected. + AddObservation(damage_rect, event_time); +@@ -132,6 +139,10 @@ void AnimatedContentSampler::ConsiderPresentationEvent( + } + + bool AnimatedContentSampler::HasProposal() const { ++ if (!enabled_) { ++ return false; ++ } ++ + return sampling_state_ != NOT_SAMPLING; + } + +@@ -230,8 +241,10 @@ bool AnimatedContentSampler::AnalyzeObservations( + if ((last_event_time - first_event_time) < kMinObservationWindow) { + return false; // Content has not animated for long enough for accuracy. + } +- if (num_pixels_damaged_in_chosen <= (num_pixels_damaged_in_all * 2 / 3)) ++ if (num_pixels_damaged_in_chosen <= ++ (num_pixels_damaged_in_all * majority_damaged_pixel_min_ratio_)) { + return false; // Animation is not damaging a supermajority of pixels. ++ } + + *rect = elected_rect; + DCHECK_GT(count_frame_durations, 0u); +diff --git a/media/capture/content/animated_content_sampler.h b/media/capture/content/animated_content_sampler.h +index 89d11829ac038ffeaafa3d4f0b811c6b2bd5665c..b5a325383813210956be7bcd59d8d41fa8c28d9b 100644 +--- a/media/capture/content/animated_content_sampler.h ++++ b/media/capture/content/animated_content_sampler.h +@@ -26,6 +26,15 @@ class CAPTURE_EXPORT AnimatedContentSampler { + explicit AnimatedContentSampler(base::TimeDelta min_capture_period); + ~AnimatedContentSampler(); + ++ // Set whether the animated content sampler would have proposal. ++ void SetEnabled(bool enabled) { enabled_ = enabled; } ++ ++ // Sets the minimum ratio of pixels in the majority-damaged region to all ++ // damaged region's area. ++ void SetMajorityDamagedRectMinRatio(float ratio) { ++ majority_damaged_pixel_min_ratio_ = ratio; ++ } ++ + // Sets a new minimum capture period. + void SetMinCapturePeriod(base::TimeDelta period); + +@@ -34,6 +43,7 @@ class CAPTURE_EXPORT AnimatedContentSampler { + base::TimeDelta target_sampling_period() const { + return target_sampling_period_; + } ++ + void SetTargetSamplingPeriod(base::TimeDelta period); + + // Examines the given presentation event metadata, along with recent history, +@@ -152,6 +162,13 @@ class CAPTURE_EXPORT AnimatedContentSampler { + + // The rewritten frame timestamp for the latest event. + base::TimeTicks frame_timestamp_; ++ ++ // Whether the animated content sampler is enabled ++ bool enabled_; ++ ++ // The minimum ratio of the majority damaged rect area among all damaged ++ // area's pixels ++ float majority_damaged_pixel_min_ratio_; + }; + + } // namespace media +diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h +index 3bb10527f7850e795cb6608dd7e881dc90920eee..b7ac2d4404ae1fdc8a9983da16dcb2ad921b76bc 100644 +--- a/media/capture/content/video_capture_oracle.h ++++ b/media/capture/content/video_capture_oracle.h +@@ -54,6 +54,7 @@ class CAPTURE_EXPORT VideoCaptureOracle { + base::TimeDelta min_capture_period() const { + return smoothing_sampler_.min_capture_period(); + } ++ + void SetMinCapturePeriod(base::TimeDelta period); + + // Sets the range of acceptable capture sizes and whether a fixed aspect ratio +@@ -70,6 +71,19 @@ class CAPTURE_EXPORT VideoCaptureOracle { + // See: SetMinSizeChangePeriod(). + void SetAutoThrottlingEnabled(bool enabled); + ++ // Specifies whether the oracle should detect animation and try to target ++ // the animation frame rate. If |enabled|, the oracle will try to detect a ++ // majority damaged rect and its animation frame rate, and will respect the ++ // minimum damaged pixel ratio of the majority rect's area among all damaged ++ // rect areas set by |majority_damaged_pixel_min_ratio|. If the threshold not ++ // met, it will not use the animated content frame rate. ++ void SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio) { ++ content_sampler_.SetEnabled(enabled); ++ content_sampler_.SetMajorityDamagedRectMinRatio( ++ majority_damaged_pixel_min_ratio); ++ } ++ + // Get/Update the source content size. Changes may not have an immediate + // effect on the proposed capture size, as the oracle will prevent too- + // frequent changes from occurring. +diff --git a/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc b/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc +index eeb4454bfd9500e4bae7544409ff4413fd0874aa..abe0cfb065a7e313b0ca3babd744fce75074ef45 100644 +--- a/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc ++++ b/remoting/host/chromeos/frame_sink_desktop_capturer_unittest.cc +@@ -195,6 +195,10 @@ class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + + MOCK_METHOD(void, SetAutoThrottlingEnabled, (bool enabled)); + ++ MOCK_METHOD(void, ++ SetAnimationFpsLockIn, ++ (bool enabled, float majority_damaged_pixel_min_ratio)); ++ + MOCK_METHOD(void, + SetResolutionConstraints, + (const Size& min_size, +diff --git a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom +index f0ca9014a0e6b42f99becb7f1fdb9e214c81a16b..fbb002941ca1d36dc2bb12e9391b1faa799d771e 100644 +--- a/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom ++++ b/services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom +@@ -156,6 +156,18 @@ interface FrameSinkVideoCapturer { + // Default, if never called: true. + SetAutoThrottlingEnabled(bool enabled); + ++ // Determines whether the capturer should detect animations and aim to match ++ // their frame rate. If |enabled| is true, the capturer will attempt to ++ // identify the majority damaged rect and its animation frame rate, ++ // while adhering to the |majority_damaged_pixel_min_ratio| threshold. ++ // This ratio is calculated as the area of the majority damaged rect ++ // divided by the total area of all damaged rects. An animation will only ++ // be considered valid if the ratio meets or exceeds the specified threshold. ++ // ++ // By default, this feature is enabled, with the ratio threshold set to 2/3. ++ SetAnimationFpsLockIn(bool enabled, ++ float majority_damaged_pixel_min_ratio); ++ + // Targets a different compositor frame sink. This may be called anytime, + // before or after Start(). + // diff --git a/shell/browser/osr/osr_video_consumer.cc b/shell/browser/osr/osr_video_consumer.cc index a09cb46f2004f..85c9286e18fc6 100644 --- a/shell/browser/osr/osr_video_consumer.cc +++ b/shell/browser/osr/osr_video_consumer.cc @@ -28,6 +28,11 @@ OffScreenVideoConsumer::OffScreenVideoConsumer( video_capturer_->SetMinSizeChangePeriod(base::TimeDelta()); video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB); + // https://crrev.org/c/6438681 + // Disable capturer's animation lock-in feature for offscreen capture to + // avoid output stutter. + video_capturer_->SetAnimationFpsLockIn(false, 1); + // Previous design of OSR try to set the resolution constraint to match the // view's size. It is actually not necessary and creates faulty textures // when the window/view's size changes frequently. The constraint may not From 07a7ebb714317e6730d4a1f4a0e0f60d055eb404 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:33:01 -0500 Subject: [PATCH 157/339] refactor: make `electron::api::BaseWindow` fields private (#46710) refactor: make electron::api::BaseWindow fields private Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_base_window.h | 5 +++-- shell/browser/api/electron_api_browser_window.cc | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 1ee4443f9cdb2..68619364a8fbf 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -42,7 +42,8 @@ class BaseWindow : public gin_helper::TrackableObject, static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - NativeWindow* window() const { return window_.get(); } + const NativeWindow* window() const { return window_.get(); } + NativeWindow* window() { return window_.get(); } protected: // Common constructor. @@ -262,6 +263,7 @@ class BaseWindow : public gin_helper::TrackableObject, #endif int32_t GetID() const; + private: // Helpers. // Remove this window from parent window's |child_windows_|. @@ -290,7 +292,6 @@ class BaseWindow : public gin_helper::TrackableObject, // Reference to JS wrapper to prevent garbage collection. v8::Global self_ref_; - private: base::WeakPtrFactory weak_factory_{this}; }; diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 742ca07976da6..b27c7f0f32c96 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -36,7 +36,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args, std::string color; if (options.Get(options::kBackgroundColor, &color)) { web_preferences.SetHidden(options::kBackgroundColor, color); - } else if (window_->IsTranslucent()) { + } else if (window()->IsTranslucent()) { // If the BrowserWindow is transparent or a vibrancy type has been set, // also propagate transparency to the WebContents unless a separate // backgroundColor has been set. @@ -67,7 +67,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args, gin::Handle web_contents_view = WebContentsView::Create(isolate, web_preferences); DCHECK(web_contents_view.get()); - window_->AddDraggableRegionProvider(web_contents_view.get()); + window()->AddDraggableRegionProvider(web_contents_view.get()); web_contents_view_.Reset(isolate, web_contents_view.ToV8()); // Save a reference of the WebContents. @@ -209,7 +209,7 @@ void BrowserWindow::UpdateWindowControlsOverlay( void BrowserWindow::CloseImmediately() { // Close all child windows before closing current window. v8::HandleScope handle_scope(isolate()); - for (v8::Local value : child_windows_.Values(isolate())) { + for (v8::Local value : GetChildWindows()) { gin::Handle child; if (gin::ConvertFromV8(isolate(), value, &child) && !child.IsEmpty()) child->window()->CloseImmediately(); From 11c820c537249155e482a35bb554a26a85be9109 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:41:52 -0400 Subject: [PATCH 158/339] fix: stop menu minimization if set false (#46714) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michaela Laurencin --- lib/browser/api/menu-item-roles.ts | 4 +++- spec/api-menu-item-spec.ts | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/browser/api/menu-item-roles.ts b/lib/browser/api/menu-item-roles.ts index fe2b07ed0fee7..1bb48b636e929 100644 --- a/lib/browser/api/menu-item-roles.ts +++ b/lib/browser/api/menu-item-roles.ts @@ -78,7 +78,9 @@ export const roleList: Record = { minimize: { label: 'Minimize', accelerator: 'CommandOrControl+M', - windowMethod: w => w.minimize() + windowMethod: w => { + if (w.minimizable) w.minimize(); + } }, paste: { label: 'Paste', diff --git a/spec/api-menu-item-spec.ts b/spec/api-menu-item-spec.ts index e385b4773d63b..dd71140cacabe 100644 --- a/spec/api-menu-item-spec.ts +++ b/spec/api-menu-item-spec.ts @@ -2,7 +2,7 @@ import { BrowserWindow, app, Menu, MenuItem, MenuItemConstructorOptions } from ' import { expect } from 'chai'; -import { ifdescribe } from './lib/spec-helpers'; +import { ifit, ifdescribe } from './lib/spec-helpers'; import { closeAllWindows } from './lib/window-helpers'; import { roleList, execute } from '../lib/browser/api/menu-item-roles'; @@ -205,6 +205,22 @@ describe('MenuItems', () => { const canExecute = execute(item.role as any, win, win.webContents); expect(canExecute).to.be.true('can execute'); }); + + ifit(process.platform === 'win32')('does not execute minimize role when minimizable false', () => { + const win = new BrowserWindow({ minimizable: false }); + const menu = Menu.buildFromTemplate([{ + label: 'text', + role: 'minimize' + }]); + + Menu.setApplicationMenu(menu); + menu._executeCommand({}, menu.items[0].commandId); + expect(win.isMinimized()).to.equal(false); + + win.setMinimizable(true); + menu._executeCommand({}, menu.items[0].commandId); + expect(win.isMinimized()).to.equal(true); + }); }); describe('MenuItem command id', () => { From 2629b31c19e94ab10ada02868c80d6e90cf333cc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 08:10:18 -0500 Subject: [PATCH 159/339] refactor: remove redundant NativeImage::GetBitmap() (#46736) * refactor: remove redundant NativeImage::GetBitmap() Co-authored-by: Charles Kerr * docs: mark NativeImage.getBitmap() as deprecated Co-authored-by: Charles Kerr * have getBitmap() emit a deprecation warning Co-authored-by: David Sanders * docs: update obsolete refefence to getBitmap() Co-authored-by: Charles Kerr * test: update obsolete refefences to getBitmap() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr Co-authored-by: David Sanders --- docs/api/native-image.md | 8 ++------ docs/api/web-contents.md | 2 +- docs/breaking-changes.md | 13 +++++++++++++ shell/common/api/electron_api_native_image.cc | 19 +++++++++---------- spec/api-browser-window-spec.ts | 2 +- spec/api-native-image-spec.ts | 12 +++++++++--- spec/ts-smoke/electron/main.ts | 2 +- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 91ee4e68ef042..14ee825618c30 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -271,16 +271,12 @@ changes: Returns `string` - The [Data URL][data-url] of the image. -#### `image.getBitmap([options])` +#### `image.getBitmap([options])` _Deprecated_ * `options` Object (optional) * `scaleFactor` Number (optional) - Defaults to 1.0. -Returns `Buffer` - A [Buffer][buffer] that contains the image's raw bitmap pixel data. - -The difference between `getBitmap()` and `toBitmap()` is that `getBitmap()` does not -copy the bitmap data, so you have to use the returned Buffer immediately in -current event loop tick; otherwise the data might be changed or destroyed. +Legacy alias for `image.toBitmap()`. #### `image.getNativeHandle()` _macOS_ diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index bcb733a8ad985..0dc3cd9dfc7cb 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -887,7 +887,7 @@ const { BrowserWindow } = require('electron') const win = new BrowserWindow({ webPreferences: { offscreen: true } }) win.webContents.on('paint', (event, dirty, image) => { - // updateBitmap(dirty, image.getBitmap()) + // updateBitmap(dirty, image.toBitmap()) }) win.loadURL('https://github.com') ``` diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 9d26d0b458365..4a75e7f95ad06 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -27,6 +27,19 @@ window is not currently visible. If you were using `app.commandLine` to control the behavior of the main process, you should do this via `process.argv`. +### Deprecated: `NativeImage.getBitmap()` + +`NativeImage.toBitmap()` returns a newly-allocated copy of the bitmap. `NativeImage.getBitmap()` was originally an alternative function that returned the original instead of a copy. This changed when sandboxing was introduced, so both return a copy and are functionally equivalent. + +Client code should call `NativeImage.toBitmap()` instead: + +```js +// Deprecated +bitmap = image.getBitmap() +// Use this instead +bitmap = image.toBitmap() +``` + ## Planned Breaking API Changes (36.0) ### Removed:`isDefault` and `status` properties on `PrinterInfo` diff --git a/shell/common/api/electron_api_native_image.cc b/shell/common/api/electron_api_native_image.cc index a6e27003600b6..d16023390d4ad 100644 --- a/shell/common/api/electron_api_native_image.cc +++ b/shell/common/api/electron_api_native_image.cc @@ -287,17 +287,16 @@ std::string NativeImage::ToDataURL(gin::Arguments* args) { } v8::Local NativeImage::GetBitmap(gin::Arguments* args) { - float scale_factor = GetScaleFactorFromOptions(args); + static bool deprecated_warning_issued = false; - const SkBitmap bitmap = - image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap(); - SkPixelRef* ref = bitmap.pixelRef(); - if (!ref) - return node::Buffer::New(args->isolate(), 0).ToLocalChecked(); - return node::Buffer::Copy(args->isolate(), - reinterpret_cast(ref->pixels()), - bitmap.computeByteSize()) - .ToLocalChecked(); + if (!deprecated_warning_issued) { + deprecated_warning_issued = true; + util::EmitWarning(isolate_, + "getBitmap() is deprecated, use toBitmap() instead.", + "DeprecationWarning"); + } + + return ToBitmap(args); } v8::Local NativeImage::GetNativeHandle( diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index f90078f9a391f..56980c7683d3e 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -4599,7 +4599,7 @@ describe('BrowserWindow module', () => { try { const expectedSize = rect.width * rect.height * 4; - expect(image.getBitmap()).to.be.an.instanceOf(Buffer).with.lengthOf(expectedSize); + expect(image.toBitmap()).to.be.an.instanceOf(Buffer).with.lengthOf(expectedSize); done(); } catch (e) { done(e); diff --git a/spec/api-native-image-spec.ts b/spec/api-native-image-spec.ts index bbd30cff7aa6a..72a5ffeaee52b 100644 --- a/spec/api-native-image-spec.ts +++ b/spec/api-native-image-spec.ts @@ -4,6 +4,7 @@ import { expect } from 'chai'; import * as path from 'node:path'; +import { expectDeprecationMessages } from './lib/deprecate-helpers'; import { ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers'; describe('nativeImage module', () => { @@ -78,15 +79,20 @@ describe('nativeImage module', () => { }); describe('createEmpty()', () => { - it('returns an empty image', () => { + it('returns an empty image', async () => { const empty = nativeImage.createEmpty(); expect(empty.isEmpty()).to.be.true(); expect(empty.getAspectRatio()).to.equal(1); expect(empty.toDataURL()).to.equal('data:image/png;base64,'); expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,'); expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }); - expect(empty.getBitmap()).to.be.empty(); - expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty(); + await expectDeprecationMessages( + () => { + expect(empty.getBitmap()).to.be.empty(); + expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty(); + }, + 'getBitmap() is deprecated, use toBitmap() instead.' + ); expect(empty.toBitmap()).to.be.empty(); expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty(); expect(empty.toJPEG(100)).to.be.empty(); diff --git a/spec/ts-smoke/electron/main.ts b/spec/ts-smoke/electron/main.ts index 64fd6232540a2..e4fc47f7383df 100644 --- a/spec/ts-smoke/electron/main.ts +++ b/spec/ts-smoke/electron/main.ts @@ -1291,7 +1291,7 @@ const win4 = new BrowserWindow({ }); win4.webContents.on('paint', (event, dirty, _image) => { - console.log(dirty, _image.getBitmap()); + console.log(dirty, _image.toBitmap()); }); win4.webContents.on('devtools-open-url', (event, url) => { From fc319e0ea365e9e11f3769884c3da8fdc69dbf53 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 09:53:53 -0500 Subject: [PATCH 160/339] fix: explicitly call GetNativeNSView() on macOS (#46750) * fix: explicitly call GetNativeNSView() on macOS Co-authored-by: Charles Kerr * chore: move macOS impl to a .mm file This is needed in order to access gfx::NativeView::GetNativeNSView() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- filenames.gni | 1 + shell/browser/api/electron_api_base_window.cc | 4 +++ .../api/electron_api_base_window_mac.mm | 32 +++++++++++++++++++ .../browser/api/electron_api_web_contents.cc | 6 ++-- .../api/electron_api_web_contents_mac.mm | 19 +++++++++++ shell/browser/native_window.h | 4 +-- 6 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 shell/browser/api/electron_api_base_window_mac.mm diff --git a/filenames.gni b/filenames.gni index ea1637c990309..45fafbdb52eb5 100644 --- a/filenames.gni +++ b/filenames.gni @@ -125,6 +125,7 @@ filenames = { "shell/browser/animation_util.h", "shell/browser/animation_util_mac.mm", "shell/browser/api/electron_api_app_mac.mm", + "shell/browser/api/electron_api_base_window_mac.mm", "shell/browser/api/electron_api_menu_mac.h", "shell/browser/api/electron_api_menu_mac.mm", "shell/browser/api/electron_api_native_theme_mac.mm", diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 848cc6cc2c9f8..a7983dd39c8bc 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -70,6 +70,7 @@ namespace electron::api { namespace { +#if !BUILDFLAG(IS_MAC) // Converts binary data to Buffer. v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { auto buffer = node::Buffer::Copy(isolate, static_cast(val), size); @@ -78,6 +79,7 @@ v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { else return buffer.ToLocalChecked(); } +#endif [[nodiscard]] constexpr std::array ToArray(const gfx::Size size) { return {size.width(), size.height()}; @@ -778,6 +780,7 @@ std::string BaseWindow::GetMediaSourceId() const { return window_->GetDesktopMediaID().ToString(); } +#if !BUILDFLAG(IS_MAC) v8::Local BaseWindow::GetNativeWindowHandle() { // TODO(MarshallOfSound): Replace once // https://chromium-review.googlesource.com/c/chromium/src/+/1253094/ has @@ -785,6 +788,7 @@ v8::Local BaseWindow::GetNativeWindowHandle() { NativeWindowHandle handle = window_->GetNativeWindowHandle(); return ToBuffer(isolate(), &handle, sizeof(handle)); } +#endif void BaseWindow::SetProgressBar(double progress, gin_helper::Arguments* args) { gin_helper::Dictionary options; diff --git a/shell/browser/api/electron_api_base_window_mac.mm b/shell/browser/api/electron_api_base_window_mac.mm new file mode 100644 index 0000000000000..148ec216283f7 --- /dev/null +++ b/shell/browser/api/electron_api_base_window_mac.mm @@ -0,0 +1,32 @@ +// Copyright (c) 2025 Microsoft, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/api/electron_api_base_window.h" + +#include "electron/buildflags/buildflags.h" +#include "shell/browser/api/electron_api_view.h" +#include "shell/browser/native_window.h" +#include "shell/common/node_includes.h" + +namespace { + +// Converts binary data to Buffer. +v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { + auto buffer = node::Buffer::Copy(isolate, static_cast(val), size); + if (buffer.IsEmpty()) + return v8::Null(isolate); + else + return buffer.ToLocalChecked(); +} + +} // namespace + +namespace electron::api { + +v8::Local BaseWindow::GetNativeWindowHandle() { + NSView* handle = window_->GetNativeWindowHandle().GetNativeNSView(); + return ToBuffer(isolate(), &handle, sizeof(handle)); +} + +} // namespace electron::api diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 0779c4919b29a..bf5f6ec4b44b2 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -3743,15 +3743,17 @@ void WebContents::SetDevToolsWebContents(const WebContents* devtools) { inspectable_web_contents_->SetDevToolsWebContents(devtools->web_contents()); } +#if !BUILDFLAG(IS_MAC) v8::Local WebContents::GetNativeView(v8::Isolate* isolate) const { gfx::NativeView ptr = web_contents()->GetNativeView(); - auto buffer = node::Buffer::Copy(isolate, reinterpret_cast(&ptr), - sizeof(gfx::NativeView)); + auto buffer = + node::Buffer::Copy(isolate, reinterpret_cast(&ptr), sizeof(ptr)); if (buffer.IsEmpty()) return v8::Null(isolate); else return buffer.ToLocalChecked(); } +#endif v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { if (devtools_web_contents_.IsEmpty()) diff --git a/shell/browser/api/electron_api_web_contents_mac.mm b/shell/browser/api/electron_api_web_contents_mac.mm index 52c4362839610..1358b3dde95da 100644 --- a/shell/browser/api/electron_api_web_contents_mac.mm +++ b/shell/browser/api/electron_api_web_contents_mac.mm @@ -6,6 +6,7 @@ #include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/ui/cocoa/event_dispatching_window.h" #include "shell/browser/web_contents_preferences.h" +#include "shell/common/node_includes.h" #include "ui/base/cocoa/command_dispatcher.h" #include "ui/base/cocoa/nsmenu_additions.h" #include "ui/base/cocoa/nsmenuitem_additions.h" @@ -92,4 +93,22 @@ - (void)redispatchKeyEvent:(NSEvent*)event; return false; } +namespace { + +// Converts binary data to Buffer. +v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { + auto buffer = node::Buffer::Copy(isolate, static_cast(val), size); + if (buffer.IsEmpty()) + return v8::Null(isolate); + else + return buffer.ToLocalChecked(); +} + +} // namespace + +v8::Local WebContents::GetNativeView(v8::Isolate* isolate) const { + NSView* handle = web_contents()->GetNativeView().GetNativeNSView(); + return ToBuffer(isolate, &handle, sizeof(handle)); +} + } // namespace electron::api diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index cc4d150ca72b1..831cf8947aa85 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -58,9 +58,9 @@ class BrowserView; } #if BUILDFLAG(IS_MAC) -typedef gfx::NativeView NativeWindowHandle; +using NativeWindowHandle = gfx::NativeView; #else -typedef gfx::AcceleratedWidget NativeWindowHandle; +using NativeWindowHandle = gfx::AcceleratedWidget; #endif class NativeWindow : public base::SupportsUserData, From 4e8c09f46a75fbb42b16944ace735bfa6a0dd690 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:40:46 +0200 Subject: [PATCH 161/339] chore: bump chromium to 136.0.7103.48 (36-x-y) (#46756) * chore: bump chromium in DEPS to 136.0.7103.48 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- DEPS | 2 +- .../build_add_public_config_simdutf_config.patch | 2 +- ..._do_not_depend_on_packed_resource_integrity.patch | 12 ++++++------ patches/chromium/can_create_window.patch | 10 +++++----- patches/chromium/chore_partial_revert_of.patch | 4 ++-- ...contentscreationoverridden_with_full_params.patch | 4 ++-- ...een-keyboard_hides_on_input_blur_in_webview.patch | 4 ++-- ...ewly_created_to_allow_for_browser_initiated.patch | 4 ++-- ...e_cursor_changes_to_the_webcontentsobserver.patch | 12 ++++++------ ...efactor_expose_file_system_access_blocklist.patch | 4 ++-- .../refactor_unfilter_unresponsive_events.patch | 4 ++-- patches/chromium/web_contents.patch | 6 +++--- patches/chromium/webview_fullscreen.patch | 10 +++++----- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/DEPS b/DEPS index 31fd17451674e..287c049ac1c4c 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.33', + '136.0.7103.48', 'node_version': 'v22.14.0', 'nan_version': diff --git a/patches/chromium/build_add_public_config_simdutf_config.patch b/patches/chromium/build_add_public_config_simdutf_config.patch index 11a7174857f61..3417d2da5db21 100644 --- a/patches/chromium/build_add_public_config_simdutf_config.patch +++ b/patches/chromium/build_add_public_config_simdutf_config.patch @@ -11,7 +11,7 @@ To accomplish this, we need to make simdutf's config public here for use by third_party/electron_node. diff --git a/third_party/simdutf/BUILD.gn b/third_party/simdutf/BUILD.gn -index d88fe7e43ac2a8129702e58bd2cd2aea094452e3..3cbeb89587f37b0ebc3622258fea0161ebf1d7b2 100644 +index 5fbce38841f04dad38f202f529ae84c609c6a8de..9f5ef9bceade8e30bbd2be616b9143e9708fefd8 100644 --- a/third_party/simdutf/BUILD.gn +++ b/third_party/simdutf/BUILD.gn @@ -6,9 +6,14 @@ source_set("header") { diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index ccd60b62c4c0b..b8e6f9b1f8afb 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -33,10 +33,10 @@ index 95c73dcb082999d0a85d82f1fe330d27c88055ca..64cd976eba849435779b280b2941f915 "//base", "//build:branding_buildflags", diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index a3623400c3548b31e3e9bc8c15e6c5081e4990cc..db7e78e4b72de2af313741bb802b51f97b187357 100644 +index b5783087429ee651d21e04f6f5f11f5cb6f015a8..dae1cca8cee4fccfed3183aca1ba7cc32652ea58 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -4593,7 +4593,7 @@ static_library("browser") { +@@ -4599,7 +4599,7 @@ static_library("browser") { [ "//chrome/browser/ui/webui/signin:profile_impl" ] } @@ -46,10 +46,10 @@ index a3623400c3548b31e3e9bc8c15e6c5081e4990cc..db7e78e4b72de2af313741bb802b51f9 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index 5de0d083bbbeddd5f394ac0154789566789099fb..692e44b75ac164a6f88398a4177913c47f49959e 100644 +index a1338c3f145e3ae0ca0acbe20ffcec35c3cc5ba1..39d0efc3367b80b18cf3a4ef8756f147807ad79b 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn -@@ -7092,9 +7092,12 @@ test("unit_tests") { +@@ -7096,9 +7096,12 @@ test("unit_tests") { "//chrome/notification_helper", ] @@ -63,7 +63,7 @@ index 5de0d083bbbeddd5f394ac0154789566789099fb..692e44b75ac164a6f88398a4177913c4 "//chrome//services/util_win:unit_tests", "//chrome/app:chrome_dll_resources", "//chrome/app:win_unit_tests", -@@ -8063,6 +8066,10 @@ test("unit_tests") { +@@ -8067,6 +8070,10 @@ test("unit_tests") { "../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc", ] @@ -74,7 +74,7 @@ index 5de0d083bbbeddd5f394ac0154789566789099fb..692e44b75ac164a6f88398a4177913c4 sources += [ # The importer code is not used on Android. "../common/importer/firefox_importer_utils_unittest.cc", -@@ -8118,7 +8125,6 @@ test("unit_tests") { +@@ -8122,7 +8129,6 @@ test("unit_tests") { # Non-android deps for "unit_tests" target. deps += [ "../browser/screen_ai:screen_ai_install_state", diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index 48655e4a29fc7..5ac6345a8f52b 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,10 +9,10 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index d905ce52d71b2e2a4163fda68f584b3a3f305716..57a450929264e3f7592b5eab5126d176dbff8a58 100644 +index d9be4e90a7c43a8a7dec829304856daddcc58dc0..f6f98ea0824896e7e0a27e672480660b0671d146 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -9644,6 +9644,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -9652,6 +9652,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -21,10 +21,10 @@ index d905ce52d71b2e2a4163fda68f584b3a3f305716..57a450929264e3f7592b5eab5126d176 &no_javascript_access); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index f80d8ecc385df65610608ee78d6b09f2824a53fb..17009e944d6f3d23c721be521bf7843dbce1320e 100644 +index 2896b2b15d9c22e27c37719867c20213b9983984..83c5918c1bc0417ed3c744828b9f93e7c00d22c3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5095,6 +5095,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5101,6 +5101,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( SetPartitionedPopinOpenerOnNewWindowIfNeeded(new_contents_impl, params, opener); @@ -37,7 +37,7 @@ index f80d8ecc385df65610608ee78d6b09f2824a53fb..17009e944d6f3d23c721be521bf7843d // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses -@@ -5136,12 +5142,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5142,12 +5148,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( AddWebContentsDestructionObserver(new_contents_impl); } diff --git a/patches/chromium/chore_partial_revert_of.patch b/patches/chromium/chore_partial_revert_of.patch index 14d92201cd07f..a2ece673822c0 100644 --- a/patches/chromium/chore_partial_revert_of.patch +++ b/patches/chromium/chore_partial_revert_of.patch @@ -14,10 +14,10 @@ track down the source of this problem & figure out if we can fix it by changing something in Electron. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 3b43dcfef507b98f8af6f0c14c4fa9af0a7b9593..9c301542a54c3aeb5ec7ce408e312b48b33d22e3 100644 +index 5465e9d42a5c4eed3b6f819418a7b0ab92c7eae2..54ff9675fef009c5bb79becb59146436508ad835 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5014,7 +5014,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -5020,7 +5020,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( : IsGuest(); // While some guest types do not have a guest SiteInstance, the ones that // don't all override WebContents creation above. diff --git a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch index 7a9f3b4f86e1b..27a9ad01bd17b 100644 --- a/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch +++ b/patches/chromium/chore_provide_iswebcontentscreationoverridden_with_full_params.patch @@ -218,10 +218,10 @@ index c6838c83ef971b88769b1f3fba8095025ae25464..2da6a4e08340e72ba7de5d03444c2f17 content::WebContents* AddNewContents( content::WebContents* source, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 09c1b87b58fb2bf99c2a3b81b300e0cc1b9a0115..a1ba9ea494a6b2da2b3fdfeb02491d37b3da9074 100644 +index d13c38de7a2e69565020ab92c874bf1289d63833..d7ec62c6761bcc8d4bb5889e873e22c4a27e7f17 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4977,8 +4977,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( +@@ -4983,8 +4983,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( // TODO(crbug.com/40202416): Support a way for MPArch guests to support this. if (delegate_ && delegate_->IsWebContentsCreationOverridden( source_site_instance, params.window_container_type, diff --git a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch index 725961ddd8861..aab52cd7e99d4 100644 --- a/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch +++ b/patches/chromium/fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch @@ -87,10 +87,10 @@ index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac4 // The view with active text input state, i.e., a focused element. // It will be nullptr if no such view exists. Note that the active view diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index a793aa22e549e0fa8fee4665775761db97496eb2..3b43dcfef507b98f8af6f0c14c4fa9af0a7b9593 100644 +index 26f41d34766899e81e32c319aad00172ffdc5fca..5465e9d42a5c4eed3b6f819418a7b0ab92c7eae2 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9843,7 +9843,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( +@@ -9856,7 +9856,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( "WebContentsImpl::OnFocusedElementChangedInFrame", "render_frame_host", frame); RenderWidgetHostViewBase* root_view = diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 63d244ca9ba91..06b952870f56d 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,10 +10,10 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 6372615eecc862325e88ebb9b189d380459163c7..38e360457b2fcb658db8c94fe26169a3f61a9cbc 100644 +index 80a6556e7b9bebc40ec61cb8b250b931b5d02bc2..b6a527a05d1f1f4286a72ec0efefcfe81288bc7c 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -808,8 +808,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( +@@ -809,8 +809,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( // TODO(crbug.com/40092527): Consider adding a separate boolean that // tracks this instead of piggybacking `origin_calculation_debug_info`. if (renderer_side_origin.opaque() && diff --git a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch index 0333718125f9e..fe633c675f5b1 100644 --- a/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch +++ b/patches/chromium/refactor_expose_cursor_changes_to_the_webcontentsobserver.patch @@ -44,10 +44,10 @@ index 20fcda4eb20459b69247003c51c2a3ed37c7b1e8..9bcab4e1e8a0fa429488555f4f7bd1c5 void RenderWidgetHostImpl::ShowContextMenuAtPoint( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 17009e944d6f3d23c721be521bf7843dbce1320e..09c1b87b58fb2bf99c2a3b81b300e0cc1b9a0115 100644 +index 83c5918c1bc0417ed3c744828b9f93e7c00d22c3..d13c38de7a2e69565020ab92c874bf1289d63833 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -5847,6 +5847,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { +@@ -5853,6 +5853,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { return text_input_manager_.get(); } @@ -60,10 +60,10 @@ index 17009e944d6f3d23c721be521bf7843dbce1320e..09c1b87b58fb2bf99c2a3b81b300e0cc RenderWidgetHostImpl* render_widget_host) { return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index b3b274099fbe0df8892e686236e9a1f759c4d0e8..2baa178ed1d147c92de0ed32e94d9e2dbfc98310 100644 +index 8fa21e99ab8624817fead79c1b032af9b499c573..17d7fc9c29b8a9f57145fcf920c8bbd913da124b 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h -@@ -1178,6 +1178,7 @@ class CONTENT_EXPORT WebContentsImpl +@@ -1186,6 +1186,7 @@ class CONTENT_EXPORT WebContentsImpl void SendScreenRects() override; void SendActiveState(bool active) override; TextInputManager* GetTextInputManager() override; @@ -72,7 +72,7 @@ index b3b274099fbe0df8892e686236e9a1f759c4d0e8..2baa178ed1d147c92de0ed32e94d9e2d RenderWidgetHostImpl* render_widget_host) override; bool IsShowingContextMenuOnPage() const override; diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h -index 9f9f4cc755886236da2f10516b119643fca9c933..cf263ec4bf7cc5443b5cca5b2d44d3a66f2a405b 100644 +index e06a02965a79f4bd0047d33262e2e9511e62fcce..9cf1f30f59d4ab49441ca633d6fa1b784268ca32 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h @@ -37,6 +37,7 @@ @@ -83,7 +83,7 @@ index 9f9f4cc755886236da2f10516b119643fca9c933..cf263ec4bf7cc5443b5cca5b2d44d3a6 #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h" -@@ -642,6 +643,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { +@@ -646,6 +647,9 @@ class CONTENT_EXPORT WebContentsObserver : public base::CheckedObserver { // Invoked when the primary main frame changes size. virtual void PrimaryMainFrameWasResized(bool width_changed) {} diff --git a/patches/chromium/refactor_expose_file_system_access_blocklist.patch b/patches/chromium/refactor_expose_file_system_access_blocklist.patch index 275fb4db72bf3..6a6e599c4dba1 100644 --- a/patches/chromium/refactor_expose_file_system_access_blocklist.patch +++ b/patches/chromium/refactor_expose_file_system_access_blocklist.patch @@ -8,7 +8,7 @@ it in Electron and prevent drift from Chrome's blocklist. We should look for a w to upstream this change to Chrome. diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc -index f08ff195ede9980cd0f491053510ed09c1b6c58a..2bdfcc05ef92c1983a9517deea98ed589ac0a065 100644 +index 6720c3f12d4ff86bd9c5262d3b0b5e42065ce506..c5081166876717cac04159855b690a24e2424ea8 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc @@ -45,7 +45,6 @@ @@ -153,7 +153,7 @@ index f08ff195ede9980cd0f491053510ed09c1b6c58a..2bdfcc05ef92c1983a9517deea98ed58 // Describes a rule for blocking a directory, which can be constructed // dynamically (based on state) or statically (from kBlockedPaths). diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h -index 68a712cb272224d7a75ff3f512e45ed82eab5a86..d46119d9a7a32c9dc0c4844da73c63b42af9f7a8 100644 +index ce16f32c9ec1280c1d68942dcaf392c21f9e7963..1e0f372a25b49babb33acc05ee86f475684b0441 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h @@ -21,7 +21,7 @@ diff --git a/patches/chromium/refactor_unfilter_unresponsive_events.patch b/patches/chromium/refactor_unfilter_unresponsive_events.patch index e2db2163f2e0c..60d9345a1b810 100644 --- a/patches/chromium/refactor_unfilter_unresponsive_events.patch +++ b/patches/chromium/refactor_unfilter_unresponsive_events.patch @@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be accessed from our JS event. The filtering is moved into Electron's code. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 9c301542a54c3aeb5ec7ce408e312b48b33d22e3..d41390bf10906e6998b2c66476032541bfb97141 100644 +index 54ff9675fef009c5bb79becb59146436508ad835..aebd3b4fd597db7b02214dd61708711dc4ad75f3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -9980,25 +9980,13 @@ void WebContentsImpl::RendererUnresponsive( +@@ -9993,25 +9993,13 @@ void WebContentsImpl::RendererUnresponsive( base::RepeatingClosure hang_monitor_restarter) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", "render_widget_host", render_widget_host); diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index f4ac29b0edba8..f138ae5d41c96 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -9,10 +9,10 @@ is needed for OSR. Originally landed in https://github.com/electron/libchromiumcontent/pull/226. diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index a1ba9ea494a6b2da2b3fdfeb02491d37b3da9074..59fb6d36a047c2ce43dd4de3c3c4492275d5f0cb 100644 +index d7ec62c6761bcc8d4bb5889e873e22c4a27e7f17..ac8aa81e213689a612d322fa75e81798ae1010e6 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -3916,6 +3916,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3922,6 +3922,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, params.main_frame_name, GetOpener(), primary_main_frame_policy, base::UnguessableToken::Create()); @@ -26,7 +26,7 @@ index a1ba9ea494a6b2da2b3fdfeb02491d37b3da9074..59fb6d36a047c2ce43dd4de3c3c44922 std::unique_ptr delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -3926,6 +3933,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, +@@ -3932,6 +3939,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, view_ = CreateWebContentsView(this, std::move(delegate), &render_view_host_delegate_view_); } diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index fae5f9ef2e997..44dab18bf4ba5 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,10 +15,10 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 57a450929264e3f7592b5eab5126d176dbff8a58..6372615eecc862325e88ebb9b189d380459163c7 100644 +index f6f98ea0824896e7e0a27e672480660b0671d146..80a6556e7b9bebc40ec61cb8b250b931b5d02bc2 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc -@@ -8754,6 +8754,17 @@ void RenderFrameHostImpl::EnterFullscreen( +@@ -8762,6 +8762,17 @@ void RenderFrameHostImpl::EnterFullscreen( } } @@ -37,10 +37,10 @@ index 57a450929264e3f7592b5eab5126d176dbff8a58..6372615eecc862325e88ebb9b189d380 if (had_fullscreen_token && !GetView()->HasFocus()) GetView()->Focus(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 59fb6d36a047c2ce43dd4de3c3c4492275d5f0cb..a793aa22e549e0fa8fee4665775761db97496eb2 100644 +index ac8aa81e213689a612d322fa75e81798ae1010e6..26f41d34766899e81e32c319aad00172ffdc5fca 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4191,21 +4191,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( +@@ -4197,21 +4197,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"), "WebContentsImpl::PreHandleKeyboardEvent"); @@ -78,7 +78,7 @@ index 59fb6d36a047c2ce43dd4de3c3c4492275d5f0cb..a793aa22e549e0fa8fee4665775761db } bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { -@@ -4364,7 +4368,7 @@ void WebContentsImpl::EnterFullscreenMode( +@@ -4370,7 +4374,7 @@ void WebContentsImpl::EnterFullscreenMode( OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode"); DCHECK(CanEnterFullscreenMode(requesting_frame)); DCHECK(requesting_frame->IsActive()); From 5a4ef1cc33a9493cc769b966220cb0c4a5b6fe63 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:24:43 -0500 Subject: [PATCH 162/339] refactor: make node Buffers more friendly to `base::span` / `std::span` (#46778) * refactor: add electron::Buffer namespace; move the Buffer as_byte_span() into it Co-authored-by: Charles Kerr * feat: add electron::Buffer::Copy() a span-friendly version of node::Buffer::Copy() Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_base_window.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_data_pipe_holder.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_safe_storage.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_clipboard.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in osr_converter.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_native_image.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in net_converter.cc Co-authored-by: Charles Kerr * refactor: use electron::Buffer::Copy() in electron_api_web_contents.cc Co-authored-by: Charles Kerr * refactor: make NewEmptyBuffer() return a Local Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_base_window.cc | 12 ++-- .../api/electron_api_data_pipe_holder.cc | 4 +- .../browser/api/electron_api_safe_storage.cc | 4 +- .../browser/api/electron_api_web_contents.cc | 4 +- shell/common/api/electron_api_clipboard.cc | 4 +- shell/common/api/electron_api_native_image.cc | 71 +++++++++---------- shell/common/gin_converters/net_converter.cc | 7 +- shell/common/gin_converters/osr_converter.cc | 10 ++- shell/common/node_util.cc | 35 ++++++--- shell/common/node_util.h | 14 +++- 10 files changed, 93 insertions(+), 72 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index a7983dd39c8bc..04b7651f3a0bb 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -31,6 +31,7 @@ #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/gin_helper/persistent_dictionary.h" #include "shell/common/node_includes.h" +#include "shell/common/node_util.h" #include "shell/common/options_switches.h" #if defined(TOOLKIT_VIEWS) @@ -72,8 +73,9 @@ namespace { #if !BUILDFLAG(IS_MAC) // Converts binary data to Buffer. -v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { - auto buffer = node::Buffer::Copy(isolate, static_cast(val), size); +v8::Local ToBuffer(v8::Isolate* isolate, + const base::span val) { + auto buffer = electron::Buffer::Copy(isolate, val); if (buffer.IsEmpty()) return v8::Null(isolate); else @@ -348,8 +350,8 @@ void BaseWindow::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); messages_callback_map_[message].Run( - ToBuffer(isolate, static_cast(&w_param), sizeof(WPARAM)), - ToBuffer(isolate, static_cast(&l_param), sizeof(LPARAM))); + ToBuffer(isolate, base::byte_span_from_ref(w_param)), + ToBuffer(isolate, base::byte_span_from_ref(l_param))); } } #endif @@ -786,7 +788,7 @@ v8::Local BaseWindow::GetNativeWindowHandle() { // https://chromium-review.googlesource.com/c/chromium/src/+/1253094/ has // landed NativeWindowHandle handle = window_->GetNativeWindowHandle(); - return ToBuffer(isolate(), &handle, sizeof(handle)); + return ToBuffer(isolate(), base::byte_span_from_ref(handle)); } #endif diff --git a/shell/browser/api/electron_api_data_pipe_holder.cc b/shell/browser/api/electron_api_data_pipe_holder.cc index f2cc6a9f9bb3d..862c56217bf2a 100644 --- a/shell/browser/api/electron_api_data_pipe_holder.cc +++ b/shell/browser/api/electron_api_data_pipe_holder.cc @@ -17,6 +17,7 @@ #include "net/base/net_errors.h" #include "shell/common/gin_helper/promise.h" #include "shell/common/key_weak_map.h" +#include "shell/common/node_util.h" #include "shell/common/node_includes.h" @@ -114,8 +115,7 @@ class DataPipeReader { // inside the sandbox v8::HandleScope handle_scope(promise_.isolate()); v8::Local buffer = - node::Buffer::Copy(promise_.isolate(), &buffer_.front(), buffer_.size()) - .ToLocalChecked(); + electron::Buffer::Copy(promise_.isolate(), buffer_).ToLocalChecked(); promise_.Resolve(buffer); // Destroy data pipe. diff --git a/shell/browser/api/electron_api_safe_storage.cc b/shell/browser/api/electron_api_safe_storage.cc index f9ecdd0734fbb..e815ec06ac18a 100644 --- a/shell/browser/api/electron_api_safe_storage.cc +++ b/shell/browser/api/electron_api_safe_storage.cc @@ -11,6 +11,7 @@ #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_includes.h" +#include "shell/common/node_util.h" namespace { @@ -72,8 +73,7 @@ v8::Local EncryptString(v8::Isolate* isolate, return {}; } - return node::Buffer::Copy(isolate, ciphertext.c_str(), ciphertext.size()) - .ToLocalChecked(); + return electron::Buffer::Copy(isolate, ciphertext).ToLocalChecked(); } std::string DecryptString(v8::Isolate* isolate, v8::Local buffer) { diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index bf5f6ec4b44b2..9a3405834253b 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2979,9 +2979,7 @@ void OnPDFCreated(gin_helper::Promise> promise, v8::Local::New(isolate, promise.GetContext())); v8::Local buffer = - node::Buffer::Copy(isolate, reinterpret_cast(data->front()), - data->size()) - .ToLocalChecked(); + electron::Buffer::Copy(isolate, *data).ToLocalChecked(); promise.Resolve(buffer); } diff --git a/shell/common/api/electron_api_clipboard.cc b/shell/common/api/electron_api_clipboard.cc index aad1d4eece441..84f9dd2a6cd7d 100644 --- a/shell/common/api/electron_api_clipboard.cc +++ b/shell/common/api/electron_api_clipboard.cc @@ -13,6 +13,7 @@ #include "shell/common/gin_converters/image_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_includes.h" +#include "shell/common/node_util.h" #include "shell/common/process_util.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_format_type.h" @@ -99,8 +100,7 @@ std::string Clipboard::Read(const std::string& format_string) { v8::Local Clipboard::ReadBuffer(const std::string& format_string, gin_helper::Arguments* args) { std::string data = Read(format_string); - return node::Buffer::Copy(args->isolate(), data.data(), data.length()) - .ToLocalChecked(); + return electron::Buffer::Copy(args->isolate(), data).ToLocalChecked(); } void Clipboard::WriteBuffer(const std::string& format, diff --git a/shell/common/api/electron_api_native_image.cc b/shell/common/api/electron_api_native_image.cc index d16023390d4ad..da34a3158c3fe 100644 --- a/shell/common/api/electron_api_native_image.cc +++ b/shell/common/api/electron_api_native_image.cc @@ -122,6 +122,10 @@ base::win::ScopedGDIObject ReadICOFromPath(int size, } #endif +[[nodiscard]] v8::Local NewEmptyBuffer(v8::Isolate* isolate) { + return node::Buffer::New(isolate, 0).ToLocalChecked(); +} + } // namespace NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image) @@ -226,57 +230,48 @@ HICON NativeImage::GetHICON(int size) { #endif v8::Local NativeImage::ToPNG(gin::Arguments* args) { + v8::Isolate* const isolate = args->isolate(); float scale_factor = GetScaleFactorFromOptions(args); if (scale_factor == 1.0f) { // Use raw 1x PNG bytes when available - scoped_refptr png = image_.As1xPNGBytes(); - if (png->size() > 0) { - const char* data = reinterpret_cast(png->front()); - size_t size = png->size(); - return node::Buffer::Copy(args->isolate(), data, size).ToLocalChecked(); - } + const scoped_refptr png = image_.As1xPNGBytes(); + const base::span png_span = *png; + if (!png_span.empty()) + return electron::Buffer::Copy(isolate, png_span).ToLocalChecked(); } const SkBitmap bitmap = image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap(); - std::optional> encoded = + const std::optional> encoded = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false); if (!encoded.has_value()) - return node::Buffer::New(args->isolate(), 0).ToLocalChecked(); - const char* data = reinterpret_cast(encoded->data()); - size_t size = encoded->size(); - return node::Buffer::Copy(args->isolate(), data, size).ToLocalChecked(); + return NewEmptyBuffer(isolate); + + return electron::Buffer::Copy(isolate, *encoded).ToLocalChecked(); } v8::Local NativeImage::ToBitmap(gin::Arguments* args) { - float scale_factor = GetScaleFactorFromOptions(args); + v8::Isolate* const isolate = args->isolate(); - const SkBitmap bitmap = - image_.AsImageSkia().GetRepresentation(scale_factor).GetBitmap(); + const float scale = GetScaleFactorFromOptions(args); + const auto src = image_.AsImageSkia().GetRepresentation(scale).GetBitmap(); - SkImageInfo info = - SkImageInfo::MakeN32Premul(bitmap.width(), bitmap.height()); + const auto dst_info = SkImageInfo::MakeN32Premul(src.dimensions()); + const size_t dst_n_bytes = dst_info.computeMinByteSize(); + auto dst_buf = v8::ArrayBuffer::New(isolate, dst_n_bytes); - auto array_buffer = - v8::ArrayBuffer::New(args->isolate(), info.computeMinByteSize()); - if (bitmap.readPixels(info, array_buffer->Data(), info.minRowBytes(), 0, 0)) { - return node::Buffer::New(args->isolate(), array_buffer, 0, - info.computeMinByteSize()) - .ToLocalChecked(); - } - return node::Buffer::New(args->isolate(), 0).ToLocalChecked(); + if (!src.readPixels(dst_info, dst_buf->Data(), dst_info.minRowBytes(), 0, 0)) + return NewEmptyBuffer(isolate); + return node::Buffer::New(isolate, dst_buf, 0, dst_n_bytes).ToLocalChecked(); } v8::Local NativeImage::ToJPEG(v8::Isolate* isolate, int quality) { - std::optional> encoded_image = + const std::optional> encoded_image = gfx::JPEG1xEncodedDataFromImage(image_, quality); - if (!encoded_image.has_value()) - return node::Buffer::New(isolate, 0).ToLocalChecked(); - return node::Buffer::Copy( - isolate, reinterpret_cast(&encoded_image->front()), - encoded_image->size()) - .ToLocalChecked(); + if (!encoded_image) + return NewEmptyBuffer(isolate); + return electron::Buffer::Copy(isolate, *encoded_image).ToLocalChecked(); } std::string NativeImage::ToDataURL(gin::Arguments* args) { @@ -301,17 +296,17 @@ v8::Local NativeImage::GetBitmap(gin::Arguments* args) { v8::Local NativeImage::GetNativeHandle( gin_helper::ErrorThrower thrower) { + v8::Isolate* const isolate = thrower.isolate(); #if BUILDFLAG(IS_MAC) if (IsEmpty()) - return node::Buffer::New(thrower.isolate(), 0).ToLocalChecked(); + return NewEmptyBuffer(isolate); NSImage* ptr = image_.AsNSImage(); - return node::Buffer::Copy(thrower.isolate(), reinterpret_cast(ptr), - sizeof(void*)) + return electron::Buffer::Copy(isolate, base::byte_span_from_ref(ptr)) .ToLocalChecked(); #else thrower.ThrowError("Not implemented"); - return v8::Undefined(thrower.isolate()); + return v8::Undefined(isolate); #endif } @@ -402,7 +397,7 @@ void NativeImage::AddRepresentation(const gin_helper::Dictionary& options) { GURL url; if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) { skia_rep_added = electron::util::AddImageSkiaRepFromBuffer( - &image_skia, electron::util::as_byte_span(buffer), width, height, + &image_skia, electron::Buffer::as_byte_span(buffer), width, height, scale_factor); } else if (options.Get("dataURL", &url)) { std::string mime_type, charset, data; @@ -511,7 +506,7 @@ gin::Handle NativeImage::CreateFromBitmap( auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType); auto size_bytes = info.computeMinByteSize(); - const auto buffer_data = electron::util::as_byte_span(buffer); + const auto buffer_data = electron::Buffer::as_byte_span(buffer); if (size_bytes != buffer_data.size()) { thrower.ThrowError("invalid buffer size"); return {}; @@ -552,7 +547,7 @@ gin::Handle NativeImage::CreateFromBuffer( gfx::ImageSkia image_skia; electron::util::AddImageSkiaRepFromBuffer( - &image_skia, electron::util::as_byte_span(buffer), width, height, + &image_skia, electron::Buffer::as_byte_span(buffer), width, height, scale_factor); return Create(args->isolate(), gfx::Image(image_skia)); } diff --git a/shell/common/gin_converters/net_converter.cc b/shell/common/gin_converters/net_converter.cc index bf67315c5b09c..0bf04cff8d135 100644 --- a/shell/common/gin_converters/net_converter.cc +++ b/shell/common/gin_converters/net_converter.cc @@ -34,6 +34,7 @@ #include "shell/common/gin_converters/value_converter.h" #include "shell/common/gin_helper/promise.h" #include "shell/common/node_includes.h" +#include "shell/common/node_util.h" #include "shell/common/v8_util.h" namespace gin { @@ -524,11 +525,11 @@ v8::Local Converter::ToV8( } case network::mojom::DataElement::Tag::kBytes: { upload_data.Set("type", "rawData"); - const auto& bytes = element.As().bytes(); - const char* data = reinterpret_cast(bytes.data()); upload_data.Set( "bytes", - node::Buffer::Copy(isolate, data, bytes.size()).ToLocalChecked()); + electron::Buffer::Copy( + isolate, element.As().bytes()) + .ToLocalChecked()); break; } case network::mojom::DataElement::Tag::kDataPipe: { diff --git a/shell/common/gin_converters/osr_converter.cc b/shell/common/gin_converters/osr_converter.cc index 7ccc1d71ec883..afa868ae8a526 100644 --- a/shell/common/gin_converters/osr_converter.cc +++ b/shell/common/gin_converters/osr_converter.cc @@ -111,12 +111,10 @@ v8::Local Converter::ToV8( dict.Set("metadata", ConvertToV8(isolate, metadata)); #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) - auto handle_buf = node::Buffer::Copy( - isolate, - reinterpret_cast( - const_cast(&val.shared_texture_handle)), - sizeof(val.shared_texture_handle)); - dict.Set("sharedTextureHandle", handle_buf.ToLocalChecked()); + dict.Set("sharedTextureHandle", + electron::Buffer::Copy( + isolate, base::byte_span_from_ref(val.shared_texture_handle)) + .ToLocalChecked()); #elif BUILDFLAG(IS_LINUX) auto v8_planes = base::ToVector(val.planes, [isolate](const auto& plane) { gin::Dictionary v8_plane(isolate, v8::Object::New(isolate)); diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index 6d18f077b61d4..055670cc4ddfb 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -75,16 +75,6 @@ void EmitWarning(v8::Isolate* isolate, emit_warning.Run(warning_msg, warning_type, ""); } -// SAFETY: There is no node::Buffer API that passes the UNSAFE_BUFFER_USAGE -// test, so let's isolate the unsafe API use into this function. Instead of -// calling `Buffer::Data()` and `Buffer::Length()` directly, the rest of our -// code should prefer to use spans returned by this function. -base::span as_byte_span(v8::Local node_buffer) { - auto* data = reinterpret_cast(node::Buffer::Data(node_buffer)); - const auto size = node::Buffer::Length(node_buffer); - return UNSAFE_BUFFERS(base::span{data, size}); -} - node::Environment* CreateEnvironment(v8::Isolate* isolate, node::IsolateData* isolate_data, v8::Local context, @@ -133,3 +123,28 @@ node::Environment* CreateEnvironment(v8::Isolate* isolate, } } // namespace electron::util + +namespace electron::Buffer { + +// SAFETY: There is no node::Buffer API that passes the UNSAFE_BUFFER_USAGE +// test, so let's isolate the unsafe API use into this function. Instead of +// calling `Buffer::Data()` and `Buffer::Length()` directly, the rest of our +// code should prefer to use spans returned by this function. +base::span as_byte_span(v8::Local node_buffer) { + auto* data = reinterpret_cast(node::Buffer::Data(node_buffer)); + const auto size = node::Buffer::Length(node_buffer); + return UNSAFE_BUFFERS(base::span{data, size}); +} + +v8::MaybeLocal Copy(v8::Isolate* isolate, + const base::span data) { + // SAFETY: span-friendly version of node::Buffer::Copy() + return UNSAFE_BUFFERS(node::Buffer::Copy(isolate, data.data(), data.size())); +} + +v8::MaybeLocal Copy(v8::Isolate* isolate, + const base::span data) { + return Copy(isolate, base::as_chars(data)); +} + +} // namespace electron::Buffer diff --git a/shell/common/node_util.h b/shell/common/node_util.h index c379d70e8ab9d..36fcfdf940ba4 100644 --- a/shell/common/node_util.h +++ b/shell/common/node_util.h @@ -53,11 +53,23 @@ node::Environment* CreateEnvironment(v8::Isolate* isolate, node::EnvironmentFlags::Flags env_flags, std::string_view process_type = ""); +} // namespace electron::util + +namespace electron::Buffer { + // Convenience function to view a Node buffer's data as a base::span(). // Analogous to base::as_byte_span() [[nodiscard]] base::span as_byte_span( v8::Local node_buffer); -} // namespace electron::util +// span-friendly version of node::Buffer::Copy() +[[nodiscard]] v8::MaybeLocal Copy(v8::Isolate* isolate, + base::span data); + +// span-friendly version of node::Buffer::Copy() +[[nodiscard]] v8::MaybeLocal Copy(v8::Isolate* isolate, + base::span data); + +} // namespace electron::Buffer #endif // ELECTRON_SHELL_COMMON_NODE_UTIL_H_ From 08a4c333a336adf3d3c55366814823b8fdd13ee5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:30:19 -0400 Subject: [PATCH 163/339] fix: missing `HandleScope` in `RemoveFromParentChildWindows` (#46773) fix: missing HandleScope in RemoveFromParentChildWindows Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_base_window.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 04b7651f3a0bb..506350baa80e1 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -1163,8 +1163,10 @@ void BaseWindow::RemoveFromParentChildWindows() { if (parent_window_.IsEmpty()) return; + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + v8::HandleScope handle_scope(isolate); gin::Handle parent; - if (!gin::ConvertFromV8(isolate(), GetParentWindow(), &parent) || + if (!gin::ConvertFromV8(isolate, GetParentWindow(), &parent) || parent.IsEmpty()) { return; } From 02e4e109d5d4ebe376ff412e3f029c055543345a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:31:12 -0400 Subject: [PATCH 164/339] fix: vibrancy window border (#46771) fix: vibrancy window border (#46648) * fix: vibrancy window border * Use WidgetDelegate::OnWidgetInitialized instead Co-authored-by: Calvin --- shell/browser/native_window_mac.h | 1 + shell/browser/native_window_mac.mm | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 590ac6d8bb7cd..5420e14aecd7d 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -225,6 +225,7 @@ class NativeWindowMac : public NativeWindow, bool CanMaximize() const override; std::unique_ptr CreateNonClientFrameView( views::Widget* widget) override; + void OnWidgetInitialized() override; // ui::NativeThemeObserver: void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 8bb7d5b51fa43..a895f26446213 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1861,6 +1861,28 @@ int NonClientHitTest(const gfx::Point& point) override { return std::nullopt; } +void NativeWindowMac::OnWidgetInitialized() { + // |window_| is not yet assigned when this function is called, so we need to + // get the window from the widget. + NSWindow* window = widget()->GetNativeWindow().GetNativeNSWindow(); + + // In |NativeWidgetNSWindowBridge::InitCompositorView| in Chromium, + // translucent windows are assigned a clear background color. This causes + // undesirable side effects, such as a window with vibrancy losing its natural + // system border highlight. We undo that behavior here. + // + // Ref: + // https://source.chromium.org/chromium/chromium/src/+/main:components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm;l=1341-1342;drc=e7c8be576285195257b0813326b3bab154dc7e73 + if (!transparent()) { + if ([[window backgroundColor] isEqual:NSColor.clearColor]) { + [window setBackgroundColor:NSColor.windowBackgroundColor]; + } + if (![window isOpaque]) { + [window setOpaque:YES]; + } + } +} + // static std::unique_ptr NativeWindow::Create( const gin_helper::Dictionary& options, From 197444181de4c38a63e4a22d1d2111b980772f4d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:59:59 -0500 Subject: [PATCH 165/339] docs: Add ObjC/macOS tutorial (#46785) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Felix Rieseberg --- .../native-code-and-electron-objc-macos.md | 1152 +++++++++++++++++ 1 file changed, 1152 insertions(+) create mode 100644 docs/tutorial/native-code-and-electron-objc-macos.md diff --git a/docs/tutorial/native-code-and-electron-objc-macos.md b/docs/tutorial/native-code-and-electron-objc-macos.md new file mode 100644 index 0000000000000..7074acda10dc0 --- /dev/null +++ b/docs/tutorial/native-code-and-electron-objc-macos.md @@ -0,0 +1,1152 @@ +# Native Code and Electron: Objective-C (macOS) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for macOS using Objective-C, Objective-C++, and Cocoa frameworks. To illustrate how you can embed native macOS code in your Electron app, we'll be building a basic native macOS GUI (using AppKit) that communicates with Electron's JavaScript. + +Specifically, we'll be integrating with two macOS frameworks: + +* AppKit - The primary UI framework for macOS applications that provides components like windows, buttons, text fields, and more. +* Foundation - A framework that provides data management, file system interaction, and other essential services. + +This tutorial will be most useful to those who already have some familiarity with Objective-C and Cocoa development. You should understand basic concepts like delegates, NSObjects, and the target-action pattern commonly used in macOS development. + +> [!NOTE] +> If you're not already familiar with these concepts, Apple's [documentation on Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html) is an excellent starting point. + +## Requirements + +Just like our general introduction to Native Code and Electron, this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code on macOS. You'll need: + +* Xcode installed (available from the Mac App Store) +* Xcode Command Line Tools (can be installed by running `xcode-select --install` in Terminal) + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +my-native-objc-addon/ +├── binding.gyp +├── include/ +│ └── objc_code.h +├── js/ +│ └── index.js +├── package.json +└── src/ + ├── objc_addon.mm + └── objc_code.mm +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "objc-macos", + "version": "1.0.0", + "description": "A demo module that exposes Objective-C code to Electron", + "main": "js/index.js", + "author": "Your Name", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +For a macOS-specific addon using Objective-C, we need to modify our `binding.gyp` file to include the appropriate frameworks and compiler flags. We need to: + +1. Ensure our addon is only compiled on macOS +2. Include the necessary macOS frameworks (Foundation and AppKit) +3. Configure the compiler for Objective-C/C++ support + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "objc_addon", + "conditions": [ + ['OS=="mac"', { + "sources": [ + "src/objc_addon.mm", + "src/objc_code.mm" + ], + "include_dirs": [ + " +#include + +namespace objc_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); + +} // namespace objc_code +``` + +This header: + +* Includes a basic hello_world function from the general tutorial +* Adds a `hello_gui` function to create a native macOS GUI +* Defines callback types for Todo operations +* Provides setter functions for these callbacks + +## 4) Implementing the Objective-C Code + +Now, let's implement our Objective-C code in `src/objc_code.mm`. This is where we'll create our native macOS GUI using AppKit. + +We'll always add code to the bottom of our file. To make this tutorial easier to follow, we'll start with the basic structure and add features incrementally - step by step. + +### Setting Up the Basic Structure + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// More code to follow later... +``` + +This imports the required frameworks and defines our callback type. The static `g_todoAddedCallback` variable will store our JavaScript callback function. + +### Defining the Window Controller Interface + +At the bottom of `objc_code.mm`, add the following code to define our window controller class interface: + +```objc title='src/objc_code.mm' +// Previous code... + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// More code to follow later... +``` + +This declares our TodoWindowController class which will manage the window and UI components: + +* A text field (`NSTextField`) for entering todo text +* A date picker (`NSDatePicker`) for selecting the date +* An "Add" button (`NSButton`) +* A table view to display the todos (`NSTableView`) +* An array to store the todo items (`NSMutableArray`) + +### Implementing the Window Controller + +At the bottom of `objc_code.mm`, add the following code to start implementing the window controller with an initialization method: + +```objc title='src/objc_code.mm' +// Previous code... + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +// More code to follow later... +``` + +This initializes our controller. We're not using a nib file, so we pass an empty string to `initWithWindowNibName`. We create an empty array to store our todos and call the `setupWindow` method, which we'll implement next. + +At this point, our full file looks like this: + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +// More code to follow later... +``` + +### Creating the Window and Basic UI + +Now, we'll add a `setupWindow()` method. This method will look a little overwhelming on first sight, but it really just instantiates a number of UI controls and then adds them to our window. + +```objc title='src/objc_code.mm' +// Previous code... + +- (void)setupWindow { + // Create a window + NSRect frame = NSMakeRect(0, 0, 400, 300); + NSWindow *window = [[NSWindow alloc] initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@"Todo List"]; + [window center]; + self.window = window; + + // Set up the content view with auto layout + NSView *contentView = [window contentView]; + + // Create text field + _textField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 260, 200, 24)]; + [_textField setPlaceholderString:@"Enter a todo..."]; + [contentView addSubview:_textField]; + + // Create date picker + _datePicker = [[NSDatePicker alloc] initWithFrame:NSMakeRect(230, 260, 100, 24)]; + [_datePicker setDatePickerStyle:NSDatePickerStyleTextField]; + [_datePicker setDatePickerElements:NSDatePickerElementFlagYearMonthDay]; + [contentView addSubview:_datePicker]; + + // Create add button + _addButton = [[NSButton alloc] initWithFrame:NSMakeRect(340, 260, 40, 24)]; + [_addButton setTitle:@"Add"]; + [_addButton setBezelStyle:NSBezelStyleRounded]; + [_addButton setTarget:self]; + [_addButton setAction:@selector(addTodo:)]; + [contentView addSubview:_addButton]; + + // More UI elements to follow in the next step... +} + +// More code to follow later... +``` + +This method: + +1. Creates a window with a title and standard window controls +2. Centers the window on the screen +3. Creates a text field for entering todo text +4. Adds a date picker configured to show only date (no time) +5. Adds an "Add" button that will call the `addTodo:` method when clicked + +We're still missing the table view to display our todos. Let's add that to the bottom of our `setupWindow()` method, right where it says `More UI elements to follow in the next step...` in the code above. + +```objc title='src/objc_code.mm' +// Previous code... + +- (void)setupWindow { + // Previous setupWindow() code... + + // Create a scroll view for the table + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 360, 230)]; + [scrollView setBorderType:NSBezelBorder]; + [scrollView setHasVerticalScroller:YES]; + [contentView addSubview:scrollView]; + + // Create table view + _tableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 360, 230)]; + + // Add a column for the todo text + NSTableColumn *textColumn = [[NSTableColumn alloc] initWithIdentifier:@"text"]; + [textColumn setWidth:240]; + [textColumn setTitle:@"Todo"]; + [_tableView addTableColumn:textColumn]; + + // Add a column for the date + NSTableColumn *dateColumn = [[NSTableColumn alloc] initWithIdentifier:@"date"]; + [dateColumn setWidth:100]; + [dateColumn setTitle:@"Date"]; + [_tableView addTableColumn:dateColumn]; + + // Set the table's delegate and data source + [_tableView setDataSource:self]; + [_tableView setDelegate:self]; + + // Add the table to the scroll view + [scrollView setDocumentView:_tableView]; +} + +// More code to follow later... +``` + +This extends our `setupWindow` method to: + +1. Create a scroll view to contain the table +2. Create a table view with two columns: one for the todo text and one for the date +3. Set up the data source and delegate to this class +4. Add the table to the scroll view + +This concludes the UI elements in `setupWindow()`, so we can now move on to business logic. + +### Implementing the "Add Todo" Functionality + +Next, let's implement the `addTodo:` method to handle adding new todos. We'll need to do two sets of operations here: First, we need to handle our native UI and perform operations like getting the data out of our UI elements or resetting them. Then, we also need notify our JavaScript world about the newly added todo. + +In the interest of keeping this tutorial easy to follow, we'll do this in two steps. + +```objc title='src/objc_code.mm' +// Previous code... + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + NSDate *date = [_datePicker dateValue]; + + // Create a unique ID + NSUUID *uuid = [NSUUID UUID]; + + // Create a dictionary to store the todo + NSDictionary *todo = @{ + @"id": [uuid UUIDString], + @"text": text, + @"date": date + }; + + // Add to our array + [_todos addObject:todo]; + + // Reload the table + [_tableView reloadData]; + + // Reset the text field + [_textField setStringValue:@""]; + + // Next, we'll notify our JavaScript world here... + } +} + +// More code to follow later... +``` + +This method: + +1. Gets the text from the text field +2. If the text is not empty, creates a new todo with a unique ID, the entered text, and the selected date +3. Adds the todo to our array +4. Reloads the table to show the new todo +5. Clears the text field for the next entry + +Now, let's extend the `addTodo:` method to notify JavaScript when a todo is added. We'll do that at the bottom of the method, where it currently reads "Next, we'll notify our JavaScript world here...". + +```objc title='src/objc_code.mm' +// Previous code... + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + // Previous addTodo() code... + + // Call the callback if it exists + if (g_todoAddedCallback) { + // Convert the todo to JSON + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{ + @"id": [uuid UUIDString], + @"text": text, + @"date": @((NSTimeInterval)[date timeIntervalSince1970] * 1000) + } options:0 error:&error]; + + if (!error) { + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + std::string cppJsonString = [jsonString UTF8String]; + g_todoAddedCallback(cppJsonString); + } + } + } +} + +// More code to follow later... +``` + +This adds code to do a whole bunch of conversions (so that N-API can eventually turn this data into structures ready for V8 and the JavaScript world) - and then calls our JavaScript callback. Specifically, it does the following: + +1. Check if a callback function has been registered +2. Convert the todo to JSON format +3. Convert the date to milliseconds since epoch (JavaScript date format) +4. Convert the JSON to a C++ string +5. Call the callback function with the JSON string + +We're now done with our `addTodo:` method and can move on to the next step: The data source for the Table View. + +### Implementing the Table View Data Source + +Let's implement the table view data source methods to display our todos: + +```objc title='src/objc_code.mm' +// Previous code... + +// NSTableViewDataSource methods +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return [_todos count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSDictionary *todo = _todos[row]; + NSString *identifier = [tableColumn identifier]; + + if ([identifier isEqualToString:@"text"]) { + return todo[@"text"]; + } else if ([identifier isEqualToString:@"date"]) { + NSDate *date = todo[@"date"]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateStyle:NSDateFormatterShortStyle]; + return [formatter stringFromDate:date]; + } + + return nil; +} + +@end + +// More code to follow later... +``` + +These methods: + +* Return the number of todos for the table view +* Provide the text or formatted date for each cell in the table + +### Implementing the C++ Functions + +Lastly, we need to implement the C++ namespace functions that were declared in our header file: + +```objc title='src/objc_code.mm' +// Previous code... + +namespace objc_code { + +std::string hello_world(const std::string& input) { + return "Hello from Objective-C! You said: " + input; +} + +void setTodoAddedCallback(TodoCallback callback) { + g_todoAddedCallback = callback; +} + +void hello_gui() { + // Create and run the GUI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // Create our window controller + TodoWindowController *windowController = [[TodoWindowController alloc] init]; + + // Show the window + [windowController showWindow:nil]; + + // Keep a reference to prevent it from being deallocated + // Note: in a real app, you'd store this reference more carefully + static TodoWindowController *staticController = nil; + staticController = windowController; + }); +} + +} // namespace objc_code +``` + +These functions: + +1. Implement the `hello_world` function that returns a greeting string +2. Provide a way to set the callback function for todo additions +3. Implement the `hello_gui` function that creates and shows our native UI +4. Lastly, we also keep a static reference to prevent the window controller from being deallocated + +Note that we're using GCD (Grand Central Dispatch) to dispatch to the main thread, which is required for UI operations. We're not dedicating more time to thread safety in this tutorial, but here's a quick reminder: In macOS/iOS, all UI updates must happen on the main thread. The main thread is the primary execution path where the application runs its event loop and processes user interface events. In our code, when JavaScript calls the `hello_gui()` function, the call might be coming from a Node.js worker thread, not the main thread. Using GCD, we safely redirect the window creation code to the main thread, ensuring proper UI behavior. + +This is a common pattern in macOS/iOS development - any code that touches the UI needs to be executed on the main thread, and GCD provides a clean way to ensure this happens. + +The final version of `objc_code.mm` looks like this: + +```objc title='src/objc_code.mm' +#import +#import +#import +#import +#import "../include/objc_code.h" + +using TodoCallback = std::function; + +static TodoCallback g_todoAddedCallback; + +// Forward declaration of our custom classes +@interface TodoWindowController : NSWindowController +@property (strong) NSTextField *textField; +@property (strong) NSDatePicker *datePicker; +@property (strong) NSButton *addButton; +@property (strong) NSTableView *tableView; +@property (strong) NSMutableArray *todos; +@end + +// Controller for the main window +@implementation TodoWindowController + +- (instancetype)init { + self = [super initWithWindowNibName:@""]; + if (self) { + // Create an array to store todos + _todos = [NSMutableArray array]; + [self setupWindow]; + } + return self; +} + +- (void)setupWindow { + // Create a window + NSRect frame = NSMakeRect(0, 0, 400, 300); + NSWindow *window = [[NSWindow alloc] initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable + backing:NSBackingStoreBuffered + defer:NO]; + [window setTitle:@"Todo List"]; + [window center]; + self.window = window; + + // Set up the content view with auto layout + NSView *contentView = [window contentView]; + + // Create text field + _textField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 260, 200, 24)]; + [_textField setPlaceholderString:@"Enter a todo..."]; + [contentView addSubview:_textField]; + + // Create date picker + _datePicker = [[NSDatePicker alloc] initWithFrame:NSMakeRect(230, 260, 100, 24)]; + [_datePicker setDatePickerStyle:NSDatePickerStyleTextField]; + [_datePicker setDatePickerElements:NSDatePickerElementFlagYearMonthDay]; + [contentView addSubview:_datePicker]; + + // Create add button + _addButton = [[NSButton alloc] initWithFrame:NSMakeRect(340, 260, 40, 24)]; + [_addButton setTitle:@"Add"]; + [_addButton setBezelStyle:NSBezelStyleRounded]; + [_addButton setTarget:self]; + [_addButton setAction:@selector(addTodo:)]; + [contentView addSubview:_addButton]; + + // Create a scroll view for the table + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 360, 230)]; + [scrollView setBorderType:NSBezelBorder]; + [scrollView setHasVerticalScroller:YES]; + [contentView addSubview:scrollView]; + + // Create table view + _tableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 360, 230)]; + + // Add a column for the todo text + NSTableColumn *textColumn = [[NSTableColumn alloc] initWithIdentifier:@"text"]; + [textColumn setWidth:240]; + [textColumn setTitle:@"Todo"]; + [_tableView addTableColumn:textColumn]; + + // Add a column for the date + NSTableColumn *dateColumn = [[NSTableColumn alloc] initWithIdentifier:@"date"]; + [dateColumn setWidth:100]; + [dateColumn setTitle:@"Date"]; + [_tableView addTableColumn:dateColumn]; + + // Set the table's delegate and data source + [_tableView setDataSource:self]; + [_tableView setDelegate:self]; + + // Add the table to the scroll view + [scrollView setDocumentView:_tableView]; +} + +// Action method for the Add button +- (void)addTodo:(id)sender { + NSString *text = [_textField stringValue]; + if ([text length] > 0) { + NSDate *date = [_datePicker dateValue]; + + // Create a unique ID + NSUUID *uuid = [NSUUID UUID]; + + // Create a dictionary to store the todo + NSDictionary *todo = @{ + @"id": [uuid UUIDString], + @"text": text, + @"date": date + }; + + // Add to our array + [_todos addObject:todo]; + + // Reload the table + [_tableView reloadData]; + + // Reset the text field + [_textField setStringValue:@""]; + + // Call the callback if it exists + if (g_todoAddedCallback) { + // Convert the todo to JSON + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{ + @"id": [uuid UUIDString], + @"text": text, + @"date": @((NSTimeInterval)[date timeIntervalSince1970] * 1000) + } options:0 error:&error]; + + if (!error) { + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + std::string cppJsonString = [jsonString UTF8String]; + g_todoAddedCallback(cppJsonString); + } + } + } +} + +// NSTableViewDataSource methods +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return [_todos count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + NSDictionary *todo = _todos[row]; + NSString *identifier = [tableColumn identifier]; + + if ([identifier isEqualToString:@"text"]) { + return todo[@"text"]; + } else if ([identifier isEqualToString:@"date"]) { + NSDate *date = todo[@"date"]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateStyle:NSDateFormatterShortStyle]; + return [formatter stringFromDate:date]; + } + + return nil; +} + +@end + +namespace objc_code { + +std::string hello_world(const std::string& input) { + return "Hello from Objective-C! You said: " + input; +} + +void setTodoAddedCallback(TodoCallback callback) { + g_todoAddedCallback = callback; +} + +void hello_gui() { + // Create and run the GUI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // Create our window controller + TodoWindowController *windowController = [[TodoWindowController alloc] init]; + + // Show the window + [windowController showWindow:nil]; + + // Keep a reference to prevent it from being deallocated + // Note: in a real app, you'd store this reference more carefully + static TodoWindowController *staticController = nil; + staticController = windowController; + }); +} + +} // namespace objc_code +``` + +## 5) Creating the Node.js Addon Bridge + +We now have working Objective-C code. To make sure it can be safely and properly called from the JavaScript world, we need to build a bridge between Objective-C and C++, which we can do with Objective-C++. We'll do that in `src/objc_addon.mm`. + +Bear with us: This bridge code always ends up being pretty verbose and might seem difficult to follow. As far as modern desktop development goes, it's fairly low-level, so be patient with yourself - it might take a little bit before the bridging really "clicks". + +### Basic Class Definition + +```objc title='src/objc_addon.mm' +#include +#include +#include "../include/objc_code.h" + +class ObjcAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "ObjcMacosAddon", { + InstanceMethod("helloWorld", &ObjcAddon::HelloWorld), + InstanceMethod("helloGui", &ObjcAddon::HelloGui), + InstanceMethod("on", &ObjcAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("ObjcMacosAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + ObjcAddon* addon; + }; + + // More code to follow later... + // Specifically, we'll add ObjcAddon here in the next step +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return ObjcAddon::Init(env, exports); +} + +NODE_API_MODULE(objc_addon, Init) +``` + +This code: + +1. Defines an ObjcAddon class that inherits from Napi::ObjectWrap +2. Creates a static Init method that registers our JavaScript methods +3. Defines a CallbackData structure for passing data between threads +4. Sets up the Node API module initialization + +### Constructor and Threadsafe Function Setup + +Next, let's implement the constructor that sets up our threadsafe callback mechanism: + +```objc title='src/objc_addon.mm' +ObjcAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "ObjcCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + objc_code::setTodoAddedCallback(makeCallback("todoAdded")); +} + +~ObjcAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } +} + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; +``` + +This code: + +* Sets up the constructor with member initialization +* Creates a threadsafe function using N-API, which allows safe callbacks from any thread +* Defines a lambda to create callback functions for different event types +* Registers the "todoAdded" callback with our Objective-C code +* Implements a destructor to clean up resources when the addon is destroyed + +The threadsafe function is important because UI events in Objective-C might happen on a different thread than the JavaScript event loop. This mechanism safely bridges those thread boundaries. + +### Implementing JavaScript Methods + +Finally, let's implement the methods that JavaScript will call: + +```objc title='src/objc_addon.mm' +Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = objc_code::hello_world(input); + + return Napi::String::New(env, result); +} + +void HelloGui(const Napi::CallbackInfo& info) { + objc_code::hello_gui(); +} + +Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +Let's take a look at what we've added in this step: + +* `HelloWorld()`: Takes a string input, calls our Objective-C function, and returns the result +* `HelloGui()`: A simple wrapper around the Objective-C `hello_gui` function +* `On`: Allows JavaScript to register event listeners that will be called when native events occur + +The `On` method is particularly important as it creates the event system that our JavaScript code will use to receive notifications from the native UI. + +Together, these three components form a complete bridge between our Objective-C code and the JavaScript world, allowing bidirectional communication. Here's what the finished file should look like: + +```objc title='src/objc_addon.mm' +#include +#include +#include "../include/objc_code.h" + +class ObjcAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "ObjcMacosAddon", { + InstanceMethod("helloWorld", &ObjcAddon::HelloWorld), + InstanceMethod("helloGui", &ObjcAddon::HelloGui), + InstanceMethod("on", &ObjcAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("ObjcMacosAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + ObjcAddon* addon; + }; + + ObjcAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "ObjcCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks + auto makeCallback = [this](const std::string& eventType) { + return [this, eventType](const std::string& payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + payload, + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + objc_code::setTodoAddedCallback(makeCallback("todoAdded")); + } + + ~ObjcAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = objc_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo& info) { + objc_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return ObjcAddon::Init(env, exports); +} + +NODE_API_MODULE(objc_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +You're so close! We now have working Objective-C and thread-safe ways to expose methods and events to JavaScript. In this final step, let's create a JavaScript wrapper in `js/index.js` to provide a more friendly API: + +```js title='js/index.js' @ts-expect-error=[10] +const EventEmitter = require('events') + +class ObjcMacosAddon extends EventEmitter { + constructor () { + super() + + if (process.platform !== 'darwin') { + throw new Error('This module is only available on macOS') + } + + const native = require('bindings')('objc_addon') + this.addon = new native.ObjcMacosAddon() + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }) + } + + helloWorld (input = '') { + return this.addon.helloWorld(input) + } + + helloGui () { + this.addon.helloGui() + } + + parse (payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'darwin') { + module.exports = new ObjcMacosAddon() +} else { + module.exports = {} +} +``` + +This wrapper: + +1. Extends EventEmitter to provide event support +2. Checks if we're running on macOS +3. Loads the native addon +4. Sets up event listeners and forwards them +5. Provides a clean API for our functions +6. Parses JSON payloads and converts timestamps to JavaScript Date objects + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +Please note that you _cannot_ call this script from Node.js directly, since Node.js doesn't set up an "app" in the eyes of macOS. Electron does though, so you can test your code by requiring and calling it from Electron. + +## Conclusion + +You've now built a complete native Node.js addon for macOS using Objective-C and AppKit. This provides a foundation for building more complex macOS-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native macOS code. + +The approach demonstrated here allows you to: + +* Create native macOS UIs using AppKit +* Implement bidirectional communication between JavaScript and Objective-C +* Leverage macOS-specific features and frameworks +* Integrate with existing Objective-C codebases + +For more information on developing with Objective-C and Cocoa, refer to Apple's developer documentation: + +* [Objective-C Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html) +* [AppKit Framework](https://developer.apple.com/documentation/appkit) +* [macOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/macos) From c4302ab58caf22c31694b5de831af5d67b737b5a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:01:41 +0200 Subject: [PATCH 166/339] fix: crash when renderer process crashes while webview is reloading (#46768) WebView uses WebContentsViewChildFrame, which doesn't have a Focus impl and triggers a fatal NOTREACHED. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Jinli Wu --- .../browser/api/electron_api_web_contents.cc | 8 +++++++ shell/browser/api/electron_api_web_contents.h | 1 + spec/webview-spec.ts | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 9a3405834253b..1ca1efb084b38 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -4014,6 +4014,14 @@ void WebContents::ExitPictureInPicture() { PictureInPictureWindowManager::GetInstance()->ExitPictureInPicture(); } +bool WebContents::ShouldFocusPageAfterCrash(content::WebContents* source) { + // WebView uses WebContentsViewChildFrame, which doesn't have a Focus impl + // and triggers a fatal NOTREACHED. + if (is_guest()) + return false; + return true; +} + void WebContents::DevToolsSaveToFile(const std::string& url, const std::string& content, bool save_as, diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 30a2365bc0a9c..6793733db9518 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -710,6 +710,7 @@ class WebContents final : public ExclusiveAccessContext, content::PictureInPictureResult EnterPictureInPicture( content::WebContents* web_contents) override; void ExitPictureInPicture() override; + bool ShouldFocusPageAfterCrash(content::WebContents* source) override; // InspectableWebContentsDelegate: void DevToolsSaveToFile(const std::string& url, diff --git a/spec/webview-spec.ts b/spec/webview-spec.ts index dfc30937706e1..23d07e32569d8 100644 --- a/spec/webview-spec.ts +++ b/spec/webview-spec.ts @@ -1895,6 +1895,28 @@ describe(' tag', function () { expect(channel).to.equal('onbeforeunload'); }); + + it('does not crash when renderer process crashes', async function () { + // It takes more time to wait for the rendering process to crash + this.timeout(120000); + await loadWebView(w, { + nodeintegration: 'on', + webpreferences: 'contextIsolation=no', + src: blankPageUrl + }); + // Create a crash in the rendering process of a webview + await w.executeJavaScript(`new Promise((resolve, reject) => { + webview.addEventListener('render-process-gone', (e) => resolve({...e}), {once: true}) + webview.executeJavaScript('process.crash()', true) + })`); + // Reload the webview and the main process will not crash. + await w.executeJavaScript(`new Promise((resolve, reject) => { + webview.reload() + webview.addEventListener('did-finish-load', () => { + resolve() + }) + })`); + }); }); describe('.goForward()', () => { From 5dab95335b3269d63da6d828b4fc6ff5feba96a2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:28:28 -0500 Subject: [PATCH 167/339] fix: bluetooth crash in `select-bluetooth-device` event (#46782) fix: bluetooth crash on bluetooth off Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/lib/bluetooth_chooser.cc | 73 ++++++++++++-------------- shell/browser/lib/bluetooth_chooser.h | 9 +++- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/shell/browser/lib/bluetooth_chooser.cc b/shell/browser/lib/bluetooth_chooser.cc index 0c8708a91a484..23f39e1a3160f 100644 --- a/shell/browser/lib/bluetooth_chooser.cc +++ b/shell/browser/lib/bluetooth_chooser.cc @@ -25,19 +25,6 @@ struct Converter { namespace electron { -namespace { - -void OnDeviceChosen(const content::BluetoothChooser::EventHandler& handler, - const std::string& device_id) { - if (device_id.empty()) { - handler.Run(content::BluetoothChooserEvent::CANCELLED, device_id); - } else { - handler.Run(content::BluetoothChooserEvent::SELECTED, device_id); - } -} - -} // namespace - BluetoothChooser::BluetoothChooser(api::WebContents* contents, const EventHandler& event_handler) : api_web_contents_(contents), event_handler_(event_handler) {} @@ -49,14 +36,13 @@ BluetoothChooser::~BluetoothChooser() { void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) { switch (presence) { case AdapterPresence::ABSENT: + NOTREACHED(); case AdapterPresence::POWERED_OFF: - // Chrome currently directs the user to system preferences - // to grant bluetooth permission for this case, should we - // do something similar ? - // https://chromium-review.googlesource.com/c/chromium/src/+/2617129 - case AdapterPresence::UNAUTHORIZED: event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, ""); break; + case AdapterPresence::UNAUTHORIZED: + event_handler_.Run(content::BluetoothChooserEvent::DENIED_PERMISSION, ""); + break; case AdapterPresence::POWERED_ON: rescan_ = true; break; @@ -74,25 +60,27 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) { refreshing_ = false; idle_state = true; break; + // The first time this state fires is due to a rescan triggering so we + // set a flag to ignore devices - the second time this state fires + // we are now safe to pick a device. case DiscoveryState::DISCOVERING: - // The first time this state fires is due to a rescan triggering so set a - // flag to ignore devices if (rescan_ && !refreshing_) { refreshing_ = true; } else { - // The second time this state fires we are now safe to pick a device refreshing_ = false; } break; } + bool prevent_default = api_web_contents_->Emit("select-bluetooth-device", GetDeviceList(), - base::BindOnce(&OnDeviceChosen, event_handler_)); + base::BindOnce(&BluetoothChooser::OnDeviceChosen, + weak_ptr_factory_.GetWeakPtr())); if (!prevent_default && idle_state) { - if (device_map_.empty()) { + if (device_id_to_name_map_.empty()) { event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, ""); } else { - auto it = device_map_.begin(); + auto it = device_id_to_name_map_.begin(); auto device_id = it->first; event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id); } @@ -105,38 +93,47 @@ void BluetoothChooser::AddOrUpdateDevice(const std::string& device_id, bool is_gatt_connected, bool is_paired, int signal_strength_level) { - if (refreshing_) { - // If the list of bluetooth devices is currently being generated don't fire - // an event + // Don't fire an event during refresh. + if (refreshing_) return; - } - auto [iter, changed] = device_map_.try_emplace(device_id, device_name); + // Emit a select-bluetooth-device handler to allow for user to listen for + // bluetooth device found. If there's no listener in place, then select the + // first device that matches the filters provided. + auto [iter, changed] = + device_id_to_name_map_.try_emplace(device_id, device_name); if (!changed && should_update_name) { iter->second = device_name; changed = true; } if (changed) { - // Emit a select-bluetooth-device handler to allow for user to listen for - // bluetooth device found. bool prevent_default = api_web_contents_->Emit( "select-bluetooth-device", GetDeviceList(), - base::BindOnce(&OnDeviceChosen, event_handler_)); + base::BindOnce(&BluetoothChooser::OnDeviceChosen, + weak_ptr_factory_.GetWeakPtr())); - // If emit not implemented select first device that matches the filters - // provided. - if (!prevent_default) { + if (!prevent_default) event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id); - } + } +} + +void BluetoothChooser::OnDeviceChosen(const std::string& device_id) { + if (event_handler_.is_null()) + return; + + if (device_id.empty()) { + event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, device_id); + } else { + event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id); } } std::vector BluetoothChooser::GetDeviceList() { std::vector vec; - vec.reserve(device_map_.size()); - for (const auto& [device_id, device_name] : device_map_) + vec.reserve(device_id_to_name_map_.size()); + for (const auto& [device_id, device_name] : device_id_to_name_map_) vec.emplace_back(device_id, device_name); return vec; } diff --git a/shell/browser/lib/bluetooth_chooser.h b/shell/browser/lib/bluetooth_chooser.h index 19f7eac838911..5b377e5e1b696 100644 --- a/shell/browser/lib/bluetooth_chooser.h +++ b/shell/browser/lib/bluetooth_chooser.h @@ -5,13 +5,14 @@ #ifndef ELECTRON_SHELL_BROWSER_LIB_BLUETOOTH_CHOOSER_H_ #define ELECTRON_SHELL_BROWSER_LIB_BLUETOOTH_CHOOSER_H_ -#include #include #include #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "content/public/browser/bluetooth_chooser.h" #include "shell/browser/api/electron_api_web_contents.h" +#include "third_party/abseil-cpp/absl/container/flat_hash_map.h" namespace electron { @@ -39,14 +40,18 @@ class BluetoothChooser : public content::BluetoothChooser { bool is_gatt_connected, bool is_paired, int signal_strength_level) override; + + void OnDeviceChosen(const std::string& device_id); std::vector GetDeviceList(); private: - std::map device_map_; + absl::flat_hash_map device_id_to_name_map_; raw_ptr api_web_contents_; EventHandler event_handler_; bool refreshing_ = false; bool rescan_ = false; + + base::WeakPtrFactory weak_ptr_factory_{this}; }; } // namespace electron From d52670c749ff10ae4b959fadb3fb5c4d19583e16 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:22:00 -0500 Subject: [PATCH 168/339] refactor: use `std::map::try_emplace()` over `std::map::insert()` (#46794) refactor: prefer std::map::try_emplace() over std::map::insert() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/electron_autofill_driver_factory.cc | 3 +-- .../file_system_access_permission_context.cc | 2 +- shell/browser/hid/electron_hid_delegate.cc | 3 +-- shell/browser/hid/hid_chooser_context.cc | 2 +- shell/browser/net/proxying_url_loader_factory.cc | 4 ++-- shell/browser/notifications/mac/cocoa_notification.mm | 4 ++-- shell/browser/serial/electron_serial_delegate.cc | 3 +-- shell/browser/serial/serial_chooser_context.cc | 4 ++-- shell/browser/usb/electron_usb_delegate.cc | 3 +-- shell/browser/usb/usb_chooser_context.cc | 8 +++----- 10 files changed, 15 insertions(+), 21 deletions(-) diff --git a/shell/browser/electron_autofill_driver_factory.cc b/shell/browser/electron_autofill_driver_factory.cc index b2a68d4ec036a..84631924d09f7 100644 --- a/shell/browser/electron_autofill_driver_factory.cc +++ b/shell/browser/electron_autofill_driver_factory.cc @@ -94,8 +94,7 @@ AutofillDriver* AutofillDriverFactory::DriverForFrame( void AutofillDriverFactory::AddDriverForFrame( content::RenderFrameHost* render_frame_host, CreationCallback factory_method) { - auto insertion_result = - driver_map_.insert(std::make_pair(render_frame_host, nullptr)); + auto insertion_result = driver_map_.try_emplace(render_frame_host, nullptr); // This can be called twice for the key representing the main frame. if (insertion_result.second) { insertion_result.first->second = std::move(factory_method).Run(); diff --git a/shell/browser/file_system_access/file_system_access_permission_context.cc b/shell/browser/file_system_access/file_system_access_permission_context.cc index 865fa06f19bac..833f3992f5814 100644 --- a/shell/browser/file_system_access/file_system_access_permission_context.cc +++ b/shell/browser/file_system_access/file_system_access_permission_context.cc @@ -745,7 +745,7 @@ void FileSystemAccessPermissionContext::SetLastPickedDirectory( base::Value::Dict dict; dict.Set(GenerateLastPickedDirectoryKey(id), std::move(entry)); MaybeEvictEntries(dict); - id_pathinfo_map_.insert(std::make_pair(origin, std::move(dict))); + id_pathinfo_map_.try_emplace(origin, std::move(dict)); } } diff --git a/shell/browser/hid/electron_hid_delegate.cc b/shell/browser/hid/electron_hid_delegate.cc index 1915d4e489393..3f0f6fd091f7e 100644 --- a/shell/browser/hid/electron_hid_delegate.cc +++ b/shell/browser/hid/electron_hid_delegate.cc @@ -243,8 +243,7 @@ HidChooserController* ElectronHidDelegate::AddControllerForFrame( auto controller = std::make_unique( render_frame_host, std::move(filters), std::move(exclusion_filters), std::move(callback), web_contents, weak_factory_.GetWeakPtr()); - controller_map_.insert( - std::make_pair(render_frame_host, std::move(controller))); + controller_map_.try_emplace(render_frame_host, std::move(controller)); return ControllerForFrame(render_frame_host); } diff --git a/shell/browser/hid/hid_chooser_context.cc b/shell/browser/hid/hid_chooser_context.cc index 4869f56eb8def..8c27a729ff580 100644 --- a/shell/browser/hid/hid_chooser_context.cc +++ b/shell/browser/hid/hid_chooser_context.cc @@ -310,7 +310,7 @@ void HidChooserContext::SetUpHidManagerConnection( void HidChooserContext::InitDeviceList( std::vector devices) { for (auto& device : devices) - devices_.insert({device->guid, std::move(device)}); + devices_.try_emplace(device->guid, std::move(device)); is_initialized_ = true; diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index cee7c0f756e42..572e5e4ae6ded 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -883,9 +883,9 @@ void ProxyingURLLoaderFactory::OnLoaderForCorsPreflightCreated( // sending request headers is very difficult. const uint64_t web_request_id = ++(*request_id_generator_); - auto result = requests_.insert(std::make_pair( + auto result = requests_.try_emplace( web_request_id, std::make_unique( - this, web_request_id, frame_routing_id_, request))); + this, web_request_id, frame_routing_id_, request)); result.first->second->OnLoaderCreated(std::move(receiver)); result.first->second->Restart(); diff --git a/shell/browser/notifications/mac/cocoa_notification.mm b/shell/browser/notifications/mac/cocoa_notification.mm index a90842f096163..2408fbd98cab0 100644 --- a/shell/browser/notifications/mac/cocoa_notification.mm +++ b/shell/browser/notifications/mac/cocoa_notification.mm @@ -93,8 +93,8 @@ actionWithIdentifier:actionIdentifier title:base::SysUTF16ToNSString(action.text)]; [additionalActions addObject:notificationAction]; - additional_action_indices_.insert( - std::make_pair(base::SysNSStringToUTF8(actionIdentifier), i)); + additional_action_indices_.try_emplace( + base::SysNSStringToUTF8(actionIdentifier), i); } } i++; diff --git a/shell/browser/serial/electron_serial_delegate.cc b/shell/browser/serial/electron_serial_delegate.cc index e7b9e551fe1d5..bd8f5c5a85c6c 100644 --- a/shell/browser/serial/electron_serial_delegate.cc +++ b/shell/browser/serial/electron_serial_delegate.cc @@ -116,8 +116,7 @@ SerialChooserController* ElectronSerialDelegate::AddControllerForFrame( render_frame_host, std::move(filters), std::move(allowed_bluetooth_service_class_ids), std::move(callback), web_contents, weak_factory_.GetWeakPtr()); - controller_map_.insert( - std::make_pair(render_frame_host, std::move(controller))); + controller_map_.try_emplace(render_frame_host, std::move(controller)); return ControllerForFrame(render_frame_host); } diff --git a/shell/browser/serial/serial_chooser_context.cc b/shell/browser/serial/serial_chooser_context.cc index be387c644e9ad..a857f6464bf7b 100644 --- a/shell/browser/serial/serial_chooser_context.cc +++ b/shell/browser/serial/serial_chooser_context.cc @@ -85,7 +85,7 @@ void SerialChooserContext::GrantPortPermission( const url::Origin& origin, const device::mojom::SerialPortInfo& port, content::RenderFrameHost* render_frame_host) { - port_info_.insert({port.token, port.Clone()}); + port_info_.try_emplace(port.token, port.Clone()); if (CanStorePersistentEntry(port)) { auto* permission_manager = static_cast( @@ -270,7 +270,7 @@ void SerialChooserContext::SetUpPortManagerConnection( void SerialChooserContext::OnGetDevices( std::vector ports) { for (auto& port : ports) - port_info_.insert({port->token, std::move(port)}); + port_info_.try_emplace(port->token, std::move(port)); is_initialized_ = true; } diff --git a/shell/browser/usb/electron_usb_delegate.cc b/shell/browser/usb/electron_usb_delegate.cc index 4e3827d04f74d..535e30e60ba72 100644 --- a/shell/browser/usb/electron_usb_delegate.cc +++ b/shell/browser/usb/electron_usb_delegate.cc @@ -301,8 +301,7 @@ UsbChooserController* ElectronUsbDelegate::AddControllerForFrame( auto controller = std::make_unique( render_frame_host, std::move(options), std::move(callback), web_contents, weak_factory_.GetWeakPtr()); - controller_map_.insert( - std::make_pair(render_frame_host, std::move(controller))); + controller_map_.try_emplace(render_frame_host, std::move(controller)); return ControllerForFrame(render_frame_host); } diff --git a/shell/browser/usb/usb_chooser_context.cc b/shell/browser/usb/usb_chooser_context.cc index 1273b7dbd41e7..42fd6b2e4284e 100644 --- a/shell/browser/usb/usb_chooser_context.cc +++ b/shell/browser/usb/usb_chooser_context.cc @@ -103,10 +103,8 @@ void UsbChooserContext::InitDeviceList( std::vector devices) { for (auto& device_info : devices) { DCHECK(device_info); - if (ShouldExposeDevice(*device_info)) { - devices_.insert( - std::make_pair(device_info->guid, std::move(device_info))); - } + if (ShouldExposeDevice(*device_info)) + devices_.try_emplace(device_info->guid, std::move(device_info)); } is_initialized_ = true; @@ -285,7 +283,7 @@ void UsbChooserContext::OnDeviceAdded( DCHECK(!devices_.contains(device_info->guid)); if (!ShouldExposeDevice(*device_info)) return; - devices_.insert(std::make_pair(device_info->guid, device_info->Clone())); + devices_.try_emplace(device_info->guid, device_info->Clone()); // Notify all observers. for (auto& observer : device_observer_list_) From 108fdac02c8ec70cb8103def062500c7040e4f1d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:27:27 -0500 Subject: [PATCH 169/339] fix: set `XDG_CURRENT_DESKTOP` env var back to original value before invoking xdg utils (#46788) fix: set `XDG_CURRENT_DESKTOP` env var back to original value before invoking xdg utils (#45310) * Fix XDG_CURRENT_DESKTOP before invoking XDGUtil * apply suggestion * use existing XDG_CURRENT_DESKTOP const Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Evan Simkowitz --- shell/common/platform_util_linux.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shell/common/platform_util_linux.cc b/shell/common/platform_util_linux.cc index 125b4d037a474..9dd491945548f 100644 --- a/shell/common/platform_util_linux.cc +++ b/shell/common/platform_util_linux.cc @@ -15,6 +15,7 @@ #include "base/cancelable_callback.h" #include "base/containers/contains.h" +#include "base/containers/map_util.h" #include "base/environment.h" #include "base/files/file_util.h" #include "base/files/scoped_file.h" @@ -58,6 +59,8 @@ const char kFreedesktopPortalName[] = "org.freedesktop.portal.Desktop"; const char kFreedesktopPortalPath[] = "/org/freedesktop/portal/desktop"; const char kFreedesktopPortalOpenURI[] = "org.freedesktop.portal.OpenURI"; +const char kOriginalXdgCurrentDesktopEnvVar[] = "ORIGINAL_XDG_CURRENT_DESKTOP"; + const char kMethodOpenDirectory[] = "OpenDirectory"; class ShowItemHelper { @@ -282,6 +285,12 @@ bool XDGUtil(const std::vector& argv, base::nix::CreateLaunchOptionsWithXdgActivation(base::BindOnce( [](base::RepeatingClosure quit_loop, base::LaunchOptions* options_out, base::LaunchOptions options) { + // Correct the XDG_CURRENT_DESKTOP environment variable before calling + // XDG, in case it was changed for compatibility. + if (const auto* orig = base::FindOrNull( + options.environment, kOriginalXdgCurrentDesktopEnvVar)) + options.environment.emplace(base::nix::kXdgCurrentDesktopEnvVar, + *orig); *options_out = std::move(options); std::move(quit_loop).Run(); }, From 66f55ead4ef4a00196125dd2eb68b5e46468954f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:14:47 -0500 Subject: [PATCH 170/339] docs: call out breaking change for GTK 4 default on GNOME (#46800) docs: add breaking change for GTK 4 default on GNOME Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: clavin --- docs/breaking-changes.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 4a75e7f95ad06..534831e1a2ed9 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -84,6 +84,24 @@ It has been always returning `true` since Electron 23, which only supports Windo https://learn.microsoft.com/en-us/windows/win32/dwm/composition-ovw#disabling-dwm-composition-windows7-and-earlier +### Changed: GTK 4 is default when running GNOME + +After an [upstream change](https://chromium-review.googlesource.com/c/chromium/src/+/6310469), GTK 4 is now the default when running GNOME. + +In rare cases, this may cause some applications or configurations to [error](https://github.com/electron/electron/issues/46538) with the following message: + +```stderr +Gtk-ERROR **: 11:30:38.382: GTK 2/3 symbols detected. Using GTK 2/3 and GTK 4 in the same process is not supported +``` + +Affected users can work around this by specifying the `gtk-version` command-line flag: + +```shell +$ electron --gtk-version=3 # or --gtk-version=2 +``` + +The same can be done with the [`app.commandLine.appendSwitch`](https://www.electronjs.org/docs/latest/api/command-line#commandlineappendswitchswitch-value) function. + ## Planned Breaking API Changes (35.0) ### Behavior Changed: Dialog API's `defaultPath` option on Linux From 1a797beeea8dab08bf8680f644a08321ee3a1fcc Mon Sep 17 00:00:00 2001 From: Calvin Date: Fri, 25 Apr 2025 17:10:04 -0600 Subject: [PATCH 171/339] fix: corner smoothing with 0 radii (36-x-y backport) (#46799) fix: corner smoothing with 0 radii (36-x-y) --- ...moothing_css_rule_and_blink_painting.patch | 17 +++++-- shell/renderer/electron_smooth_round_rect.cc | 43 +++++++++++------- spec/api-corner-smoothing-spec.ts | 7 +-- .../corner-smoothing/shape/expected-false.png | Bin 166667 -> 125457 bytes .../corner-smoothing/shape/expected-true.png | Bin 148213 -> 117286 bytes .../api/corner-smoothing/shape/test.html | 11 ++++- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch index 41283b3c18ae1..c18b4dfd98002 100644 --- a/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch +++ b/patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch @@ -400,7 +400,7 @@ index b147b8d321d865295007516b15d0aaccfc6f7fac..8f54a3a657c660a52fcd4c94865ca219 // A Corner is a axis-aligned quad, with the points ordered (start, outer, diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc -index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d42368b2e 100644 +index 346cfc0b13b31808fbe1381b3785150810f347bb..ebb2be8cc3bb71c4df6526d743a851121e36c138 100644 --- a/third_party/blink/renderer/platform/geometry/path_builder.cc +++ b/third_party/blink/renderer/platform/geometry/path_builder.cc @@ -4,6 +4,7 @@ @@ -411,7 +411,7 @@ index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d #include "third_party/blink/renderer/platform/geometry/contoured_rect.h" #include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h" #include "third_party/blink/renderer/platform/geometry/path.h" -@@ -231,6 +232,19 @@ PathBuilder& PathBuilder::AddContouredRect( +@@ -231,6 +232,26 @@ PathBuilder& PathBuilder::AddContouredRect( AddRoundedRect(target_rect); return *this; } @@ -419,12 +419,19 @@ index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d + // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners + if (contoured_rect.GetCornerCurvature().IsSmooth()) { + const gfx::RectF& box = contoured_rect.Rect(); -+ const FloatRoundedRect::Radii& radii = contoured_rect.GetRadii(); ++ ++ // Constrain the radii (on a copy) to ensure they do not exceed the box. ++ FloatRoundedRect round_rect_copy = contoured_rect.AsRoundedRect(); ++ round_rect_copy.ConstrainRadii(); ++ const FloatRoundedRect::Radii& radii = round_rect_copy.GetRadii(); ++ float smoothness = std::clamp( ++ contoured_rect.GetCornerCurvature().Smoothness(), 0.0f, 1.0f); ++ + builder_.addPath(electron::DrawSmoothRoundRect( -+ box.x(), box.y(), box.width(), box.height(), -+ std::min(contoured_rect.GetCornerCurvature().Smoothness(), 1.0f), ++ box.x(), box.y(), box.width(), box.height(), smoothness, + radii.TopLeft().width(), radii.TopRight().width(), + radii.BottomRight().width(), radii.BottomLeft().width())); ++ + return *this; + } + diff --git a/shell/renderer/electron_smooth_round_rect.cc b/shell/renderer/electron_smooth_round_rect.cc index dcd681387c1f6..e78f7cf0df43b 100644 --- a/shell/renderer/electron_smooth_round_rect.cc +++ b/shell/renderer/electron_smooth_round_rect.cc @@ -100,10 +100,23 @@ constexpr CurveGeometry::CurveGeometry(float radius, float smoothness) { void DrawCorner(SkPath& path, float radius, - const CurveGeometry& curve1, - const CurveGeometry& curve2, + float smoothness1, + float smoothness2, const SkPoint& corner, unsigned int quarter_rotations) { + // If the radius is 0 then we can simply draw a line to the corner point. + if (radius == 0.0f) { + if (quarter_rotations == 0) { + path.moveTo(corner); + } else { + path.lineTo(corner); + } + return; + } + + CurveGeometry curve1(radius, smoothness1); + CurveGeometry curve2(radius, smoothness2); + // Move/Line to the edge connecting point { SkPoint edge_connecting_point = @@ -160,6 +173,12 @@ constexpr std::pair ConstrainSmoothness(float size, float smoothness, float radius1, float radius2) { + // If both radii are 0 then we don't need any smoothing. This avoids a + // division by zero in the ratio calculation. + if (radius1 == 0.0f && radius2 == 0.0f) { + return {0.0f, 0.0f}; + } + float edge_consumed1 = LengthForCornerSmoothness(smoothness, radius1); float edge_consumed2 = LengthForCornerSmoothness(smoothness, radius2); @@ -269,28 +288,20 @@ SkPath DrawSmoothRoundRect(float x, SkPath path; // Top left corner - DrawCorner(path, top_left_radius, - CurveGeometry(top_left_radius, left_top_smoothness), - CurveGeometry(top_left_radius, top_left_smoothness), + DrawCorner(path, top_left_radius, left_top_smoothness, top_left_smoothness, SkPoint::Make(x, y), 0); // Top right corner - DrawCorner(path, top_right_radius, - CurveGeometry(top_right_radius, top_right_smoothness), - CurveGeometry(top_right_radius, right_top_smoothness), + DrawCorner(path, top_right_radius, top_right_smoothness, right_top_smoothness, SkPoint::Make(x + width, y), 1); // Bottom right corner - DrawCorner(path, bottom_right_radius, - CurveGeometry(bottom_right_radius, right_bottom_smoothness), - CurveGeometry(bottom_right_radius, bottom_right_smoothness), - SkPoint::Make(x + width, y + height), 2); + DrawCorner(path, bottom_right_radius, right_bottom_smoothness, + bottom_right_smoothness, SkPoint::Make(x + width, y + height), 2); // Bottom left corner - DrawCorner(path, bottom_left_radius, - CurveGeometry(bottom_left_radius, bottom_left_smoothness), - CurveGeometry(bottom_left_radius, left_bottom_smoothness), - SkPoint::Make(x, y + height), 3); + DrawCorner(path, bottom_left_radius, left_bottom_smoothness, + bottom_left_smoothness, SkPoint::Make(x, y + height), 3); path.close(); return path; diff --git a/spec/api-corner-smoothing-spec.ts b/spec/api-corner-smoothing-spec.ts index a25a787db7625..2ca9285832a28 100644 --- a/spec/api-corner-smoothing-spec.ts +++ b/spec/api-corner-smoothing-spec.ts @@ -6,7 +6,6 @@ import { AssertionError, expect } from 'chai'; import path = require('node:path'); import { createArtifact } from './lib/artifacts'; -import { ifdescribe } from './lib/spec-helpers'; import { closeAllWindows } from './lib/window-helpers'; const FIXTURE_PATH = path.resolve( @@ -42,7 +41,7 @@ const COMPARISON_TOLERANCE = 2.5; function compareImages (img1: NativeImage, img2: NativeImage): boolean { expect(img1.getSize()).to.deep.equal( img2.getSize(), - 'Cannot compare images with different sizes' + 'Cannot compare images with different sizes. Run tests with --force-device-scale-factor=1' ); const bitmap1 = img1.toBitmap(); @@ -119,9 +118,7 @@ async function pageCaptureTestRecipe ( } } -// FIXME: these tests rely on live rendering results, which are too variable to -// reproduce outside of CI, primarily due to display scaling. -ifdescribe(!!process.env.CI)('-electron-corner-smoothing', () => { +describe('-electron-corner-smoothing', () => { afterEach(async () => { await closeAllWindows(); }); diff --git a/spec/fixtures/api/corner-smoothing/shape/expected-false.png b/spec/fixtures/api/corner-smoothing/shape/expected-false.png index 31aea673f8b06fcd4b92d4ea48bff0aa73021c9f..56c135d740af762f572fcb7859d8173e927ba155 100644 GIT binary patch literal 125457 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

*%X9f9GgO_!$yzglmx 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�k8<-J8cw#@iWaS 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 Du*Xdc literal 166667 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�CphU2Hr9iQpV|b6$L}5Q| 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

VLQ%*^OE} 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� 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 @@ -95,6 +99,7 @@
+
@@ -104,6 +109,7 @@
+
@@ -113,6 +119,7 @@
+
@@ -122,6 +129,7 @@
+
@@ -131,6 +139,7 @@
+
\ No newline at end of file From 61d6872dcf8d52938c735410ac7ed3975dcda055 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:08:43 -0500 Subject: [PATCH 172/339] refactor: use `absl::InlinedVector` in `ToV8(ElectronPermissionManager)` (#46805) perf: use a stack-allocated string_view array in ToV8(USBProtectedClasses) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../gin_converters/usb_protected_classes_converter.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shell/common/gin_converters/usb_protected_classes_converter.h b/shell/common/gin_converters/usb_protected_classes_converter.h index d4db86b5971a6..df93c9f474f41 100644 --- a/shell/common/gin_converters/usb_protected_classes_converter.h +++ b/shell/common/gin_converters/usb_protected_classes_converter.h @@ -13,6 +13,8 @@ #include "gin/converter.h" #include "services/device/public/mojom/usb_device.mojom-forward.h" #include "shell/browser/electron_permission_manager.h" +#include "shell/common/gin_converters/std_converter.h" +#include "third_party/abseil-cpp/absl/container/inlined_vector.h" namespace gin { @@ -31,15 +33,14 @@ struct Converter { static v8::Local ToV8( v8::Isolate* isolate, const electron::ElectronPermissionManager::USBProtectedClasses& classes) { - std::vector class_strings; - class_strings.reserve(std::size(classes)); + absl::InlinedVector class_strings; for (const auto& itr : classes) { for (const auto& [usb_class, name] : ClassMapping) { if (usb_class == itr) class_strings.emplace_back(name); } } - return gin::ConvertToV8(isolate, class_strings); + return gin::ConvertToV8(isolate, std::span(class_strings)); } static bool FromV8( From 3981c173ef334a7196625f3498b7c2baa9614d35 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 20:09:44 -0500 Subject: [PATCH 173/339] refactor: remove public method `BrowserWindow::GetWeakPtr()` (#46796) refactor: remove public method electron::api::BrowserWindow::GetWeakPtr() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_browser_window.cc | 2 +- shell/browser/api/electron_api_browser_window.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index b27c7f0f32c96..c352ee79a5de7 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -132,7 +132,7 @@ void BrowserWindow::OnActivateContents() { void BrowserWindow::OnPageTitleUpdated(const std::u16string& title, bool explicit_set) { // Change window title to page title. - auto self = GetWeakPtr(); + auto self = weak_factory_.GetWeakPtr(); if (!Emit("page-title-updated", title, explicit_set)) { // |this| might be deleted, or marked as destroyed by close(). if (self && !IsDestroyed()) diff --git a/shell/browser/api/electron_api_browser_window.h b/shell/browser/api/electron_api_browser_window.h index f3d4b352c67ce..4e8822e487cf4 100644 --- a/shell/browser/api/electron_api_browser_window.h +++ b/shell/browser/api/electron_api_browser_window.h @@ -32,10 +32,6 @@ class BrowserWindow : public BaseWindow, static v8::Local From(v8::Isolate* isolate, NativeWindow* native_window); - base::WeakPtr GetWeakPtr() { - return weak_factory_.GetWeakPtr(); - } - // disable copy BrowserWindow(const BrowserWindow&) = delete; BrowserWindow& operator=(const BrowserWindow&) = delete; From 5c2f70c1d5feb28d3ea907536f7cabbcc27924e9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 25 Apr 2025 21:13:51 -0500 Subject: [PATCH 174/339] fix: `backgroundMaterial` on initial activate (36-x-y) (#46792) * fix: `backgroundMaterial` on initial activate (#46657) * empty commit --------- Co-authored-by: Shelley Vohr --- .../fix_activate_background_material_on_windows.patch | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch index 4f4fc4263db01..2b561ab7cce82 100644 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ b/patches/chromium/fix_activate_background_material_on_windows.patch @@ -14,7 +14,7 @@ This patch likely can't be upstreamed as-is, as Chromium doesn't have this use case in mind currently. diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..96cf055f1144887c0b4b4aef83898508016b310f 100644 +index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..5c74a092920cdb3b7e98d218c75874cac2d0542f 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -954,13 +954,13 @@ void HWNDMessageHandler::FrameTypeChanged() { @@ -33,6 +33,15 @@ index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..96cf055f1144887c0b4b4aef83898508 } void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, +@@ -1757,7 +1757,7 @@ void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { + if (delegate_->HasNonClientView() && !active && + thread_id != GetCurrentThreadId()) { + // Update the native frame if it is rendering the non-client area. +- if (HasSystemFrame()) { ++ if (is_translucent_ || HasSystemFrame()) { + DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); + } + } @@ -2365,17 +2365,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, delegate_->SchedulePaint(); } From 34adb976b632157379de34cc1a71bdf6cc089714 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 27 Apr 2025 15:49:52 -0500 Subject: [PATCH 175/339] refactor: use upstream content protection logic on macOS (#46813) * refactor: use upstream content protection logic on macOS Co-authored-by: Shelley Vohr * Update shell/browser/native_window.h Co-authored-by: Charles Kerr Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window.cc | 15 +++++++++++++++ shell/browser/native_window.h | 4 ++-- shell/browser/native_window_mac.h | 2 -- shell/browser/native_window_mac.mm | 9 --------- shell/browser/native_window_views.cc | 15 --------------- shell/browser/native_window_views.h | 2 -- 6 files changed, 17 insertions(+), 30 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index d8a0e49c83f69..50a76e3985308 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -26,6 +26,7 @@ #include "shell/common/options_switches.h" #include "ui/base/hit_test.h" #include "ui/compositor/compositor.h" +#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #if !BUILDFLAG(IS_MAC) @@ -818,6 +819,20 @@ void NativeWindow::HandlePendingFullscreenTransitions() { SetFullScreen(next_transition); } +void NativeWindow::SetContentProtection(bool enable) { +#if !BUILDFLAG(IS_LINUX) + widget()->native_widget_private()->SetAllowScreenshots(!enable); +#endif +} + +bool NativeWindow::IsContentProtected() const { +#if !BUILDFLAG(IS_LINUX) + return !widget()->native_widget_private()->AreScreenshotsAllowed(); +#else // Not implemented on Linux + return false; +#endif +} + bool NativeWindow::IsTranslucent() const { // Transparent windows are translucent if (transparent()) { diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 831cf8947aa85..7a9b762c60b2c 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -186,8 +186,8 @@ class NativeWindow : public base::SupportsUserData, virtual void SetDocumentEdited(bool edited) {} virtual bool IsDocumentEdited() const; virtual void SetIgnoreMouseEvents(bool ignore, bool forward) = 0; - virtual void SetContentProtection(bool enable) = 0; - virtual bool IsContentProtected() const = 0; + void SetContentProtection(bool enable); + bool IsContentProtected() const; virtual void SetFocusable(bool focusable) {} virtual bool IsFocusable() const; virtual void SetMenu(ElectronMenuModel* menu) {} diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 5420e14aecd7d..fa51e9ee447d2 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -109,8 +109,6 @@ class NativeWindowMac : public NativeWindow, void SetIgnoreMouseEvents(bool ignore, bool forward) override; bool IsHiddenInMissionControl() const override; void SetHiddenInMissionControl(bool hidden) override; - void SetContentProtection(bool enable) override; - bool IsContentProtected() const override; void SetFocusable(bool focusable) override; bool IsFocusable() const override; void SetParentWindow(NativeWindow* parent) override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index a895f26446213..ff97e84c0b1a8 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1171,15 +1171,6 @@ static bool FromV8(v8::Isolate* isolate, } } -void NativeWindowMac::SetContentProtection(bool enable) { - [window_ - setSharingType:enable ? NSWindowSharingNone : NSWindowSharingReadOnly]; -} - -bool NativeWindowMac::IsContentProtected() const { - return [window_ sharingType] == NSWindowSharingNone; -} - void NativeWindowMac::SetFocusable(bool focusable) { // No known way to unfocus the window if it had the focus. Here we do not // want to call Focus(false) because it moves the window to the back, i.e. diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index aaaa136565d9b..b294a94731770 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -44,7 +44,6 @@ #include "ui/ozone/public/ozone_platform.h" #include "ui/views/background.h" #include "ui/views/controls/webview/webview.h" -#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #include "ui/views/window/client_view.h" #include "ui/wm/core/shadow_types.h" @@ -1312,20 +1311,6 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { #endif } -void NativeWindowViews::SetContentProtection(bool enable) { -#if BUILDFLAG(IS_WIN) - widget()->native_widget_private()->SetAllowScreenshots(!enable); -#endif -} - -bool NativeWindowViews::IsContentProtected() const { -#if BUILDFLAG(IS_WIN) - return !widget()->native_widget_private()->AreScreenshotsAllowed(); -#else // Not implemented on Linux - return false; -#endif -} - void NativeWindowViews::SetFocusable(bool focusable) { widget()->widget_delegate()->SetCanActivate(focusable); #if BUILDFLAG(IS_WIN) diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 518842aa90a3b..7c77d7764a6b7 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -115,8 +115,6 @@ class NativeWindowViews : public NativeWindow, void SetOpacity(const double opacity) override; double GetOpacity() const override; void SetIgnoreMouseEvents(bool ignore, bool forward) override; - void SetContentProtection(bool enable) override; - bool IsContentProtected() const override; void SetFocusable(bool focusable) override; bool IsFocusable() const override; void SetMenu(ElectronMenuModel* menu_model) override; From 698bff0efabbfec5d2c2d110191ba708bef34ce1 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:20:07 -0500 Subject: [PATCH 176/339] fix: enable `autoHideMenuBar` tests on Linux and Windows (#46830) * fix: enable autoHideMenuBar tests Co-authored-by: Charles Kerr * docs: mark autoHideMenuBar as supported on Linux, Windows Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- docs/api/base-window.md | 2 +- docs/api/browser-window.md | 2 +- docs/api/structures/base-window-options.md | 4 ++-- spec/api-browser-window-spec.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/api/base-window.md b/docs/api/base-window.md index d23ced860c5b9..7ad17d045c909 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -398,7 +398,7 @@ A `View` property for the content view of the window. A `string` (optional) property that is equal to the `tabbingIdentifier` passed to the `BrowserWindow` constructor or `undefined` if none was set. -#### `win.autoHideMenuBar` +#### `win.autoHideMenuBar` _Linux_ _Windows_ A `boolean` property that determines whether the window menu bar should hide itself automatically. Once set, the menu bar will only show when users press the single `Alt` key. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 96ef6896ffbea..95a1baa784450 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -499,7 +499,7 @@ A `Integer` property representing the unique ID of the window. Each ID is unique A `string` (optional) property that is equal to the `tabbingIdentifier` passed to the `BrowserWindow` constructor or `undefined` if none was set. -#### `win.autoHideMenuBar` +#### `win.autoHideMenuBar` _Linux_ _Windows_ A `boolean` property that determines whether the window menu bar should hide itself automatically. Once set, the menu bar will only show when users press the single `Alt` key. diff --git a/docs/api/structures/base-window-options.md b/docs/api/structures/base-window-options.md index 84169a049ad94..0b451c7733a0e 100644 --- a/docs/api/structures/base-window-options.md +++ b/docs/api/structures/base-window-options.md @@ -58,8 +58,8 @@ `false` on macOS. This option is not configurable on other platforms. * `disableAutoHideCursor` boolean (optional) - Whether to hide cursor when typing. Default is `false`. -* `autoHideMenuBar` boolean (optional) - Auto hide the menu bar unless the `Alt` - key is pressed. Default is `false`. +* `autoHideMenuBar` boolean (optional) _Linux_ _Windows_ - Auto hide the menu bar + unless the `Alt` key is pressed. Default is `false`. * `enableLargerThanScreen` boolean (optional) _macOS_ - Enable the window to be resized larger than screen. Only relevant for macOS, as other OSes allow larger-than-screen windows by default. Default is `false`. diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 56980c7683d3e..c421f3cb094fa 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -2361,10 +2361,10 @@ describe('BrowserWindow module', () => { }); }); - describe('autoHideMenuBar state', () => { + ifdescribe(process.platform !== 'darwin')('autoHideMenuBar state', () => { afterEach(closeAllWindows); - it('for properties', () => { + describe('for properties', () => { it('can be set with autoHideMenuBar constructor option', () => { const w = new BrowserWindow({ show: false, autoHideMenuBar: true }); expect(w.autoHideMenuBar).to.be.true('autoHideMenuBar'); @@ -2380,7 +2380,7 @@ describe('BrowserWindow module', () => { }); }); - it('for functions', () => { + describe('for functions', () => { it('can be set with autoHideMenuBar constructor option', () => { const w = new BrowserWindow({ show: false, autoHideMenuBar: true }); expect(w.isMenuBarAutoHide()).to.be.true('autoHideMenuBar'); From 6bb07bf2777e4921bf0c771607c5ed85cda45ab6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:21:03 -0500 Subject: [PATCH 177/339] fix: build error with enable_electron_extensions=false (#46842) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix build error with enable_electron_extensions=false In file included from ../../base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h:13, from ../../base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:50, from ../../base/memory/raw_ptr.h:11, from ../../base/memory/weak_ptr.h:82, from ../../electron/shell/browser/usb/electron_usb_delegate.h:14, from ../../electron/shell/browser/usb/electron_usb_delegate.cc:5: ../../base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h:279:3: warning: multi-line comment [-Wcomment] 279 | // \ | ^ ../../base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h:281:3: warning: multi-line comment [-Wcomment] 281 | // \ | ^ In file included from /usr/include/c++/14/memory:78, from ../../electron/shell/browser/usb/electron_usb_delegate.h:8: /usr/include/c++/14/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = device::mojom::UsbDeviceInfo]’: /usr/include/c++/14/bits/unique_ptr.h:399:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = device::mojom::UsbDeviceInfo; _Dp = std::default_delete]’ 399 | get_deleter()(std::move(__ptr)); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ ../../mojo/public/cpp/bindings/struct_ptr.h:48:3: required from ‘constexpr void std::destroy_at(_Tp*) [with _Tp = mojo::StructPtr]’ 48 | ~StructPtr() = default; | ^ /usr/include/c++/14/bits/stl_construct.h:149:22: required from ‘constexpr void std::_Destroy(_Tp*) [with _Tp = mojo::StructPtr]’ 149 | std::destroy_at(__pointer); | ~~~~~~~~~~~~~~~^~~~~~~~~~~ /usr/include/c++/14/bits/stl_construct.h:163:19: required from ‘static constexpr void std::_Destroy_aux< >::__destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = mojo::StructPtr*; bool = false]’ 163 | std::_Destroy(std::__addressof(*__first)); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/include/c++/14/bits/stl_construct.h:193:44: required from ‘constexpr void std::_Destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = mojo::StructPtr*]’ 193 | return std::_Destroy_aux::__destroy(__first, __last); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ /usr/include/c++/14/bits/alloc_traits.h:981:20: required from ‘constexpr void std::_Destroy(_ForwardIterator, _ForwardIterator, allocator<_T2>&) [with _ForwardIterator = mojo::StructPtr*; _Tp = mojo::StructPtr]’ 981 | std::_Destroy(__first, __last); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ /usr/include/c++/14/bits/stl_vector.h:735:15: required from ‘constexpr std::vector<_Tp, _Alloc>::~vector() [with _Tp = mojo::StructPtr; _Alloc = std::allocator >]’ 735 | std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 736 | _M_get_Tp_allocator()); | ~~~~~~~~~~~~~~~~~~~~~~ ../../electron/shell/browser/usb/electron_usb_delegate.cc:231:74: required from here 231 | std::move(callback).Run(std::vector()); | ^ /usr/include/c++/14/bits/unique_ptr.h:91:23: error: invalid application of ‘sizeof’ to incomplete type ‘device::mojom::UsbDeviceInfo’ 91 | static_assert(sizeof(_Tp)>0, | ^~~~~~~~~~~ Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Bruno Pitrus --- shell/browser/usb/electron_usb_delegate.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/usb/electron_usb_delegate.cc b/shell/browser/usb/electron_usb_delegate.cc index 535e30e60ba72..8d47c344197c6 100644 --- a/shell/browser/usb/electron_usb_delegate.cc +++ b/shell/browser/usb/electron_usb_delegate.cc @@ -12,6 +12,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "electron/buildflags/buildflags.h" +#include "services/device/public/mojom/usb_device.mojom.h" #include "services/device/public/mojom/usb_enumeration_options.mojom.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/electron_permission_manager.h" @@ -27,7 +28,6 @@ #include "extensions/browser/guest_view/web_view/web_view_guest.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" -#include "services/device/public/mojom/usb_device.mojom.h" #endif namespace { From 65081e89392aa61c929af8df816390245823235f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:42:36 -0500 Subject: [PATCH 178/339] fix: run `visibleOnAllWorkspaces` tests on the right platforms (#46834) * test: add platform test on visibleOnAllWorkspaces tests visibleOnAllWorkspaces is not supported on Windows Co-authored-by: Charles Kerr * test: do not skip visibleOnAllWorkspaces tests on Windows That feature is supported on Linux, so move the test from the "window states (excluding Linux)" section into the "window states" section. Co-authored-by: Charles Kerr * fix: nested it() calls in visibleOnAllWorkspaces specs Co-authored-by: Charles Kerr * chore: make the process.platform test simpler Co-authored-by: Charles Kerr * chore: disable visibleOnAllWorkspaces test on Linux --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/api-browser-window-spec.ts | 41 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index c421f3cb094fa..163be971dab9f 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -5407,6 +5407,27 @@ describe('BrowserWindow module', () => { expect(w.webContents.isLoadingMainFrame()).to.be.true('isLoadingMainFrame'); }); }); + + // FIXME: enable this test on Linux as well. + ifdescribe(process.platform === 'darwin')('visibleOnAllWorkspaces state', () => { + describe('with properties', () => { + it('can be changed', () => { + const w = new BrowserWindow({ show: false }); + expect(w.visibleOnAllWorkspaces).to.be.false(); + w.visibleOnAllWorkspaces = true; + expect(w.visibleOnAllWorkspaces).to.be.true(); + }); + }); + + describe('with functions', () => { + it('can be changed', () => { + const w = new BrowserWindow({ show: false }); + expect(w.isVisibleOnAllWorkspaces()).to.be.false(); + w.setVisibleOnAllWorkspaces(true); + expect(w.isVisibleOnAllWorkspaces()).to.be.true(); + }); + }); + }); }); ifdescribe(process.platform !== 'linux')('window states (excluding Linux)', () => { @@ -5447,26 +5468,6 @@ describe('BrowserWindow module', () => { }); }); - describe('visibleOnAllWorkspaces state', () => { - it('with properties', () => { - it('can be changed', () => { - const w = new BrowserWindow({ show: false }); - expect(w.visibleOnAllWorkspaces).to.be.false(); - w.visibleOnAllWorkspaces = true; - expect(w.visibleOnAllWorkspaces).to.be.true(); - }); - }); - - it('with functions', () => { - it('can be changed', () => { - const w = new BrowserWindow({ show: false }); - expect(w.isVisibleOnAllWorkspaces()).to.be.false(); - w.setVisibleOnAllWorkspaces(true); - expect(w.isVisibleOnAllWorkspaces()).to.be.true(); - }); - }); - }); - ifdescribe(process.platform === 'darwin')('documentEdited state', () => { it('with properties', () => { it('can be changed', () => { From dcd8224c156636441eddc50db7b280bed7119aac Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:07:32 -0500 Subject: [PATCH 179/339] fix: fullscreen fillet / recovery is incorrect (#46848) * fix: fullscreen fillet / recovery is incorrect Signed-off-by: ZOY\zoy-l Co-authored-by: ZOY\zoy-l * fix: maintain frameless consistency on windows 11 Co-authored-by: ZOY\zoy-l * fix: maintain frameless consistency on windows 11 Co-authored-by: ZOY\zoy-l * chore: modify the comments Co-authored-by: ZOY\zoy-l --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: ZOY\zoy-l --- shell/browser/native_window_views.cc | 33 +++++++++++++++++++--------- shell/browser/native_window_views.h | 3 +++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index b294a94731770..90e838d9992ce 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -374,14 +374,17 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, frame_style |= WS_MINIMIZEBOX; if (maximizable_) frame_style |= WS_MAXIMIZEBOX; + // We should not show a frame for transparent window. - if (!thick_frame_) + if (!thick_frame_) { frame_style &= ~(WS_THICKFRAME | WS_CAPTION); - ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, frame_style); + rounded_corner_ = false; + } else { + options.Get(options::kRoundedCorners, &rounded_corner_); + } - bool rounded_corner = true; - options.Get(options::kRoundedCorners, &rounded_corner); - SetRoundedCorners(rounded_corner); + ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, frame_style); + SetRoundedCorners(rounded_corner_); } LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); @@ -646,10 +649,12 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { void NativeWindowViews::Maximize() { #if BUILDFLAG(IS_WIN) if (IsTranslucent()) { - // If a window is translucent but not transparent on Windows, - // that means it must have a backgroundMaterial set. - if (!transparent()) + // Semi-transparent windows with backgroundMaterial not set to 'none', and + // not fully transparent, require manual handling of rounded corners when + // maximized. + if (rounded_corner_) SetRoundedCorners(false); + restore_bounds_ = GetBounds(); auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( GetNativeWindow()); @@ -678,7 +683,8 @@ void NativeWindowViews::Unmaximize() { NotifyWindowUnmaximize(); if (transparent()) { UpdateThickFrame(); - } else { + } + if (rounded_corner_) { SetRoundedCorners(true); } return; @@ -724,7 +730,8 @@ void NativeWindowViews::Restore() { NotifyWindowRestore(); if (transparent()) { UpdateThickFrame(); - } else { + } + if (rounded_corner_) { SetRoundedCorners(true); } return; @@ -758,6 +765,12 @@ void NativeWindowViews::SetFullScreen(bool fullscreen) { NotifyWindowLeaveFullScreen(); } + // If round corners are enabled, + // they need to be set based on whether the window is fullscreen. + if (rounded_corner_) { + SetRoundedCorners(!fullscreen); + } + // For window without WS_THICKFRAME style, we can not call SetFullscreen(). // This path will be used for transparent windows as well. if (!thick_frame_) { diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 7c77d7764a6b7..5bc9db1ea7bb8 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -287,6 +287,9 @@ class NativeWindowViews : public NativeWindow, HWND legacy_window_ = nullptr; bool layered_ = false; + // This value is determined when the window is created. + bool rounded_corner_ = true; + // Set to true if the window is always on top and behind the task bar. bool behind_task_bar_ = false; From 069ca16b9eb2ecae3abd22eaa67e1cf43a653132 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:39:57 -0500 Subject: [PATCH 180/339] test: refactor deprecate-helpers.ts to warning-helpers.ts (#46837) Add a generic expectWarningMessages and start checking warning names Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- spec/api-native-image-spec.ts | 2 +- spec/extensions-spec.ts | 16 +++++----- spec/lib/deprecate-helpers.ts | 26 --------------- spec/lib/warning-helpers.ts | 60 +++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 35 deletions(-) delete mode 100644 spec/lib/deprecate-helpers.ts create mode 100644 spec/lib/warning-helpers.ts diff --git a/spec/api-native-image-spec.ts b/spec/api-native-image-spec.ts index 72a5ffeaee52b..737e064a926f3 100644 --- a/spec/api-native-image-spec.ts +++ b/spec/api-native-image-spec.ts @@ -4,8 +4,8 @@ import { expect } from 'chai'; import * as path from 'node:path'; -import { expectDeprecationMessages } from './lib/deprecate-helpers'; import { ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers'; +import { expectDeprecationMessages } from './lib/warning-helpers'; describe('nativeImage module', () => { const fixturesPath = path.join(__dirname, 'fixtures'); diff --git a/spec/extensions-spec.ts b/spec/extensions-spec.ts index 92dc26980cfbc..fe246b05ad0ca 100644 --- a/spec/extensions-spec.ts +++ b/spec/extensions-spec.ts @@ -10,6 +10,7 @@ import * as path from 'node:path'; import { emittedNTimes, emittedUntil } from './lib/events-helpers'; import { ifit, listen, waitUntil } from './lib/spec-helpers'; +import { expectWarningMessages } from './lib/warning-helpers'; import { closeAllWindows, closeWindow, cleanupWebContents } from './lib/window-helpers'; const uuid = require('uuid'); @@ -95,14 +96,13 @@ describe('chrome extensions', () => { it('recognize malformed host permissions', async () => { await w.loadURL(url); - const extPath = path.join(fixtures, 'extensions', 'host-permissions', 'malformed'); - customSession.extensions.loadExtension(extPath); - - const warning = await new Promise(resolve => { process.on('warning', resolve); }); - - const malformedHost = /URL pattern 'malformed_host' is malformed/; - - expect(warning).to.match(malformedHost); + await expectWarningMessages( + async () => { + const extPath = path.join(fixtures, 'extensions', 'host-permissions', 'malformed'); + await customSession.extensions.loadExtension(extPath); + }, + { name: 'ExtensionLoadWarning', message: /URL pattern 'malformed_host' is malformed/ } + ); }); it('can grant special privileges to urls with host permissions', async () => { diff --git a/spec/lib/deprecate-helpers.ts b/spec/lib/deprecate-helpers.ts deleted file mode 100644 index 524e656dd56d9..0000000000000 --- a/spec/lib/deprecate-helpers.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { expect } from 'chai'; - -export async function expectDeprecationMessages (func: () => any, ...expected: string[]) { - const messages: string[] = []; - - const originalWarn = console.warn; - console.warn = (message) => { - messages.push(message); - }; - - const warningListener = (error: Error) => { - messages.push(error.message); - }; - - process.on('warning', warningListener); - - try { - return await func(); - } finally { - // process.emitWarning seems to need us to wait a tick - await new Promise(process.nextTick); - console.warn = originalWarn; - process.off('warning' as any, warningListener); - expect(messages).to.deep.equal(expected); - } -} diff --git a/spec/lib/warning-helpers.ts b/spec/lib/warning-helpers.ts new file mode 100644 index 0000000000000..46241117a1f73 --- /dev/null +++ b/spec/lib/warning-helpers.ts @@ -0,0 +1,60 @@ +import { expect } from 'chai'; + +type ExpectedWarningMessage = RegExp | string; + +async function expectWarnings ( + func: () => any, + expected: { name: string, message: ExpectedWarningMessage }[] +) { + const messages: { name: string, message: string }[] = []; + + const originalWarn = console.warn; + console.warn = (message) => { + messages.push({ name: 'Warning', message }); + }; + + const warningListener = (error: Error) => { + messages.push({ name: error.name, message: error.message }); + }; + + process.on('warning', warningListener); + + try { + return await func(); + } finally { + // process.emitWarning seems to need us to wait a tick + await new Promise(process.nextTick); + console.warn = originalWarn; + process.off('warning' as any, warningListener); + expect(messages).to.have.lengthOf(expected.length); + for (const [idx, { name, message }] of messages.entries()) { + expect(name).to.equal(expected[idx].name); + if (expected[idx].message instanceof RegExp) { + expect(message).to.match(expected[idx].message); + } else { + expect(message).to.equal(expected[idx].message); + } + } + } +} + +export async function expectWarningMessages ( + func: () => any, + ...expected: ({ name: string, message: ExpectedWarningMessage } | ExpectedWarningMessage)[] +) { + return expectWarnings(func, expected.map((message) => { + if (typeof message === 'string' || message instanceof RegExp) { + return { name: 'Warning', message }; + } else { + return message; + } + })); +} + +export async function expectDeprecationMessages ( + func: () => any, ...expected: ExpectedWarningMessage[] +) { + return expectWarnings( + func, expected.map((message) => ({ name: 'DeprecationWarning', message })) + ); +} From 98380468bb8052cd6f9b1a206429643e177cb85a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 29 Apr 2025 13:08:53 -0500 Subject: [PATCH 181/339] fix: Linux `visibleOnAllWorkspaces` property (#46850) * test: do not skip visibleOnAllWorkspaces tests on Windows That feature is supported on Linux, so move the test from the "window states (excluding Linux)" section into the "window states" section. Co-authored-by: Charles Kerr * chore: disable visibleOnAllWorkspaces test on Linux * fix: NativeWindowViews::IsVisibleOnAllWorkspaces * test: enable visibleOnAllWorkspaces tests on Linux --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> --- shell/browser/native_window_views.cc | 7 +++++++ spec/api-browser-window-spec.ts | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 90e838d9992ce..be8e5e124f581 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1538,6 +1538,13 @@ void NativeWindowViews::SetVisibleOnAllWorkspaces( } bool NativeWindowViews::IsVisibleOnAllWorkspaces() const { + // NB: Electron >= 37 has a better long-term fix, but it also has an edge + // case which is a breaking change. The code here is dirtier (e.g. accessing + // a method marked as private) to avoid that edge case. More info @ + // https://github.com/electron/electron/pull/46834#issuecomment-2836287699 + if (const auto* view_native_widget = widget()->native_widget_private()) + return view_native_widget->IsVisibleOnAllWorkspaces(); + #if BUILDFLAG(IS_LINUX) if (IsX11()) { // Use the presence/absence of _NET_WM_STATE_STICKY in _NET_WM_STATE to diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 163be971dab9f..7eacf395b2669 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -5408,8 +5408,7 @@ describe('BrowserWindow module', () => { }); }); - // FIXME: enable this test on Linux as well. - ifdescribe(process.platform === 'darwin')('visibleOnAllWorkspaces state', () => { + ifdescribe(process.platform !== 'win32')('visibleOnAllWorkspaces state', () => { describe('with properties', () => { it('can be changed', () => { const w = new BrowserWindow({ show: false }); From 643b35503fe78a2d843a0f82671795f5c171b2b6 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:19:07 -0400 Subject: [PATCH 182/339] chore: bump chromium to 136.0.7103.49 (36-x-y) (#46870) chore: bump chromium in DEPS to 136.0.7103.49 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 287c049ac1c4c..1350957645393 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.48', + '136.0.7103.49', 'node_version': 'v22.14.0', 'nan_version': From 77b945dd0a6a7a7ce9f987865c2db2ef48e907cd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:16:41 -0500 Subject: [PATCH 183/339] fix: enable some tests that were accidentally disabled (#46844) * fix: tests that were not run in api-app-spec due to nested it() Co-authored-by: Charles Kerr * fix: tests that were not run in api-browser-window-spec due to nested it() Co-authored-by: Charles Kerr * chore: annotate disabled test Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/api-app-spec.ts | 6 ++--- spec/api-browser-window-spec.ts | 41 +++++++++++++++++---------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index 810c94e5382e2..e3bb800e45581 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -84,7 +84,7 @@ describe('app module', () => { }); describe('app name APIs', () => { - it('with properties', () => { + describe('with properties', () => { it('returns the name field of package.json', () => { expect(app.name).to.equal('Electron Test Main'); }); @@ -98,7 +98,7 @@ describe('app module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('returns the name field of package.json', () => { expect(app.getName()).to.equal('Electron Test Main'); }); @@ -988,7 +988,7 @@ describe('app module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can set accessibility support enabled', () => { expect(app.isAccessibilitySupportEnabled()).to.eql(false); diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 7eacf395b2669..bf4e2988c261d 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -2102,13 +2102,14 @@ describe('BrowserWindow module', () => { }); ifdescribe(process.platform === 'win32')('Fullscreen state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with the fullscreen constructor option', () => { w = new BrowserWindow({ fullscreen: true }); expect(w.fullScreen).to.be.true(); }); - it('does not go fullscreen if roundedCorners are enabled', async () => { + // FIXME: this test needs to be fixed and re-enabled. + it.skip('does not go fullscreen if roundedCorners are enabled', async () => { w = new BrowserWindow({ frame: false, roundedCorners: false, fullscreen: true }); expect(w.fullScreen).to.be.false(); }); @@ -2186,7 +2187,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with the fullscreen constructor option', () => { w = new BrowserWindow({ fullscreen: true }); expect(w.isFullScreen()).to.be.true(); @@ -5434,7 +5435,7 @@ describe('BrowserWindow module', () => { afterEach(closeAllWindows); describe('movable state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with movable constructor option', () => { const w = new BrowserWindow({ show: false, movable: false }); expect(w.movable).to.be.false('movable'); @@ -5450,7 +5451,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with movable constructor option', () => { const w = new BrowserWindow({ show: false, movable: false }); expect(w.isMovable()).to.be.false('movable'); @@ -5468,7 +5469,7 @@ describe('BrowserWindow module', () => { }); ifdescribe(process.platform === 'darwin')('documentEdited state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be changed', () => { const w = new BrowserWindow({ show: false }); expect(w.documentEdited).to.be.false(); @@ -5477,7 +5478,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be changed', () => { const w = new BrowserWindow({ show: false }); expect(w.isDocumentEdited()).to.be.false(); @@ -5508,7 +5509,7 @@ describe('BrowserWindow module', () => { }); describe('native window title', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with title constructor option', () => { const w = new BrowserWindow({ show: false, title: 'mYtItLe' }); expect(w.title).to.eql('mYtItLe'); @@ -5522,7 +5523,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with minimizable constructor option', () => { const w = new BrowserWindow({ show: false, title: 'mYtItLe' }); expect(w.getTitle()).to.eql('mYtItLe'); @@ -5538,7 +5539,7 @@ describe('BrowserWindow module', () => { }); describe('minimizable state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with minimizable constructor option', () => { const w = new BrowserWindow({ show: false, minimizable: false }); expect(w.minimizable).to.be.false('minimizable'); @@ -5554,7 +5555,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with minimizable constructor option', () => { const w = new BrowserWindow({ show: false, minimizable: false }); expect(w.isMinimizable()).to.be.false('movable'); @@ -5640,7 +5641,7 @@ describe('BrowserWindow module', () => { }); ifdescribe(process.platform === 'win32')('maximizable state', () => { - it('with properties', () => { + describe('with properties', () => { it('is reset to its former state', () => { const w = new BrowserWindow({ show: false }); w.maximizable = false; @@ -5654,7 +5655,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('is reset to its former state', () => { const w = new BrowserWindow({ show: false }); w.setMaximizable(false); @@ -5767,7 +5768,7 @@ describe('BrowserWindow module', () => { }); ifdescribe(process.platform === 'darwin')('fullscreenable state', () => { - it('with functions', () => { + describe('with functions', () => { it('can be set with fullscreenable constructor option', () => { const w = new BrowserWindow({ show: false, fullscreenable: false }); expect(w.isFullScreenable()).to.be.false('isFullScreenable'); @@ -5834,7 +5835,7 @@ describe('BrowserWindow module', () => { }); ifdescribe(process.platform === 'darwin')('isHiddenInMissionControl state', () => { - it('with functions', () => { + describe('with functions', () => { it('can be set with ignoreMissionControl constructor option', () => { const w = new BrowserWindow({ show: false, hiddenInMissionControl: true }); expect(w.isHiddenInMissionControl()).to.be.true('isHiddenInMissionControl'); @@ -5854,7 +5855,7 @@ describe('BrowserWindow module', () => { // fullscreen events are dispatched eagerly and twiddling things too fast can confuse poor Electron ifdescribe(process.platform === 'darwin')('kiosk state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with a constructor property', () => { const w = new BrowserWindow({ kiosk: true }); expect(w.kiosk).to.be.true(); @@ -5875,7 +5876,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with a constructor property', () => { const w = new BrowserWindow({ kiosk: true }); expect(w.isKiosk()).to.be.true(); @@ -6174,7 +6175,7 @@ describe('BrowserWindow module', () => { }); describe('closable state', () => { - it('with properties', () => { + describe('with properties', () => { it('can be set with closable constructor option', () => { const w = new BrowserWindow({ show: false, closable: false }); expect(w.closable).to.be.false('closable'); @@ -6190,7 +6191,7 @@ describe('BrowserWindow module', () => { }); }); - it('with functions', () => { + describe('with functions', () => { it('can be set with closable constructor option', () => { const w = new BrowserWindow({ show: false, closable: false }); expect(w.isClosable()).to.be.false('isClosable'); @@ -6208,7 +6209,7 @@ describe('BrowserWindow module', () => { }); describe('hasShadow state', () => { - it('with properties', () => { + describe('with properties', () => { it('returns a boolean on all platforms', () => { const w = new BrowserWindow({ show: false }); expect(w.shadow).to.be.a('boolean'); From ee65ab75f5d36f78812256abb0dc73cc217ad0a9 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 1 May 2025 09:22:43 +0900 Subject: [PATCH 184/339] fix: display id order validation on certain versions of Windows 10 (#46874) --- patches/chromium/.patches | 1 + ...order_of_display_id_order_on_windows.patch | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index a0203f5cba639..542f273eea961 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -147,3 +147,4 @@ fix_linter_error.patch revert_enable_crel_for_arm32_targets.patch mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch fix_osr_stutter_fix_backport_for_electron.patch +do_not_check_the_order_of_display_id_order_on_windows.patch diff --git a/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch b/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch new file mode 100644 index 0000000000000..372b667ff6fb3 --- /dev/null +++ b/patches/chromium/do_not_check_the_order_of_display_id_order_on_windows.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mitsuru Oshima +Date: Thu, 17 Apr 2025 16:49:21 -0700 +Subject: Do not check the order of Display ID order on Windows + +Id is generated by Windows side logic so this rule isn't applicable. + +Bug: 394622418 +Change-Id: I79c7f91103c6b752b6a7a123aacd3573a9ab815f +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6471333 +Reviewed-by: Vincent Chiang +Commit-Queue: Mitsuru Oshima +Cr-Commit-Position: refs/heads/main@{#1448671} + +diff --git a/ui/display/display_layout.cc b/ui/display/display_layout.cc +index 0d3746d8174868b743990b5ab10b3506819ef0ea..85d5c06851148576ab4dd272918215335292b4aa 100644 +--- a/ui/display/display_layout.cc ++++ b/ui/display/display_layout.cc +@@ -500,6 +500,7 @@ void DisplayLayout::ApplyToDisplayList(Displays* display_list, + if (!DisplayLayout::Validate(DisplayListToDisplayIdList(*display_list), + *this)) { + // Prevent invalid and non-relevant display layouts. ++ LOG(ERROR) << "Invalid Display Layout"; + return; + } + +@@ -549,15 +550,19 @@ bool DisplayLayout::Validate(const DisplayIdList& list, + + bool has_primary_as_parent = false; + // The placement list must be sorted by the first 8 bits of the display IDs. ++#if BUILDFLAG(IS_CHROMEOS) + int64_t prev_id = std::numeric_limits::min(); ++#endif // BUILDFLAG(IS_CHROMEOS) + for (const auto& placement : layout.placement_list) { +- // Placements are sorted by display_id. ++#if BUILDFLAG(IS_CHROMEOS) ++ // Placements are sorted by display_id on ChromeOS. + if (prev_id >= (placement.display_id & 0xFF)) { + DISPLAY_LOG(ERROR) << "PlacementList must be sorted by first 8 bits of" + << " display_id "; + return false; + } + prev_id = (placement.display_id & 0xFF); ++#endif // BUILDFLAG(IS_CHROMEOS) + if (placement.display_id == kInvalidDisplayId) { + DISPLAY_LOG(ERROR) << "display_id is not initialized"; + return false; +diff --git a/ui/display/display_layout_unittest.cc b/ui/display/display_layout_unittest.cc +index 68327c8a6b71853a0f1bf3c0351e38865bcbe054..4ea830ef086eb009c692a0b90b100eaaed303fd0 100644 +--- a/ui/display/display_layout_unittest.cc ++++ b/ui/display/display_layout_unittest.cc +@@ -122,6 +122,7 @@ TEST(DisplayLayoutTest, SwapPrimaryDisplayThreeDisplays) { + EXPECT_EQ(Position::RIGHT, layout->placement_list[1].position); + } + ++#if BUILDFLAG(IS_CHROMEOS) + // Makes sure that only the least significant 8 bits of the display IDs in the + // placement lists are used to validate their sort order. + TEST(DisplayLayoutTest, PlacementSortOrder) { +@@ -148,6 +149,8 @@ TEST(DisplayLayoutTest, PlacementSortOrder) { + EXPECT_TRUE(DisplayLayout::Validate({456, 0x0504, 0x0605, 0x0406}, *layout)); + } + ++#endif // BUILDFLAG(IS_CHROMEOS) ++ + namespace { + + class TwoDisplays From c24f330b4588f39a7619fa1563cfdf93cbcd7a6e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 09:44:59 -0500 Subject: [PATCH 185/339] refactor: add EmitDeprecationWarning helper (#46879) * refactor: add EmitDeprecationWarning helper Also switches EmitWarning to using Node's ProcessEmitWarningGeneric Co-authored-by: David Sanders * chore: use node namespace for function call Co-authored-by: David Sanders --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- shell/browser/api/electron_api_web_request.cc | 5 ++-- shell/browser/native_window_mac.mm | 5 ++-- shell/common/api/electron_api_native_image.cc | 5 ++-- shell/common/node_util.cc | 26 ++++++++++++------- shell/common/node_util.h | 12 ++++++++- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/shell/browser/api/electron_api_web_request.cc b/shell/browser/api/electron_api_web_request.cc index 095f96397aee5..c091e91489e2e 100644 --- a/shell/browser/api/electron_api_web_request.cc +++ b/shell/browser/api/electron_api_web_request.cc @@ -666,10 +666,9 @@ void WebRequest::SetListener(Event event, } if (filter_include_patterns.empty()) { - util::EmitWarning( + util::EmitDeprecationWarning( "The urls array in WebRequestFilter is empty, which is deprecated. " - "Please use '' to match all URLs.", - "DeprecationWarning"); + "Please use '' to match all URLs."); filter_include_patterns.insert(""); } diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index ff97e84c0b1a8..62b350fda7faf 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -182,9 +182,8 @@ static bool FromV8(v8::Isolate* isolate, #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (windowType == "textured" && (transparent() || !has_frame())) { - util::EmitWarning( - "The 'textured' window type is deprecated and will be removed", - "DeprecationWarning"); + util::EmitDeprecationWarning( + "The 'textured' window type is deprecated and will be removed"); styleMask |= NSWindowStyleMaskTexturedBackground; } #pragma clang diagnostic pop diff --git a/shell/common/api/electron_api_native_image.cc b/shell/common/api/electron_api_native_image.cc index da34a3158c3fe..ffe3267b2999a 100644 --- a/shell/common/api/electron_api_native_image.cc +++ b/shell/common/api/electron_api_native_image.cc @@ -286,9 +286,8 @@ v8::Local NativeImage::GetBitmap(gin::Arguments* args) { if (!deprecated_warning_issued) { deprecated_warning_issued = true; - util::EmitWarning(isolate_, - "getBitmap() is deprecated, use toBitmap() instead.", - "DeprecationWarning"); + util::EmitDeprecationWarning( + isolate_, "getBitmap() is deprecated, use toBitmap() instead."); } return ToBitmap(args); diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index 055670cc4ddfb..89eaf88942194 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -12,10 +12,10 @@ #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "gin/converter.h" -#include "gin/dictionary.h" #include "shell/browser/javascript_environment.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/node_includes.h" +#include "third_party/electron_node/src/node_process-inl.h" namespace electron::util { @@ -65,14 +65,22 @@ void EmitWarning(const std::string_view warning_msg, void EmitWarning(v8::Isolate* isolate, const std::string_view warning_msg, const std::string_view warning_type) { - v8::HandleScope scope{isolate}; - gin::Dictionary process{ - isolate, node::Environment::GetCurrent(isolate)->process_object()}; - base::RepeatingCallback - emit_warning; - process.Get("emitWarning", &emit_warning); - emit_warning.Run(warning_msg, warning_type, ""); + node::ProcessEmitWarningGeneric(node::Environment::GetCurrent(isolate), + warning_msg, warning_type); +} + +void EmitDeprecationWarning(const std::string_view warning_msg, + const std::string_view deprecation_code) { + EmitDeprecationWarning(JavascriptEnvironment::GetIsolate(), warning_msg, + deprecation_code); +} + +void EmitDeprecationWarning(v8::Isolate* isolate, + const std::string_view warning_msg, + const std::string_view deprecation_code) { + node::ProcessEmitWarningGeneric(node::Environment::GetCurrent(isolate), + warning_msg, "DeprecationWarning", + deprecation_code); } node::Environment* CreateEnvironment(v8::Isolate* isolate, diff --git a/shell/common/node_util.h b/shell/common/node_util.h index 36fcfdf940ba4..fdc92c0f6b9e5 100644 --- a/shell/common/node_util.h +++ b/shell/common/node_util.h @@ -30,9 +30,19 @@ void EmitWarning(v8::Isolate* isolate, std::string_view warning_type); // Emit a warning via node's process.emitWarning(), -// using JavscriptEnvironment's isolate +// using JavascriptEnvironment's isolate void EmitWarning(std::string_view warning_msg, std::string_view warning_type); +// Emit a deprecation warning via node's process.emitWarning() +void EmitDeprecationWarning(v8::Isolate* isolate, + std::string_view warning_msg, + std::string_view deprecation_code = ""); + +// Emit a deprecation warning via node's process.emitWarning(), +// using JavascriptEnvironment's isolate +void EmitDeprecationWarning(std::string_view warning_msg, + std::string_view deprecation_code = ""); + // Run a script with JS source bundled inside the binary as if it's wrapped // in a function called with a null receiver and arguments specified in C++. // The returned value is empty if an exception is encountered. From 3d4a4b934376cecd5560a7db96799dbc80995bd0 Mon Sep 17 00:00:00 2001 From: Aman Karmani Date: Thu, 1 May 2025 07:46:10 -0700 Subject: [PATCH 186/339] feat: add support for `--js-flags=--perf-prof` on macOS (#46876) * feat: add support for `--js-flags=--perf-prof` on macOS allows for profiling of macOS electron apps using samply[1], which can capture cpu usage for an entire process tree, including both c/c++ frames and interpreted + jit-compiled JS stack frames [1] https://github.com/mstange/samply * chore: update patches --------- Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> --- patches/v8/.patches | 1 + .../v8/enable_--perf-prof_flag_on_macos.patch | 465 ++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100644 patches/v8/enable_--perf-prof_flag_on_macos.patch diff --git a/patches/v8/.patches b/patches/v8/.patches index 280a34b936037..25d88a0b459c7 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,2 +1,3 @@ chore_allow_customizing_microtask_policy_per_context.patch deps_add_v8_object_setinternalfieldfornodecore.patch +enable_--perf-prof_flag_on_macos.patch diff --git a/patches/v8/enable_--perf-prof_flag_on_macos.patch b/patches/v8/enable_--perf-prof_flag_on_macos.patch new file mode 100644 index 0000000000000..34ee2009b623e --- /dev/null +++ b/patches/v8/enable_--perf-prof_flag_on_macos.patch @@ -0,0 +1,465 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: yangwenming +Date: Wed, 23 Apr 2025 22:14:00 +0800 +Subject: Enable --perf-prof flag on MacOS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +MacOS actually can share the implementation of {PerfJitLogger} with +Linux. From the issue 40112126, only Fuzzer tests on Windows ran +into UNREACHABLE/FATAL because of the unimplemented {PerfJitLogger}. +This CL enables the flag --perf-prof on MacOS. + +Bug: 403765219 +Change-Id: I97871fbcc0cb9890c51ca14fd7a6e65bd0e3c0d2 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6385655 +Reviewed-by: Clemens Backes +Reviewed-by: Matthias Liedtke +Auto-Submit: 杨文明 +Commit-Queue: Clemens Backes +Cr-Commit-Position: refs/heads/main@{#99885} + +diff --git a/src/compiler/backend/code-generator.cc b/src/compiler/backend/code-generator.cc +index 023cee8098e232b67acbed311c899732854cc01f..ad557facb9d46814273a67136878c9fbdfe6197e 100644 +--- a/src/compiler/backend/code-generator.cc ++++ b/src/compiler/backend/code-generator.cc +@@ -435,7 +435,7 @@ void CodeGenerator::AssembleCode() { + } + } + +- // The LinuxPerfJitLogger logs code up until here, excluding the safepoint ++ // The PerfJitLogger logs code up until here, excluding the safepoint + // table. Resolve the unwinding info now so it is aware of the same code + // size as reported by perf. + unwinding_info_writer_.Finish(masm()->pc_offset()); +diff --git a/src/diagnostics/perf-jit.cc b/src/diagnostics/perf-jit.cc +index 6af13fe8cf29c4ab10fab2107d10de7df8722f10..c509e4220c8e164594665afdea97cc75f13da05c 100644 +--- a/src/diagnostics/perf-jit.cc ++++ b/src/diagnostics/perf-jit.cc +@@ -30,8 +30,8 @@ + #include "src/common/assert-scope.h" + #include "src/flags/flags.h" + +-// Only compile the {LinuxPerfJitLogger} on Linux. +-#if V8_OS_LINUX ++// Only compile the {PerfJitLogger} on Linux & Darwin. ++#if V8_OS_LINUX || V8_OS_DARWIN + + #include + #include +@@ -118,22 +118,22 @@ struct PerfJitCodeUnwindingInfo : PerfJitBase { + // Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data. + }; + +-const char LinuxPerfJitLogger::kFilenameFormatString[] = "%s/jit-%d.dump"; ++const char PerfJitLogger::kFilenameFormatString[] = "%s/jit-%d.dump"; + + // Extra padding for the PID in the filename +-const int LinuxPerfJitLogger::kFilenameBufferPadding = 16; ++const int PerfJitLogger::kFilenameBufferPadding = 16; + + static const char kStringTerminator[] = {'\0'}; + + // The following static variables are protected by + // GetFileMutex(). +-int LinuxPerfJitLogger::process_id_ = 0; +-uint64_t LinuxPerfJitLogger::reference_count_ = 0; +-void* LinuxPerfJitLogger::marker_address_ = nullptr; +-uint64_t LinuxPerfJitLogger::code_index_ = 0; +-FILE* LinuxPerfJitLogger::perf_output_handle_ = nullptr; ++int PerfJitLogger::process_id_ = 0; ++uint64_t PerfJitLogger::reference_count_ = 0; ++void* PerfJitLogger::marker_address_ = nullptr; ++uint64_t PerfJitLogger::code_index_ = 0; ++FILE* PerfJitLogger::perf_output_handle_ = nullptr; + +-void LinuxPerfJitLogger::OpenJitDumpFile() { ++void PerfJitLogger::OpenJitDumpFile() { + // Open the perf JIT dump file. + perf_output_handle_ = nullptr; + +@@ -153,8 +153,17 @@ void LinuxPerfJitLogger::OpenJitDumpFile() { + if (v8_flags.perf_prof_delete_file) + CHECK_EQ(0, unlink(perf_dump_name.begin())); + ++ // On Linux, call OpenMarkerFile so that perf knows about the file path via ++ // an MMAP record. ++ // On macOS, don't call OpenMarkerFile because samply has already detected ++ // the file path during the call to `open` above (it interposes `open` with ++ // a preloaded library), and because the mmap call can be slow. ++#if V8_OS_DARWIN ++ marker_address_ = nullptr; ++#else + marker_address_ = OpenMarkerFile(fd); + if (marker_address_ == nullptr) return; ++#endif + + perf_output_handle_ = fdopen(fd, "w+"); + if (perf_output_handle_ == nullptr) return; +@@ -162,13 +171,13 @@ void LinuxPerfJitLogger::OpenJitDumpFile() { + setvbuf(perf_output_handle_, nullptr, _IOFBF, kLogBufferSize); + } + +-void LinuxPerfJitLogger::CloseJitDumpFile() { ++void PerfJitLogger::CloseJitDumpFile() { + if (perf_output_handle_ == nullptr) return; + base::Fclose(perf_output_handle_); + perf_output_handle_ = nullptr; + } + +-void* LinuxPerfJitLogger::OpenMarkerFile(int fd) { ++void* PerfJitLogger::OpenMarkerFile(int fd) { + long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) + if (page_size == -1) return nullptr; + +@@ -180,15 +189,14 @@ void* LinuxPerfJitLogger::OpenMarkerFile(int fd) { + return (marker_address == MAP_FAILED) ? nullptr : marker_address; + } + +-void LinuxPerfJitLogger::CloseMarkerFile(void* marker_address) { ++void PerfJitLogger::CloseMarkerFile(void* marker_address) { + if (marker_address == nullptr) return; + long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) + if (page_size == -1) return; + munmap(marker_address, page_size); + } + +-LinuxPerfJitLogger::LinuxPerfJitLogger(Isolate* isolate) +- : CodeEventLogger(isolate) { ++PerfJitLogger::PerfJitLogger(Isolate* isolate) : CodeEventLogger(isolate) { + base::LockGuard guard_file(GetFileMutex().Pointer()); + process_id_ = base::OS::GetCurrentProcessId(); + +@@ -201,7 +209,7 @@ LinuxPerfJitLogger::LinuxPerfJitLogger(Isolate* isolate) + } + } + +-LinuxPerfJitLogger::~LinuxPerfJitLogger() { ++PerfJitLogger::~PerfJitLogger() { + base::LockGuard guard_file(GetFileMutex().Pointer()); + + reference_count_--; +@@ -211,16 +219,11 @@ LinuxPerfJitLogger::~LinuxPerfJitLogger() { + } + } + +-uint64_t LinuxPerfJitLogger::GetTimestamp() { +- struct timespec ts; +- int result = clock_gettime(CLOCK_MONOTONIC, &ts); +- DCHECK_EQ(0, result); +- USE(result); +- static const uint64_t kNsecPerSec = 1000000000; +- return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec; ++uint64_t PerfJitLogger::GetTimestamp() { ++ return base::TimeTicks::Now().since_origin().InNanoseconds(); + } + +-void LinuxPerfJitLogger::LogRecordedBuffer( ++void PerfJitLogger::LogRecordedBuffer( + Tagged abstract_code, + MaybeDirectHandle maybe_sfi, const char* name, + size_t length) { +@@ -264,8 +267,8 @@ void LinuxPerfJitLogger::LogRecordedBuffer( + } + + #if V8_ENABLE_WEBASSEMBLY +-void LinuxPerfJitLogger::LogRecordedBuffer(const wasm::WasmCode* code, +- const char* name, size_t length) { ++void PerfJitLogger::LogRecordedBuffer(const wasm::WasmCode* code, ++ const char* name, size_t length) { + base::LockGuard guard_file(GetFileMutex().Pointer()); + + if (perf_output_handle_ == nullptr) return; +@@ -277,10 +280,9 @@ void LinuxPerfJitLogger::LogRecordedBuffer(const wasm::WasmCode* code, + } + #endif // V8_ENABLE_WEBASSEMBLY + +-void LinuxPerfJitLogger::WriteJitCodeLoadEntry(const uint8_t* code_pointer, +- uint32_t code_size, +- const char* name, +- size_t name_length) { ++void PerfJitLogger::WriteJitCodeLoadEntry(const uint8_t* code_pointer, ++ uint32_t code_size, const char* name, ++ size_t name_length) { + PerfJitCodeLoad code_load; + code_load.event_ = PerfJitCodeLoad::kLoad; + code_load.size_ = +@@ -342,8 +344,8 @@ SourcePositionInfo GetSourcePositionInfo( + + } // namespace + +-void LinuxPerfJitLogger::LogWriteDebugInfo( +- Tagged code, DirectHandle shared) { ++void PerfJitLogger::LogWriteDebugInfo(Tagged code, ++ DirectHandle shared) { + // Line ends of all scripts have been initialized prior to this. + DisallowGarbageCollection no_gc; + // The WasmToJS wrapper stubs have source position entries. +@@ -425,7 +427,7 @@ void LinuxPerfJitLogger::LogWriteDebugInfo( + } + + #if V8_ENABLE_WEBASSEMBLY +-void LinuxPerfJitLogger::LogWriteDebugInfo(const wasm::WasmCode* code) { ++void PerfJitLogger::LogWriteDebugInfo(const wasm::WasmCode* code) { + if (code->IsAnonymous()) { + return; + } +@@ -497,7 +499,7 @@ void LinuxPerfJitLogger::LogWriteDebugInfo(const wasm::WasmCode* code) { + } + #endif // V8_ENABLE_WEBASSEMBLY + +-void LinuxPerfJitLogger::LogWriteUnwindingInfo(Tagged code) { ++void PerfJitLogger::LogWriteUnwindingInfo(Tagged code) { + PerfJitCodeUnwindingInfo unwinding_info_header; + unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo; + unwinding_info_header.time_stamp_ = GetTimestamp(); +@@ -532,13 +534,13 @@ void LinuxPerfJitLogger::LogWriteUnwindingInfo(Tagged code) { + LogWriteBytes(padding_bytes, padding_size); + } + +-void LinuxPerfJitLogger::LogWriteBytes(const char* bytes, size_t size) { ++void PerfJitLogger::LogWriteBytes(const char* bytes, size_t size) { + size_t rv = fwrite(bytes, 1, size, perf_output_handle_); + DCHECK_EQ(size, rv); + USE(rv); + } + +-void LinuxPerfJitLogger::LogWriteHeader() { ++void PerfJitLogger::LogWriteHeader() { + DCHECK_NOT_NULL(perf_output_handle_); + PerfJitHeader header; + +@@ -559,4 +561,4 @@ void LinuxPerfJitLogger::LogWriteHeader() { + } // namespace internal + } // namespace v8 + +-#endif // V8_OS_LINUX ++#endif // V8_OS_LINUX || V8_OS_DARWIN +diff --git a/src/diagnostics/perf-jit.h b/src/diagnostics/perf-jit.h +index 84f669ca3f98690264e1ee05bfe6842c47bbcbd0..e95c3bb6fe0f445904df9dff3e6fe35735cd7045 100644 +--- a/src/diagnostics/perf-jit.h ++++ b/src/diagnostics/perf-jit.h +@@ -30,8 +30,8 @@ + + #include "include/v8config.h" + +-// {LinuxPerfJitLogger} is only implemented on Linux. +-#if V8_OS_LINUX ++// {PerfJitLogger} is only implemented on Linux & Darwin. ++#if V8_OS_LINUX || V8_OS_DARWIN + + #include "src/logging/log.h" + +@@ -39,10 +39,10 @@ namespace v8 { + namespace internal { + + // Linux perf tool logging support. +-class LinuxPerfJitLogger : public CodeEventLogger { ++class PerfJitLogger : public CodeEventLogger { + public: +- explicit LinuxPerfJitLogger(Isolate* isolate); +- ~LinuxPerfJitLogger() override; ++ explicit PerfJitLogger(Isolate* isolate); ++ ~PerfJitLogger() override; + + void CodeMoveEvent(Tagged from, + Tagged to) override { +@@ -143,6 +143,6 @@ class LinuxPerfJitLogger : public CodeEventLogger { + } // namespace internal + } // namespace v8 + +-#endif // V8_OS_LINUX ++#endif // V8_OS_LINUX || V8_OS_DARWIN + + #endif // V8_DIAGNOSTICS_PERF_JIT_H_ +diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h +index dd713090c674585c23f9b9574df7ed258c0cd23e..8c12e97c4e256fb8bc6ef51c7abd6a926df7b008 100644 +--- a/src/flags/flag-definitions.h ++++ b/src/flags/flag-definitions.h +@@ -3262,7 +3262,7 @@ DEFINE_IMPLICATION(prof, log_code) + + DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.") + +-#if V8_OS_LINUX ++#if V8_OS_LINUX || V8_OS_DARWIN + #define DEFINE_PERF_PROF_BOOL(nam, cmt) DEFINE_BOOL(nam, false, cmt) + #define DEFINE_PERF_PROF_IMPLICATION DEFINE_IMPLICATION + #else +@@ -3279,7 +3279,7 @@ DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.") + #endif + + DEFINE_PERF_PROF_BOOL(perf_basic_prof, +- "Enable perf linux profiler (basic support).") ++ "Enable basic support for perf profiler.") + DEFINE_NEG_IMPLICATION(perf_basic_prof, compact_code_space) + DEFINE_STRING(perf_basic_prof_path, DEFAULT_PERF_BASIC_PROF_PATH, + "directory to write perf-.map symbol file to") +@@ -3288,8 +3288,8 @@ DEFINE_PERF_PROF_BOOL( + "Only report function code ranges to perf (i.e. no stubs).") + DEFINE_PERF_PROF_IMPLICATION(perf_basic_prof_only_functions, perf_basic_prof) + +-DEFINE_PERF_PROF_BOOL( +- perf_prof, "Enable perf linux profiler (experimental annotate support).") ++DEFINE_PERF_PROF_BOOL(perf_prof, ++ "Enable experimental annotate support for perf profiler.") + DEFINE_STRING(perf_prof_path, DEFAULT_PERF_PROF_PATH, + "directory to write jit-.dump symbol file to") + DEFINE_PERF_PROF_BOOL( +diff --git a/src/logging/log.cc b/src/logging/log.cc +index ea07e176391b027287f007dadd44e64db8f62c3d..fe4096d4dadcc314274d2892c6ba1ed25b426b29 100644 +--- a/src/logging/log.cc ++++ b/src/logging/log.cc +@@ -350,12 +350,12 @@ void CodeEventLogger::RegExpCodeCreateEvent(DirectHandle code, + name_buffer_->get(), name_buffer_->size()); + } + +-// Linux perf tool logging support. +-#if V8_OS_LINUX +-class LinuxPerfBasicLogger : public CodeEventLogger { ++// Linux & Darwin perf tool logging support. ++#if V8_OS_LINUX || V8_OS_DARWIN ++class PerfBasicLogger : public CodeEventLogger { + public: +- explicit LinuxPerfBasicLogger(Isolate* isolate); +- ~LinuxPerfBasicLogger() override; ++ explicit PerfBasicLogger(Isolate* isolate); ++ ~PerfBasicLogger() override; + + void CodeMoveEvent(Tagged from, + Tagged to) override {} +@@ -388,21 +388,20 @@ class LinuxPerfBasicLogger : public CodeEventLogger { + }; + + // Extra space for the "perf-%d.map" filename, including the PID. +-const int LinuxPerfBasicLogger::kFilenameBufferPadding = 32; ++const int PerfBasicLogger::kFilenameBufferPadding = 32; + + // static +-base::LazyRecursiveMutex& LinuxPerfBasicLogger::GetFileMutex() { ++base::LazyRecursiveMutex& PerfBasicLogger::GetFileMutex() { + static base::LazyRecursiveMutex file_mutex = LAZY_RECURSIVE_MUTEX_INITIALIZER; + return file_mutex; + } + + // The following static variables are protected by +-// LinuxPerfBasicLogger::GetFileMutex(). +-uint64_t LinuxPerfBasicLogger::reference_count_ = 0; +-FILE* LinuxPerfBasicLogger::perf_output_handle_ = nullptr; ++// PerfBasicLogger::GetFileMutex(). ++uint64_t PerfBasicLogger::reference_count_ = 0; ++FILE* PerfBasicLogger::perf_output_handle_ = nullptr; + +-LinuxPerfBasicLogger::LinuxPerfBasicLogger(Isolate* isolate) +- : CodeEventLogger(isolate) { ++PerfBasicLogger::PerfBasicLogger(Isolate* isolate) : CodeEventLogger(isolate) { + base::LockGuard guard_file(GetFileMutex().Pointer()); + int process_id_ = base::OS::GetCurrentProcessId(); + reference_count_++; +@@ -424,7 +423,7 @@ LinuxPerfBasicLogger::LinuxPerfBasicLogger(Isolate* isolate) + } + } + +-LinuxPerfBasicLogger::~LinuxPerfBasicLogger() { ++PerfBasicLogger::~PerfBasicLogger() { + base::LockGuard guard_file(GetFileMutex().Pointer()); + reference_count_--; + +@@ -436,9 +435,9 @@ LinuxPerfBasicLogger::~LinuxPerfBasicLogger() { + } + } + +-void LinuxPerfBasicLogger::WriteLogRecordedBuffer(uintptr_t address, +- size_t size, const char* name, +- size_t name_length) { ++void PerfBasicLogger::WriteLogRecordedBuffer(uintptr_t address, size_t size, ++ const char* name, ++ size_t name_length) { + // Linux perf expects hex literals without a leading 0x, while some + // implementations of printf might prepend one when using the %p format + // for pointers, leading to wrongly formatted JIT symbols maps. On the other +@@ -456,9 +455,9 @@ void LinuxPerfBasicLogger::WriteLogRecordedBuffer(uintptr_t address, + #endif + } + +-void LinuxPerfBasicLogger::LogRecordedBuffer( +- Tagged code, MaybeDirectHandle, +- const char* name, size_t length) { ++void PerfBasicLogger::LogRecordedBuffer(Tagged code, ++ MaybeDirectHandle, ++ const char* name, size_t length) { + DisallowGarbageCollection no_gc; + PtrComprCageBase cage_base(isolate_); + if (v8_flags.perf_basic_prof_only_functions && +@@ -472,13 +471,13 @@ void LinuxPerfBasicLogger::LogRecordedBuffer( + } + + #if V8_ENABLE_WEBASSEMBLY +-void LinuxPerfBasicLogger::LogRecordedBuffer(const wasm::WasmCode* code, +- const char* name, size_t length) { ++void PerfBasicLogger::LogRecordedBuffer(const wasm::WasmCode* code, ++ const char* name, size_t length) { + WriteLogRecordedBuffer(static_cast(code->instruction_start()), + code->instructions().length(), name, length); + } + #endif // V8_ENABLE_WEBASSEMBLY +-#endif // V8_OS_LINUX ++#endif // V8_OS_LINUX || V8_OS_DARWIN + + // External LogEventListener + ExternalLogEventListener::ExternalLogEventListener(Isolate* isolate) +@@ -2304,14 +2303,14 @@ bool V8FileLogger::SetUp(Isolate* isolate) { + PrepareLogFileName(log_file_name, isolate, v8_flags.logfile); + log_file_ = std::make_unique(this, log_file_name.str()); + +-#if V8_OS_LINUX ++#if V8_OS_LINUX || V8_OS_DARWIN + if (v8_flags.perf_basic_prof) { +- perf_basic_logger_ = std::make_unique(isolate); ++ perf_basic_logger_ = std::make_unique(isolate); + CHECK(logger()->AddListener(perf_basic_logger_.get())); + } + + if (v8_flags.perf_prof) { +- perf_jit_logger_ = std::make_unique(isolate); ++ perf_jit_logger_ = std::make_unique(isolate); + CHECK(logger()->AddListener(perf_jit_logger_.get())); + } + #else +@@ -2457,7 +2456,7 @@ FILE* V8FileLogger::TearDownAndGetLogFile() { + ticker_.reset(); + timer_.Stop(); + +-#if V8_OS_LINUX ++#if V8_OS_LINUX || V8_OS_DARWIN + if (perf_basic_logger_) { + CHECK(logger()->RemoveListener(perf_basic_logger_.get())); + perf_basic_logger_.reset(); +diff --git a/src/logging/log.h b/src/logging/log.h +index ec39b1ec097a864b6f7aaf5625fd681d8b2437a9..b8b5c2be526a456e35b393e9ed8dfae74ef00eb1 100644 +--- a/src/logging/log.h ++++ b/src/logging/log.h +@@ -64,8 +64,8 @@ class Isolate; + class JitLogger; + class LogFile; + class LowLevelLogger; +-class LinuxPerfBasicLogger; +-class LinuxPerfJitLogger; ++class PerfBasicLogger; ++class PerfJitLogger; + class Profiler; + class SourcePosition; + class Ticker; +@@ -363,9 +363,9 @@ class V8FileLogger : public LogEventListener { + + std::atomic is_logging_; + std::unique_ptr log_file_; +-#if V8_OS_LINUX +- std::unique_ptr perf_basic_logger_; +- std::unique_ptr perf_jit_logger_; ++#if V8_OS_LINUX || V8_OS_DARWIN ++ std::unique_ptr perf_basic_logger_; ++ std::unique_ptr perf_jit_logger_; + #endif + std::unique_ptr ll_logger_; + std::unique_ptr jit_logger_; From 1687b95849ad8dda917f1c8aa034f131f4ff29a4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 14:57:32 -0700 Subject: [PATCH 187/339] fix: revert macOS content protection logic refactor (#46890) Revert "refactor: use upstream content protection logic on macOS (#46813)" This reverts commit 34adb976b632157379de34cc1a71bdf6cc089714. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond --- shell/browser/native_window.cc | 15 --------------- shell/browser/native_window.h | 4 ++-- shell/browser/native_window_mac.h | 2 ++ shell/browser/native_window_mac.mm | 9 +++++++++ shell/browser/native_window_views.cc | 15 +++++++++++++++ shell/browser/native_window_views.h | 2 ++ 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 50a76e3985308..d8a0e49c83f69 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -26,7 +26,6 @@ #include "shell/common/options_switches.h" #include "ui/base/hit_test.h" #include "ui/compositor/compositor.h" -#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #if !BUILDFLAG(IS_MAC) @@ -819,20 +818,6 @@ void NativeWindow::HandlePendingFullscreenTransitions() { SetFullScreen(next_transition); } -void NativeWindow::SetContentProtection(bool enable) { -#if !BUILDFLAG(IS_LINUX) - widget()->native_widget_private()->SetAllowScreenshots(!enable); -#endif -} - -bool NativeWindow::IsContentProtected() const { -#if !BUILDFLAG(IS_LINUX) - return !widget()->native_widget_private()->AreScreenshotsAllowed(); -#else // Not implemented on Linux - return false; -#endif -} - bool NativeWindow::IsTranslucent() const { // Transparent windows are translucent if (transparent()) { diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 7a9b762c60b2c..831cf8947aa85 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -186,8 +186,8 @@ class NativeWindow : public base::SupportsUserData, virtual void SetDocumentEdited(bool edited) {} virtual bool IsDocumentEdited() const; virtual void SetIgnoreMouseEvents(bool ignore, bool forward) = 0; - void SetContentProtection(bool enable); - bool IsContentProtected() const; + virtual void SetContentProtection(bool enable) = 0; + virtual bool IsContentProtected() const = 0; virtual void SetFocusable(bool focusable) {} virtual bool IsFocusable() const; virtual void SetMenu(ElectronMenuModel* menu) {} diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index fa51e9ee447d2..5420e14aecd7d 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -109,6 +109,8 @@ class NativeWindowMac : public NativeWindow, void SetIgnoreMouseEvents(bool ignore, bool forward) override; bool IsHiddenInMissionControl() const override; void SetHiddenInMissionControl(bool hidden) override; + void SetContentProtection(bool enable) override; + bool IsContentProtected() const override; void SetFocusable(bool focusable) override; bool IsFocusable() const override; void SetParentWindow(NativeWindow* parent) override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 62b350fda7faf..008c84a516b7f 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1170,6 +1170,15 @@ static bool FromV8(v8::Isolate* isolate, } } +void NativeWindowMac::SetContentProtection(bool enable) { + [window_ + setSharingType:enable ? NSWindowSharingNone : NSWindowSharingReadOnly]; +} + +bool NativeWindowMac::IsContentProtected() const { + return [window_ sharingType] == NSWindowSharingNone; +} + void NativeWindowMac::SetFocusable(bool focusable) { // No known way to unfocus the window if it had the focus. Here we do not // want to call Focus(false) because it moves the window to the back, i.e. diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index be8e5e124f581..d8f9210e17bfe 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -44,6 +44,7 @@ #include "ui/ozone/public/ozone_platform.h" #include "ui/views/background.h" #include "ui/views/controls/webview/webview.h" +#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #include "ui/views/window/client_view.h" #include "ui/wm/core/shadow_types.h" @@ -1324,6 +1325,20 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { #endif } +void NativeWindowViews::SetContentProtection(bool enable) { +#if BUILDFLAG(IS_WIN) + widget()->native_widget_private()->SetAllowScreenshots(!enable); +#endif +} + +bool NativeWindowViews::IsContentProtected() const { +#if BUILDFLAG(IS_WIN) + return !widget()->native_widget_private()->AreScreenshotsAllowed(); +#else // Not implemented on Linux + return false; +#endif +} + void NativeWindowViews::SetFocusable(bool focusable) { widget()->widget_delegate()->SetCanActivate(focusable); #if BUILDFLAG(IS_WIN) diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 5bc9db1ea7bb8..8ef7d2b783a2f 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -115,6 +115,8 @@ class NativeWindowViews : public NativeWindow, void SetOpacity(const double opacity) override; double GetOpacity() const override; void SetIgnoreMouseEvents(bool ignore, bool forward) override; + void SetContentProtection(bool enable) override; + bool IsContentProtected() const override; void SetFocusable(bool focusable) override; bool IsFocusable() const override; void SetMenu(ElectronMenuModel* menu_model) override; From 17e1ff26759234882029fb663baf6da50a97a0b3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 07:51:05 -0500 Subject: [PATCH 188/339] refactor: use `base::ObserverList::Notify()` (#46897) * refactor: use ObserverList::Notify() in shell/browser/window_list.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/web_contents_zoom_controller.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/usb/usb_chooser_context.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/usb/electron_usb_delegate.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/ui/views/menu_delegate.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/ui/tray_icon.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/ui/electron_menu_model.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/serial/serial_chooser_context.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/native_window.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/serial/electron_serial_delegate.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/browser.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/api/electron_api_web_contents.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/hid/electron_hid_delegate.cc Co-authored-by: Charles Kerr * refactor: use ObserverList::Notify() in shell/browser/hid/hid_chooser_context.cc Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../browser/api/electron_api_web_contents.cc | 13 +- shell/browser/browser.cc | 48 +++---- shell/browser/hid/electron_hid_delegate.cc | 16 +-- shell/browser/hid/hid_chooser_context.cc | 12 +- shell/browser/native_window.cc | 120 +++++++----------- .../serial/electron_serial_delegate.cc | 11 +- .../browser/serial/serial_chooser_context.cc | 7 +- shell/browser/ui/electron_menu_model.cc | 8 +- shell/browser/ui/tray_icon.cc | 54 +++----- shell/browser/ui/views/menu_delegate.cc | 9 +- shell/browser/usb/electron_usb_delegate.cc | 12 +- shell/browser/usb/usb_chooser_context.cc | 9 +- shell/browser/web_contents_zoom_controller.cc | 35 +++-- shell/browser/window_list.cc | 9 +- 14 files changed, 134 insertions(+), 229 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 1ca1efb084b38..83afa1e0e7933 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1299,8 +1299,7 @@ void WebContents::BeforeUnloadFired(content::WebContents* tab, void WebContents::SetContentsBounds(content::WebContents* source, const gfx::Rect& rect) { if (!Emit("content-bounds-updated", rect)) - for (ExtendedWebContentsObserver& observer : observers_) - observer.OnSetContentBounds(rect); + observers_.Notify(&ExtendedWebContentsObserver::OnSetContentBounds, rect); } void WebContents::CloseContents(content::WebContents* source) { @@ -1316,8 +1315,7 @@ void WebContents::CloseContents(content::WebContents* source) { } void WebContents::ActivateContents(content::WebContents* source) { - for (ExtendedWebContentsObserver& observer : observers_) - observer.OnActivateContents(); + observers_.Notify(&ExtendedWebContentsObserver::OnActivateContents); } void WebContents::UpdateTargetURL(content::WebContents* source, @@ -2112,8 +2110,8 @@ void WebContents::TitleWasSet(content::NavigationEntry* entry) { } else { final_title = web_contents()->GetTitle(); } - for (ExtendedWebContentsObserver& observer : observers_) - observer.OnPageTitleUpdated(final_title, explicit_set); + observers_.Notify(&ExtendedWebContentsObserver::OnPageTitleUpdated, + final_title, explicit_set); Emit("page-title-updated", final_title, explicit_set); } @@ -2169,8 +2167,7 @@ void WebContents::DevToolsClosed() { } void WebContents::DevToolsResized() { - for (ExtendedWebContentsObserver& observer : observers_) - observer.OnDevToolsResized(); + observers_.Notify(&ExtendedWebContentsObserver::OnDevToolsResized); } void WebContents::SetOwnerWindow(NativeWindow* owner_window) { diff --git a/shell/browser/browser.cc b/shell/browser/browser.cc index 711aa4c2b9c44..0c0e7c8c5076f 100644 --- a/shell/browser/browser.cc +++ b/shell/browser/browser.cc @@ -129,8 +129,7 @@ void Browser::Shutdown() { is_shutdown_ = true; is_quitting_ = true; - for (BrowserObserver& observer : observers_) - observer.OnQuit(); + observers_.Notify(&BrowserObserver::OnQuit); if (quit_main_message_loop_) { RunQuitClosure(std::move(quit_main_message_loop_)); @@ -165,25 +164,20 @@ void Browser::SetName(const std::string& name) { bool Browser::OpenFile(const std::string& file_path) { bool prevent_default = false; - for (BrowserObserver& observer : observers_) - observer.OnOpenFile(&prevent_default, file_path); - + observers_.Notify(&BrowserObserver::OnOpenFile, &prevent_default, file_path); return prevent_default; } void Browser::OpenURL(const std::string& url) { - for (BrowserObserver& observer : observers_) - observer.OnOpenURL(url); + observers_.Notify(&BrowserObserver::OnOpenURL, url); } void Browser::Activate(bool has_visible_windows) { - for (BrowserObserver& observer : observers_) - observer.OnActivate(has_visible_windows); + observers_.Notify(&BrowserObserver::OnActivate, has_visible_windows); } void Browser::WillFinishLaunching() { - for (BrowserObserver& observer : observers_) - observer.OnWillFinishLaunching(); + observers_.Notify(&BrowserObserver::OnWillFinishLaunching); } void Browser::DidFinishLaunching(base::Value::Dict launch_info) { @@ -201,6 +195,7 @@ void Browser::DidFinishLaunching(base::Value::Dict launch_info) { if (ready_promise_) { ready_promise_->Resolve(); } + for (BrowserObserver& observer : observers_) observer.OnFinishLaunching(launch_info.Clone()); } @@ -216,20 +211,15 @@ v8::Local Browser::WhenReady(v8::Isolate* isolate) { } void Browser::OnAccessibilitySupportChanged() { - for (BrowserObserver& observer : observers_) - observer.OnAccessibilitySupportChanged(); + observers_.Notify(&BrowserObserver::OnAccessibilitySupportChanged); } void Browser::PreMainMessageLoopRun() { - for (BrowserObserver& observer : observers_) { - observer.OnPreMainMessageLoopRun(); - } + observers_.Notify(&BrowserObserver::OnPreMainMessageLoopRun); } void Browser::PreCreateThreads() { - for (BrowserObserver& observer : observers_) { - observer.OnPreCreateThreads(); - } + observers_.Notify(&BrowserObserver::OnPreCreateThreads); } void Browser::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) { @@ -244,9 +234,7 @@ void Browser::NotifyAndShutdown() { return; bool prevent_default = false; - for (BrowserObserver& observer : observers_) - observer.OnWillQuit(&prevent_default); - + observers_.Notify(&BrowserObserver::OnWillQuit, &prevent_default); if (prevent_default) { is_quitting_ = false; return; @@ -257,9 +245,7 @@ void Browser::NotifyAndShutdown() { bool Browser::HandleBeforeQuit() { bool prevent_default = false; - for (BrowserObserver& observer : observers_) - observer.OnBeforeQuit(&prevent_default); - + observers_.Notify(&BrowserObserver::OnBeforeQuit, &prevent_default); return !prevent_default; } @@ -276,25 +262,21 @@ void Browser::OnWindowAllClosed() { } else if (is_quitting_) { NotifyAndShutdown(); } else { - for (BrowserObserver& observer : observers_) - observer.OnWindowAllClosed(); + observers_.Notify(&BrowserObserver::OnWindowAllClosed); } } #if BUILDFLAG(IS_MAC) void Browser::NewWindowForTab() { - for (BrowserObserver& observer : observers_) - observer.OnNewWindowForTab(); + observers_.Notify(&BrowserObserver::OnNewWindowForTab); } void Browser::DidBecomeActive() { - for (BrowserObserver& observer : observers_) - observer.OnDidBecomeActive(); + observers_.Notify(&BrowserObserver::OnDidBecomeActive); } void Browser::DidResignActive() { - for (BrowserObserver& observer : observers_) - observer.OnDidResignActive(); + observers_.Notify(&BrowserObserver::OnDidResignActive); } #endif diff --git a/shell/browser/hid/electron_hid_delegate.cc b/shell/browser/hid/electron_hid_delegate.cc index 3f0f6fd091f7e..268b2679f87fb 100644 --- a/shell/browser/hid/electron_hid_delegate.cc +++ b/shell/browser/hid/electron_hid_delegate.cc @@ -56,25 +56,25 @@ class ElectronHidDelegate::ContextObservation // HidChooserContext::DeviceObserver: void OnDeviceAdded(const device::mojom::HidDeviceInfo& device_info) override { - for (auto& observer : observer_list_) - observer.OnDeviceAdded(device_info); + observer_list_.Notify(&content::HidDelegate::Observer::OnDeviceAdded, + device_info); } void OnDeviceRemoved( const device::mojom::HidDeviceInfo& device_info) override { - for (auto& observer : observer_list_) - observer.OnDeviceRemoved(device_info); + observer_list_.Notify(&content::HidDelegate::Observer::OnDeviceRemoved, + device_info); } void OnDeviceChanged( const device::mojom::HidDeviceInfo& device_info) override { - for (auto& observer : observer_list_) - observer.OnDeviceChanged(device_info); + observer_list_.Notify(&content::HidDelegate::Observer::OnDeviceChanged, + device_info); } void OnHidManagerConnectionError() override { - for (auto& observer : observer_list_) - observer.OnHidManagerConnectionError(); + observer_list_.Notify( + &content::HidDelegate::Observer::OnHidManagerConnectionError); } void OnHidChooserContextShutdown() override { diff --git a/shell/browser/hid/hid_chooser_context.cc b/shell/browser/hid/hid_chooser_context.cc index 8c27a729ff580..e1bec890070da 100644 --- a/shell/browser/hid/hid_chooser_context.cc +++ b/shell/browser/hid/hid_chooser_context.cc @@ -247,8 +247,7 @@ void HidChooserContext::DeviceAdded(device::mojom::HidDeviceInfoPtr device) { devices_.insert({device->guid, device->Clone()}); // Notify all observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceAdded(*device); + device_observer_list_.Notify(&DeviceObserver::OnDeviceAdded, *device); } void HidChooserContext::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) { @@ -259,8 +258,7 @@ void HidChooserContext::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) { DCHECK_EQ(n_erased, 1U); // Notify all device observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceRemoved(*device); + device_observer_list_.Notify(&DeviceObserver::OnDeviceRemoved, *device); // Next we'll notify observers for revoked permissions. If the device does not // support persistent permissions then device permissions are revoked on @@ -281,8 +279,7 @@ void HidChooserContext::DeviceChanged(device::mojom::HidDeviceInfoPtr device) { mapped = device->Clone(); // Notify all observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceChanged(*device); + device_observer_list_.Notify(&DeviceObserver::OnDeviceChanged, *device); } void HidChooserContext::EnsureHidManagerConnection() { @@ -332,8 +329,7 @@ void HidChooserContext::OnHidManagerConnectionError() { ephemeral_devices_.clear(); // Notify all device observers. - for (auto& observer : device_observer_list_) - observer.OnHidManagerConnectionError(); + device_observer_list_.Notify(&DeviceObserver::OnHidManagerConnectionError); } } // namespace electron diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index d8a0e49c83f69..fb8a4314b68c5 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -500,23 +500,21 @@ void NativeWindow::SetWindowControlsOverlayRect(const gfx::Rect& overlay_rect) { } void NativeWindow::NotifyWindowRequestPreferredWidth(int* width) { - for (NativeWindowObserver& observer : observers_) - observer.RequestPreferredWidth(width); + observers_.Notify(&NativeWindowObserver::RequestPreferredWidth, width); } void NativeWindow::NotifyWindowCloseButtonClicked() { // First ask the observers whether we want to close. bool prevent_default = false; - for (NativeWindowObserver& observer : observers_) - observer.WillCloseWindow(&prevent_default); + observers_.Notify(&NativeWindowObserver::WillCloseWindow, &prevent_default); if (prevent_default) { WindowList::WindowCloseCancelled(this); return; } // Then ask the observers how should we close the window. - for (NativeWindowObserver& observer : observers_) - observer.OnCloseButtonClicked(&prevent_default); + observers_.Notify(&NativeWindowObserver::OnCloseButtonClicked, + &prevent_default); if (prevent_default) return; @@ -528,8 +526,7 @@ void NativeWindow::NotifyWindowClosed() { return; is_closed_ = true; - for (NativeWindowObserver& observer : observers_) - observer.OnWindowClosed(); + observers_.Notify(&NativeWindowObserver::OnWindowClosed); WindowList::RemoveWindow(this); } @@ -537,180 +534,153 @@ void NativeWindow::NotifyWindowClosed() { void NativeWindow::NotifyWindowQueryEndSession( const std::vector& reasons, bool* prevent_default) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowQueryEndSession(reasons, prevent_default); + observers_.Notify(&NativeWindowObserver::OnWindowQueryEndSession, reasons, + prevent_default); } void NativeWindow::NotifyWindowEndSession( const std::vector& reasons) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowEndSession(reasons); + observers_.Notify(&NativeWindowObserver::OnWindowEndSession, reasons); } void NativeWindow::NotifyWindowBlur() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowBlur(); + observers_.Notify(&NativeWindowObserver::OnWindowBlur); } void NativeWindow::NotifyWindowFocus() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowFocus(); + observers_.Notify(&NativeWindowObserver::OnWindowFocus); } void NativeWindow::NotifyWindowIsKeyChanged(bool is_key) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowIsKeyChanged(is_key); + observers_.Notify(&NativeWindowObserver::OnWindowIsKeyChanged, is_key); } void NativeWindow::NotifyWindowShow() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowShow(); + observers_.Notify(&NativeWindowObserver::OnWindowShow); } void NativeWindow::NotifyWindowHide() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowHide(); + observers_.Notify(&NativeWindowObserver::OnWindowHide); } void NativeWindow::NotifyWindowMaximize() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowMaximize(); + observers_.Notify(&NativeWindowObserver::OnWindowMaximize); } void NativeWindow::NotifyWindowUnmaximize() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowUnmaximize(); + observers_.Notify(&NativeWindowObserver::OnWindowUnmaximize); } void NativeWindow::NotifyWindowMinimize() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowMinimize(); + observers_.Notify(&NativeWindowObserver::OnWindowMinimize); } void NativeWindow::NotifyWindowRestore() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowRestore(); + observers_.Notify(&NativeWindowObserver::OnWindowRestore); } void NativeWindow::NotifyWindowWillResize(const gfx::Rect& new_bounds, const gfx::ResizeEdge& edge, bool* prevent_default) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowWillResize(new_bounds, edge, prevent_default); + observers_.Notify(&NativeWindowObserver::OnWindowWillResize, new_bounds, edge, + prevent_default); } void NativeWindow::NotifyWindowWillMove(const gfx::Rect& new_bounds, bool* prevent_default) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowWillMove(new_bounds, prevent_default); + observers_.Notify(&NativeWindowObserver::OnWindowWillMove, new_bounds, + prevent_default); } void NativeWindow::NotifyWindowResize() { NotifyLayoutWindowControlsOverlay(); - for (NativeWindowObserver& observer : observers_) - observer.OnWindowResize(); + observers_.Notify(&NativeWindowObserver::OnWindowResize); } void NativeWindow::NotifyWindowResized() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowResized(); + observers_.Notify(&NativeWindowObserver::OnWindowResized); } void NativeWindow::NotifyWindowMove() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowMove(); + observers_.Notify(&NativeWindowObserver::OnWindowMove); } void NativeWindow::NotifyWindowMoved() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowMoved(); + observers_.Notify(&NativeWindowObserver::OnWindowMoved); } void NativeWindow::NotifyWindowEnterFullScreen() { NotifyLayoutWindowControlsOverlay(); - for (NativeWindowObserver& observer : observers_) - observer.OnWindowEnterFullScreen(); + observers_.Notify(&NativeWindowObserver::OnWindowEnterFullScreen); } void NativeWindow::NotifyWindowSwipe(const std::string& direction) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowSwipe(direction); + observers_.Notify(&NativeWindowObserver::OnWindowSwipe, direction); } void NativeWindow::NotifyWindowRotateGesture(float rotation) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowRotateGesture(rotation); + observers_.Notify(&NativeWindowObserver::OnWindowRotateGesture, rotation); } void NativeWindow::NotifyWindowSheetBegin() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowSheetBegin(); + observers_.Notify(&NativeWindowObserver::OnWindowSheetBegin); } void NativeWindow::NotifyWindowSheetEnd() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowSheetEnd(); + observers_.Notify(&NativeWindowObserver::OnWindowSheetEnd); } void NativeWindow::NotifyWindowLeaveFullScreen() { NotifyLayoutWindowControlsOverlay(); - for (NativeWindowObserver& observer : observers_) - observer.OnWindowLeaveFullScreen(); + observers_.Notify(&NativeWindowObserver::OnWindowLeaveFullScreen); } void NativeWindow::NotifyWindowEnterHtmlFullScreen() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowEnterHtmlFullScreen(); + observers_.Notify(&NativeWindowObserver::OnWindowEnterHtmlFullScreen); } void NativeWindow::NotifyWindowLeaveHtmlFullScreen() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowLeaveHtmlFullScreen(); + observers_.Notify(&NativeWindowObserver::OnWindowLeaveHtmlFullScreen); } void NativeWindow::NotifyWindowAlwaysOnTopChanged() { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowAlwaysOnTopChanged(); + observers_.Notify(&NativeWindowObserver::OnWindowAlwaysOnTopChanged); } void NativeWindow::NotifyWindowExecuteAppCommand( const std::string_view command_name) { - for (NativeWindowObserver& observer : observers_) - observer.OnExecuteAppCommand(command_name); + observers_.Notify(&NativeWindowObserver::OnExecuteAppCommand, command_name); } void NativeWindow::NotifyTouchBarItemInteraction(const std::string& item_id, base::Value::Dict details) { - for (NativeWindowObserver& observer : observers_) - observer.OnTouchBarItemResult(item_id, details); + observers_.Notify(&NativeWindowObserver::OnTouchBarItemResult, item_id, + details); } void NativeWindow::NotifyNewWindowForTab() { - for (NativeWindowObserver& observer : observers_) - observer.OnNewWindowForTab(); + observers_.Notify(&NativeWindowObserver::OnNewWindowForTab); } void NativeWindow::NotifyWindowSystemContextMenu(int x, int y, bool* prevent_default) { - for (NativeWindowObserver& observer : observers_) - observer.OnSystemContextMenu(x, y, prevent_default); + observers_.Notify(&NativeWindowObserver::OnSystemContextMenu, x, y, + prevent_default); } void NativeWindow::NotifyLayoutWindowControlsOverlay() { - auto bounding_rect = GetWindowControlsOverlayRect(); - if (bounding_rect.has_value()) { - for (NativeWindowObserver& observer : observers_) - observer.UpdateWindowControlsOverlay(bounding_rect.value()); - } + if (const auto bounds = GetWindowControlsOverlayRect()) + observers_.Notify(&NativeWindowObserver::UpdateWindowControlsOverlay, + *bounds); } #if BUILDFLAG(IS_WIN) void NativeWindow::NotifyWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) { - for (NativeWindowObserver& observer : observers_) - observer.OnWindowMessage(message, w_param, l_param); + observers_.Notify(&NativeWindowObserver::OnWindowMessage, message, w_param, + l_param); } #endif diff --git a/shell/browser/serial/electron_serial_delegate.cc b/shell/browser/serial/electron_serial_delegate.cc index bd8f5c5a85c6c..5b6821d26522d 100644 --- a/shell/browser/serial/electron_serial_delegate.cc +++ b/shell/browser/serial/electron_serial_delegate.cc @@ -128,20 +128,19 @@ void ElectronSerialDelegate::DeleteControllerForFrame( // SerialChooserContext::PortObserver: void ElectronSerialDelegate::OnPortAdded( const device::mojom::SerialPortInfo& port) { - for (auto& observer : observer_list_) - observer.OnPortAdded(port); + observer_list_.Notify(&content::SerialDelegate::Observer::OnPortAdded, port); } void ElectronSerialDelegate::OnPortRemoved( const device::mojom::SerialPortInfo& port) { - for (auto& observer : observer_list_) - observer.OnPortRemoved(port); + observer_list_.Notify(&content::SerialDelegate::Observer::OnPortRemoved, + port); } void ElectronSerialDelegate::OnPortManagerConnectionError() { port_observation_.Reset(); - for (auto& observer : observer_list_) - observer.OnPortManagerConnectionError(); + observer_list_.Notify( + &content::SerialDelegate::Observer::OnPortManagerConnectionError); } void ElectronSerialDelegate::OnSerialChooserContextShutdown() { diff --git a/shell/browser/serial/serial_chooser_context.cc b/shell/browser/serial/serial_chooser_context.cc index a857f6464bf7b..dd399f3732047 100644 --- a/shell/browser/serial/serial_chooser_context.cc +++ b/shell/browser/serial/serial_chooser_context.cc @@ -233,15 +233,12 @@ void SerialChooserContext::OnPortAdded(device::mojom::SerialPortInfoPtr port) { ports.erase(port->token); } - for (auto& observer : port_observer_list_) - observer.OnPortAdded(*port); + port_observer_list_.Notify(&PortObserver::OnPortAdded, *port); } void SerialChooserContext::OnPortRemoved( device::mojom::SerialPortInfoPtr port) { - for (auto& observer : port_observer_list_) - observer.OnPortRemoved(*port); - + port_observer_list_.Notify(&PortObserver::OnPortRemoved, *port); port_info_.erase(port->token); } diff --git a/shell/browser/ui/electron_menu_model.cc b/shell/browser/ui/electron_menu_model.cc index e926d24e3bd19..fd854a6313bb3 100644 --- a/shell/browser/ui/electron_menu_model.cc +++ b/shell/browser/ui/electron_menu_model.cc @@ -101,16 +101,12 @@ void ElectronMenuModel::SetSharingItem(SharingItem item) { void ElectronMenuModel::MenuWillClose() { ui::SimpleMenuModel::MenuWillClose(); - for (Observer& observer : observers_) { - observer.OnMenuWillClose(); - } + observers_.Notify(&Observer::OnMenuWillClose); } void ElectronMenuModel::MenuWillShow() { ui::SimpleMenuModel::MenuWillShow(); - for (Observer& observer : observers_) { - observer.OnMenuWillShow(); - } + observers_.Notify(&Observer::OnMenuWillShow); } ElectronMenuModel* ElectronMenuModel::GetSubmenuModelAt(size_t index) { diff --git a/shell/browser/ui/tray_icon.cc b/shell/browser/ui/tray_icon.cc index 22de23fb65c5c..acb1b2fe5c19c 100644 --- a/shell/browser/ui/tray_icon.cc +++ b/shell/browser/ui/tray_icon.cc @@ -19,93 +19,75 @@ gfx::Rect TrayIcon::GetBounds() { void TrayIcon::NotifyClicked(const gfx::Rect& bounds, const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnClicked(bounds, location, modifiers); + observers_.Notify(&TrayIconObserver::OnClicked, bounds, location, modifiers); } void TrayIcon::NotifyDoubleClicked(const gfx::Rect& bounds, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnDoubleClicked(bounds, modifiers); + observers_.Notify(&TrayIconObserver::OnDoubleClicked, bounds, modifiers); } void TrayIcon::NotifyMiddleClicked(const gfx::Rect& bounds, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMiddleClicked(bounds, modifiers); + observers_.Notify(&TrayIconObserver::OnMiddleClicked, bounds, modifiers); } void TrayIcon::NotifyBalloonShow() { - for (TrayIconObserver& observer : observers_) - observer.OnBalloonShow(); + observers_.Notify(&TrayIconObserver::OnBalloonShow); } void TrayIcon::NotifyBalloonClicked() { - for (TrayIconObserver& observer : observers_) - observer.OnBalloonClicked(); + observers_.Notify(&TrayIconObserver::OnBalloonClicked); } void TrayIcon::NotifyBalloonClosed() { - for (TrayIconObserver& observer : observers_) - observer.OnBalloonClosed(); + observers_.Notify(&TrayIconObserver::OnBalloonClosed); } void TrayIcon::NotifyRightClicked(const gfx::Rect& bounds, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnRightClicked(bounds, modifiers); + observers_.Notify(&TrayIconObserver::OnRightClicked, bounds, modifiers); } void TrayIcon::NotifyDrop() { - for (TrayIconObserver& observer : observers_) - observer.OnDrop(); + observers_.Notify(&TrayIconObserver::OnDrop); } void TrayIcon::NotifyDropFiles(const std::vector& files) { - for (TrayIconObserver& observer : observers_) - observer.OnDropFiles(files); + observers_.Notify(&TrayIconObserver::OnDropFiles, files); } void TrayIcon::NotifyDropText(const std::string& text) { - for (TrayIconObserver& observer : observers_) - observer.OnDropText(text); + observers_.Notify(&TrayIconObserver::OnDropText, text); } void TrayIcon::NotifyMouseUp(const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMouseUp(location, modifiers); + observers_.Notify(&TrayIconObserver::OnMouseUp, location, modifiers); } void TrayIcon::NotifyMouseDown(const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMouseDown(location, modifiers); + observers_.Notify(&TrayIconObserver::OnMouseDown, location, modifiers); } void TrayIcon::NotifyMouseEntered(const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMouseEntered(location, modifiers); + observers_.Notify(&TrayIconObserver::OnMouseEntered, location, modifiers); } void TrayIcon::NotifyMouseExited(const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMouseExited(location, modifiers); + observers_.Notify(&TrayIconObserver::OnMouseExited, location, modifiers); } void TrayIcon::NotifyMouseMoved(const gfx::Point& location, int modifiers) { - for (TrayIconObserver& observer : observers_) - observer.OnMouseMoved(location, modifiers); + observers_.Notify(&TrayIconObserver::OnMouseMoved, location, modifiers); } void TrayIcon::NotifyDragEntered() { - for (TrayIconObserver& observer : observers_) - observer.OnDragEntered(); + observers_.Notify(&TrayIconObserver::OnDragEntered); } void TrayIcon::NotifyDragExited() { - for (TrayIconObserver& observer : observers_) - observer.OnDragExited(); + observers_.Notify(&TrayIconObserver::OnDragExited); } void TrayIcon::NotifyDragEnded() { - for (TrayIconObserver& observer : observers_) - observer.OnDragEnded(); + observers_.Notify(&TrayIconObserver::OnDragEnded); } } // namespace electron diff --git a/shell/browser/ui/views/menu_delegate.cc b/shell/browser/ui/views/menu_delegate.cc index 2d7ca15f2a86e..cf79c26f5ad3a 100644 --- a/shell/browser/ui/views/menu_delegate.cc +++ b/shell/browser/ui/views/menu_delegate.cc @@ -51,14 +51,12 @@ void MenuDelegate::RunMenu(ElectronMenuModel* model, } void MenuDelegate::ExecuteCommand(int id) { - for (Observer& obs : observers_) - obs.OnBeforeExecuteCommand(); + observers_.Notify(&Observer::OnBeforeExecuteCommand); adapter_->ExecuteCommand(id); } void MenuDelegate::ExecuteCommand(int id, int mouse_event_flags) { - for (Observer& obs : observers_) - obs.OnBeforeExecuteCommand(); + observers_.Notify(&Observer::OnBeforeExecuteCommand); adapter_->ExecuteCommand(id, mouse_event_flags); } @@ -104,8 +102,7 @@ void MenuDelegate::WillHideMenu(views::MenuItemView* menu) { } void MenuDelegate::OnMenuClosed(views::MenuItemView* menu) { - for (Observer& obs : observers_) - obs.OnMenuClosed(); + observers_.Notify(&Observer::OnMenuClosed); // Only switch to new menu when current menu is closed. if (button_to_open_) diff --git a/shell/browser/usb/electron_usb_delegate.cc b/shell/browser/usb/electron_usb_delegate.cc index 8d47c344197c6..1e80325db588f 100644 --- a/shell/browser/usb/electron_usb_delegate.cc +++ b/shell/browser/usb/electron_usb_delegate.cc @@ -102,19 +102,19 @@ class ElectronUsbDelegate::ContextObservation // UsbChooserContext::DeviceObserver: void OnDeviceAdded(const device::mojom::UsbDeviceInfo& device_info) override { - for (auto& observer : observer_list_) - observer.OnDeviceAdded(device_info); + observer_list_.Notify(&content::UsbDelegate::Observer::OnDeviceAdded, + device_info); } void OnDeviceRemoved( const device::mojom::UsbDeviceInfo& device_info) override { - for (auto& observer : observer_list_) - observer.OnDeviceRemoved(device_info); + observer_list_.Notify(&content::UsbDelegate::Observer::OnDeviceRemoved, + device_info); } void OnDeviceManagerConnectionError() override { - for (auto& observer : observer_list_) - observer.OnDeviceManagerConnectionError(); + observer_list_.Notify( + &content::UsbDelegate::Observer::OnDeviceManagerConnectionError); } void OnBrowserContextShutdown() override { diff --git a/shell/browser/usb/usb_chooser_context.cc b/shell/browser/usb/usb_chooser_context.cc index 42fd6b2e4284e..425f0a946669d 100644 --- a/shell/browser/usb/usb_chooser_context.cc +++ b/shell/browser/usb/usb_chooser_context.cc @@ -286,8 +286,7 @@ void UsbChooserContext::OnDeviceAdded( devices_.try_emplace(device_info->guid, device_info->Clone()); // Notify all observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceAdded(*device_info); + device_observer_list_.Notify(&DeviceObserver::OnDeviceAdded, *device_info); } void UsbChooserContext::OnDeviceRemoved( @@ -304,8 +303,7 @@ void UsbChooserContext::OnDeviceRemoved( DCHECK_EQ(n_erased, 1U); // Notify all device observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceRemoved(*device_info); + device_observer_list_.Notify(&DeviceObserver::OnDeviceRemoved, *device_info); // If the device was persistent, return. Otherwise, notify all permission // observers that its permissions were revoked. @@ -327,8 +325,7 @@ void UsbChooserContext::OnDeviceManagerConnectionError() { ephemeral_devices_.clear(); // Notify all device observers. - for (auto& observer : device_observer_list_) - observer.OnDeviceManagerConnectionError(); + device_observer_list_.Notify(&DeviceObserver::OnDeviceManagerConnectionError); } } // namespace electron diff --git a/shell/browser/web_contents_zoom_controller.cc b/shell/browser/web_contents_zoom_controller.cc index 64933e192d043..b4c237d68f7ac 100644 --- a/shell/browser/web_contents_zoom_controller.cc +++ b/shell/browser/web_contents_zoom_controller.cc @@ -48,9 +48,7 @@ WebContentsZoomController::WebContentsZoomController( WebContentsZoomController::~WebContentsZoomController() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - for (auto& observer : observers_) { - observer.OnZoomControllerDestroyed(this); - } + observers_.Notify(&WebContentsZoomObserver::OnZoomControllerDestroyed, this); } void WebContentsZoomController::AddObserver(WebContentsZoomObserver* observer) { @@ -90,8 +88,8 @@ bool WebContentsZoomController::SetZoomLevel(double level) { ZoomChangedEventData zoom_change_data(web_contents(), old_zoom_level, zoom_level_, true /* temporary */, zoom_mode_); - for (auto& observer : observers_) - observer.OnZoomChanged(zoom_change_data); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + zoom_change_data); return true; } @@ -110,8 +108,8 @@ bool WebContentsZoomController::SetZoomLevel(double level) { zoom_map->SetTemporaryZoomLevel(rfh_id, level); ZoomChangedEventData zoom_change_data(web_contents(), zoom_level_, level, true /* temporary */, zoom_mode_); - for (auto& observer : observers_) - observer.OnZoomChanged(zoom_change_data); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + zoom_change_data); } else { const GURL url = content::HostZoomMap::GetURLForRenderFrameHost(rfh_id); if (url.is_empty()) { @@ -148,8 +146,7 @@ void WebContentsZoomController::SetTemporaryZoomLevel(double level) { // Notify observers of zoom level changes. ZoomChangedEventData zoom_change_data(web_contents(), zoom_level_, level, true /* temporary */, zoom_mode_); - for (auto& observer : observers_) - observer.OnZoomChanged(zoom_change_data); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, zoom_change_data); } bool WebContentsZoomController::UsesTemporaryZoomLevel() { @@ -213,8 +210,8 @@ void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - for (auto& observer : observers_) - observer.OnZoomChanged(*event_data_); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + *event_data_); event_data_.reset(); } break; @@ -229,8 +226,8 @@ void WebContentsZoomController::SetZoomMode(ZoomMode new_mode) { } else { // When we don't call any HostZoomMap set functions, we send the event // manually. - for (auto& observer : observers_) - observer.OnZoomChanged(*event_data_); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + *event_data_); event_data_.reset(); } break; @@ -303,9 +300,7 @@ void WebContentsZoomController::WebContentsDestroyed() { DCHECK_CURRENTLY_ON(BrowserThread::UI); // At this point we should no longer be sending any zoom events with this // WebContents. - for (auto& observer : observers_) { - observer.OnZoomControllerDestroyed(this); - } + observers_.Notify(&WebContentsZoomObserver::OnZoomControllerDestroyed, this); embedder_zoom_controller_ = nullptr; } @@ -389,14 +384,14 @@ void WebContentsZoomController::UpdateState(const std::string& host) { // the change should be sent. ZoomChangedEventData zoom_change_data = *event_data_; event_data_.reset(); - for (auto& observer : observers_) - observer.OnZoomChanged(zoom_change_data); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + zoom_change_data); } else { double zoom_level = GetZoomLevel(); ZoomChangedEventData zoom_change_data(web_contents(), zoom_level, zoom_level, false, zoom_mode_); - for (auto& observer : observers_) - observer.OnZoomChanged(zoom_change_data); + observers_.Notify(&WebContentsZoomObserver::OnZoomChanged, + zoom_change_data); } } diff --git a/shell/browser/window_list.cc b/shell/browser/window_list.cc index 89a3ab1d4b9e2..188402d0e6bb1 100644 --- a/shell/browser/window_list.cc +++ b/shell/browser/window_list.cc @@ -57,16 +57,13 @@ void WindowList::RemoveWindow(NativeWindow* window) { WindowVector& windows = GetInstance()->windows_; std::erase(windows, window); - if (windows.empty()) { - for (WindowListObserver& observer : GetObservers()) - observer.OnWindowAllClosed(); - } + if (windows.empty()) + GetObservers().Notify(&WindowListObserver::OnWindowAllClosed); } // static void WindowList::WindowCloseCancelled(NativeWindow* window) { - for (WindowListObserver& observer : GetObservers()) - observer.OnWindowCloseCancelled(window); + GetObservers().Notify(&WindowListObserver::OnWindowCloseCancelled, window); } // static From bb4ab67e52c1cf07fa8fb36b47d12417d51e7f6a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 09:49:52 -0400 Subject: [PATCH 189/339] docs: clarified usage of createFromPath() (#46899) * docs: clarified usage of createFromPath() Co-authored-by: Yuri * Update docs/api/native-image.md Co-authored-by: Niklas Wenzel Co-authored-by: Yuri --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Yuri --- docs/api/native-image.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 14ee825618c30..58c9dadfd3c01 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -142,8 +142,8 @@ Note: The Windows implementation will ignore `size.height` and scale the height Returns `NativeImage` -Creates a new `NativeImage` instance from a file located at `path`. This method -returns an empty image if the `path` does not exist, cannot be read, or is not +Creates a new `NativeImage` instance from an image file (e.g., PNG or JPEG) located at `path`. +This method returns an empty image if the `path` does not exist, cannot be read, or is not a valid image. ```js From bac01a33cd737814dd369f2466111cd9f8ca9d4e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 12:50:20 +0200 Subject: [PATCH 190/339] fix: xdg portal version detection for file dialogs on linux (#46923) * chore: use dbus thread for portal version detection Co-authored-by: deepak1556 * Update shell/browser/ui/file_dialog_linux_portal.cc Co-authored-by: Robo Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 Co-authored-by: Charles Kerr --- filenames.gni | 1 + ...ing_dialog_features_to_shell_dialogs.patch | 115 ++++++----------- shell/browser/ui/file_dialog.h | 10 ++ shell/browser/ui/file_dialog_linux.cc | 9 +- shell/browser/ui/file_dialog_linux_portal.cc | 121 ++++++++++++++++++ 5 files changed, 171 insertions(+), 85 deletions(-) create mode 100644 shell/browser/ui/file_dialog_linux_portal.cc diff --git a/filenames.gni b/filenames.gni index 45fafbdb52eb5..50242a7e7a1c1 100644 --- a/filenames.gni +++ b/filenames.gni @@ -35,6 +35,7 @@ filenames = { "shell/browser/relauncher_linux.cc", "shell/browser/ui/electron_desktop_window_tree_host_linux.cc", "shell/browser/ui/file_dialog_linux.cc", + "shell/browser/ui/file_dialog_linux_portal.cc", "shell/browser/ui/gtk/menu_gtk.cc", "shell/browser/ui/gtk/menu_gtk.h", "shell/browser/ui/gtk/menu_util.cc", diff --git a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch index 681f039b6343b..3b127aae5909b 100644 --- a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch +++ b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch @@ -10,6 +10,8 @@ This CL adds support for the following features to //shell_dialogs: It also: * Changes XDG Portal implementation behavior to set default path regardless of dialog type. +* XDG Portal implementation calls into //electron to perform version checks on the dbus thread + Refs https://github.com/electron/electron/issues/46652. This may be partially upstreamed to Chromium in the future. @@ -345,83 +347,52 @@ index 9d45ec49a4fb5e12407b65b83c1ba0c13cd0dfd8..400cce91b020ecd5e48566f125515d2c + } // namespace ui diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a78df74ba 100644 +index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..e768afc05b42d4d026c88f1516d4e9db84e8dff6 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -@@ -12,6 +12,7 @@ - #include - - #include "base/check.h" -+#include "base/command_line.h" - #include "base/files/file_util.h" - #include "base/functional/bind.h" - #include "base/logging.h" -@@ -45,6 +46,8 @@ namespace { - constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop"; - constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop"; - -+// Version 4 includes support for current_folder option to the OpenFile method via -+// https://github.com/flatpak/xdg-desktop-portal/commit/71165a5. - constexpr int kXdgPortalRequiredVersion = 3; - - constexpr char kFileChooserInterfaceName[] = -@@ -66,6 +69,8 @@ constexpr uint32_t kFileChooserFilterKindGlob = 0; - - constexpr char kFileUriPrefix[] = "file://"; - -+const char kXdgPortalRequiredVersionFlag[] = "xdg-portal-required-version"; -+ - enum class ServiceAvailability { - kNotStarted, - kInProgress, -@@ -75,6 +80,9 @@ enum class ServiceAvailability { - - ServiceAvailability g_service_availability = ServiceAvailability::kNotStarted; - -+uint32_t g_available_portal_version = 0; -+uint32_t g_required_portal_version = kXdgPortalRequiredVersion; -+ - scoped_refptr& GetMainTaskRunner() { - static base::NoDestructor> - main_task_runner; -@@ -94,9 +102,10 @@ void OnGetPropertyReply(dbus::Response* response) { - return; - } - -- g_service_availability = version >= kXdgPortalRequiredVersion -+ g_service_availability = version >= g_required_portal_version - ? ServiceAvailability::kAvailable +@@ -28,6 +28,7 @@ + #include "dbus/message.h" + #include "dbus/object_path.h" + #include "dbus/object_proxy.h" ++#include "electron/shell/browser/ui/file_dialog.h" + #include "ui/aura/window_tree_host.h" + #include "ui/base/l10n/l10n_util.h" + #include "ui/gfx/native_widget_types.h" +@@ -99,7 +100,7 @@ void OnGetPropertyReply(dbus::Response* response) { : ServiceAvailability::kNotAvailable; -+ g_available_portal_version = version; } - void OnServiceStarted(std::optional service_started) { -@@ -164,6 +173,12 @@ void SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground() { - } - g_service_availability = ServiceAvailability::kInProgress; +-void OnServiceStarted(std::optional service_started) { ++[[maybe_unused]] void OnServiceStarted(std::optional service_started) { + if (!service_started.value_or(false)) { + g_service_availability = ServiceAvailability::kNotAvailable; + return; +@@ -166,18 +167,24 @@ void SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground() { -+ auto* cmd = base::CommandLine::ForCurrentProcess(); -+ if (!base::StringToUint(cmd->GetSwitchValueASCII(kXdgPortalRequiredVersionFlag), -+ &g_required_portal_version)) { -+ g_required_portal_version = kXdgPortalRequiredVersion; -+ } -+ GetMainTaskRunner() = base::SequencedTaskRunner::GetCurrentDefault(); ++#if 0 dbus_utils::CheckForServiceAndStart(dbus_thread_linux::GetSharedSessionBus(), -@@ -180,6 +195,11 @@ bool SelectFileDialogLinuxPortal::IsPortalAvailable() { + kXdgPortalService, + base::BindOnce(&OnServiceStarted)); ++#endif ++ file_dialog::StartPortalAvailabilityTestInBackground(); + } + + // static + bool SelectFileDialogLinuxPortal::IsPortalAvailable() { ++#if 0 + if (g_service_availability == ServiceAvailability::kInProgress) { + LOG(WARNING) << "Portal availability checked before test was complete"; + } + return g_service_availability == ServiceAvailability::kAvailable; ++#endif ++ return file_dialog::IsPortalAvailable(); } -+// static -+uint32_t SelectFileDialogLinuxPortal::GetPortalVersion() { -+ return g_available_portal_version; -+} -+ bool SelectFileDialogLinuxPortal::IsRunning( - gfx::NativeWindow parent_window) const { - return parent_window && host_ && host_.get() == parent_window->GetHost(); -@@ -382,11 +402,14 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( +@@ -382,11 +389,14 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( const PortalFilterSet& filter_set) { DbusDictionary dict; @@ -439,7 +410,7 @@ index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a [[fallthrough]]; case SelectFileDialog::SELECT_FOLDER: case SelectFileDialog::Type::SELECT_EXISTING_FOLDER: -@@ -399,6 +422,10 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( +@@ -399,6 +409,10 @@ DbusDictionary SelectFileDialogLinuxPortal::BuildOptionsDictionary( break; } @@ -450,17 +421,3 @@ index b23d357d4e21f10be82f0ea37b1ec3e959fc2c0b..d29de35ac813cc68b1faa11e803ace9a if (!default_path.empty()) { if (default_path_exists) { // If this is an existing directory, navigate to that directory, with no -diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.h b/ui/shell_dialogs/select_file_dialog_linux_portal.h -index 651684b1840eaff664f3d73d99bbea40e097c866..9a9d541f1e9586d9d545f8547d3f09ff33dce48d 100644 ---- a/ui/shell_dialogs/select_file_dialog_linux_portal.h -+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.h -@@ -45,6 +45,9 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { - // availability test has not yet completed. - static bool IsPortalAvailable(); - -+ // Get version of portal if available. -+ static uint32_t GetPortalVersion(); -+ - protected: - ~SelectFileDialogLinuxPortal() override; - diff --git a/shell/browser/ui/file_dialog.h b/shell/browser/ui/file_dialog.h index 5a6c4cadbbf22..b8858c06ecb3b 100644 --- a/shell/browser/ui/file_dialog.h +++ b/shell/browser/ui/file_dialog.h @@ -77,6 +77,16 @@ bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path); void ShowSaveDialog(const DialogSettings& settings, gin_helper::Promise promise); +#if BUILDFLAG(IS_LINUX) +// Rewrite of SelectFileDialogLinuxPortal equivalent functions with primary +// difference being that dbus_thread_linux::GetSharedSessionBus is not used +// so that version detection can be initiated and compeleted on the dbus thread +// Refs https://github.com/electron/electron/issues/46652 +void StartPortalAvailabilityTestInBackground(); +bool IsPortalAvailable(); +uint32_t GetPortalVersion(); +#endif + } // namespace file_dialog #endif // ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_ diff --git a/shell/browser/ui/file_dialog_linux.cc b/shell/browser/ui/file_dialog_linux.cc index 404aead64b5d9..732820aa193a9 100644 --- a/shell/browser/ui/file_dialog_linux.cc +++ b/shell/browser/ui/file_dialog_linux.cc @@ -18,7 +18,6 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/promise.h" #include "ui/shell_dialogs/select_file_dialog.h" -#include "ui/shell_dialogs/select_file_dialog_linux_portal.h" #include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/selected_file_info.h" @@ -60,11 +59,9 @@ ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) { } void LogIfNeededAboutUnsupportedPortalFeature(const DialogSettings& settings) { - if (!settings.default_path.empty() && - ui::SelectFileDialogLinuxPortal::IsPortalAvailable() && - ui::SelectFileDialogLinuxPortal::GetPortalVersion() < 4) { - LOG(INFO) << "Available portal version " - << ui::SelectFileDialogLinuxPortal::GetPortalVersion() + if (!settings.default_path.empty() && IsPortalAvailable() && + GetPortalVersion() < 4) { + LOG(INFO) << "Available portal version " << GetPortalVersion() << " does not support defaultPath option, try the non-portal" << " file chooser dialogs by launching with" << " --xdg-portal-required-version"; diff --git a/shell/browser/ui/file_dialog_linux_portal.cc b/shell/browser/ui/file_dialog_linux_portal.cc new file mode 100644 index 0000000000000..298ee25caffb4 --- /dev/null +++ b/shell/browser/ui/file_dialog_linux_portal.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2025 Microsoft, GmbH. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/ui/file_dialog.h" + +#include + +#include "base/command_line.h" +#include "base/functional/bind.h" +#include "base/logging.h" +#include "base/memory/scoped_refptr.h" +#include "base/no_destructor.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/atomic_flag.h" +#include "components/dbus/thread_linux/dbus_thread_linux.h" +#include "components/dbus/utils/check_for_service_and_start.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" +#include "dbus/object_proxy.h" +#include "dbus/property.h" + +namespace file_dialog { + +namespace { + +constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop"; +constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop"; +constexpr char kFileChooserInterfaceName[] = + "org.freedesktop.portal.FileChooser"; + +// Version 4 includes support for current_folder option to the OpenFile method +// via https://github.com/flatpak/xdg-desktop-portal/commit/71165a5. +uint32_t g_required_portal_version = 3; +uint32_t g_available_portal_version = 0; +constexpr char kXdgPortalRequiredVersionFlag[] = "xdg-portal-required-version"; + +bool g_portal_available = false; + +struct FileChooserProperties : dbus::PropertySet { + dbus::Property version; + + explicit FileChooserProperties(dbus::ObjectProxy* object_proxy) + : dbus::PropertySet(object_proxy, kFileChooserInterfaceName, {}) { + RegisterProperty("version", &version); + } + + ~FileChooserProperties() override = default; +}; + +base::AtomicFlag* GetAvailabilityTestCompletionFlag() { + static base::NoDestructor flag; + return flag.get(); +} + +void CheckPortalAvailabilityOnBusThread() { + auto* flag = GetAvailabilityTestCompletionFlag(); + if (flag->IsSet()) + return; + + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + options.connection_type = dbus::Bus::PRIVATE; + options.dbus_task_runner = dbus_thread_linux::GetTaskRunner(); + scoped_refptr bus = base::MakeRefCounted(options); + dbus_utils::CheckForServiceAndStart( + bus, kXdgPortalService, + base::BindOnce( + [](scoped_refptr bus, base::AtomicFlag* flag, + std::optional name_has_owner) { + if (name_has_owner.value_or(false)) { + // + dbus::ObjectPath portal_path(kXdgPortalObject); + dbus::ObjectProxy* portal = + bus->GetObjectProxy(kXdgPortalService, portal_path); + FileChooserProperties properties(portal); + if (!properties.GetAndBlock(&properties.version)) { + LOG(ERROR) << "Failed to read portal version property"; + } else if (properties.version.value() >= + g_required_portal_version) { + g_portal_available = true; + g_available_portal_version = properties.version.value(); + } + } + VLOG(1) << "File chooser portal available: " + << (g_portal_available ? "yes" : "no"); + flag->Set(); + bus->ShutdownAndBlock(); + }, + std::move(bus), flag)); +} + +} // namespace + +void StartPortalAvailabilityTestInBackground() { + if (GetAvailabilityTestCompletionFlag()->IsSet()) + return; + + const auto* cmd = base::CommandLine::ForCurrentProcess(); + if (!base::StringToUint( + cmd->GetSwitchValueASCII(kXdgPortalRequiredVersionFlag), + &g_required_portal_version)) { + VLOG(1) << "Unable to parse --xdg-portal-required-version"; + } + + dbus_thread_linux::GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&CheckPortalAvailabilityOnBusThread)); +} + +bool IsPortalAvailable() { + if (!GetAvailabilityTestCompletionFlag()->IsSet()) + LOG(WARNING) << "Portal availability checked before test was complete"; + + return g_portal_available; +} + +uint32_t GetPortalVersion() { + return g_available_portal_version; +} + +} // namespace file_dialog From 1b596f6261b50e47837eb051804165d6164309fa Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 12:50:30 +0200 Subject: [PATCH 191/339] fix: prevent log files being written to current directory on Windows (#46912) * fix: prevent log files being written to current directory on Windows Co-authored-by: Derek Cicerone * Update shell/common/logging.cc Co-authored-by: Robo Co-authored-by: Derek Cicerone <120135886+derekcicerone@users.noreply.github.com> * chore: add test Co-authored-by: deepak1556 * chore: update includes Refs https://chromium-review.googlesource.com/c/chromium/src/+/6418805 Co-authored-by: deepak1556 * chore: address review feedback Co-authored-by: deepak1556 * chore: update includes Refs https://chromium-review.googlesource.com/c/chromium/src/+/6418805 --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Derek Cicerone Co-authored-by: Derek Cicerone <120135886+derekcicerone@users.noreply.github.com> Co-authored-by: deepak1556 --- shell/common/api/electron_api_testing.cc | 8 +++ shell/common/logging.cc | 78 +++++++++++++++++++++--- spec/fixtures/log-test.js | 3 + spec/logging-spec.ts | 30 ++++++++- 4 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 spec/fixtures/log-test.js diff --git a/shell/common/api/electron_api_testing.cc b/shell/common/api/electron_api_testing.cc index 8d88fca564186..097da88e158b6 100644 --- a/shell/common/api/electron_api_testing.cc +++ b/shell/common/api/electron_api_testing.cc @@ -2,8 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "base/dcheck_is_on.h" #include "base/logging.h" +#include "content/public/common/content_switches.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_includes.h" #include "v8/include/v8.h" @@ -34,12 +36,18 @@ void Log(int severity, std::string text) { } } +std::string GetLoggingDestination() { + const auto* command_line = base::CommandLine::ForCurrentProcess(); + return command_line->GetSwitchValueASCII(switches::kEnableLogging); +} + void Initialize(v8::Local exports, v8::Local unused, v8::Local context, void* priv) { gin_helper::Dictionary dict(context->GetIsolate(), exports); dict.SetMethod("log", &Log); + dict.SetMethod("getLoggingDestination", &GetLoggingDestination); } } // namespace diff --git a/shell/common/logging.cc b/shell/common/logging.cc index 1b735fce687ec..8b2baea67da30 100644 --- a/shell/common/logging.cc +++ b/shell/common/logging.cc @@ -17,11 +17,41 @@ #include "chrome/common/chrome_paths.h" #include "content/public/common/content_switches.h" +#if BUILDFLAG(IS_WIN) +#include +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" +#include "sandbox/policy/switches.h" +#endif + namespace logging { constexpr std::string_view kLogFileName{"ELECTRON_LOG_FILE"}; constexpr std::string_view kElectronEnableLogging{"ELECTRON_ENABLE_LOGGING"}; +#if BUILDFLAG(IS_WIN) +base::win::ScopedHandle GetLogInheritedHandle( + const base::CommandLine& command_line) { + auto handle_str = command_line.GetSwitchValueNative(::switches::kLogFile); + uint32_t handle_value = 0; + if (!base::StringToUint(handle_str, &handle_value)) { + return {}; + } + // Duplicate the handle from the command line so that different things can + // init logging. This means the handle from the parent is never closed, but + // there will only be one of these in the process. + HANDLE log_handle = nullptr; + if (!::DuplicateHandle(::GetCurrentProcess(), + base::win::Uint32ToHandle(handle_value), + ::GetCurrentProcess(), &log_handle, 0, + /*bInheritHandle=*/FALSE, DUPLICATE_SAME_ACCESS)) { + return {}; + } + // Transfer ownership to the caller. + return base::win::ScopedHandle(log_handle); +} +#endif + base::FilePath GetLogFileName(const base::CommandLine& command_line) { std::string filename = command_line.GetSwitchValueASCII(switches::kLogFile); if (filename.empty()) @@ -47,9 +77,9 @@ bool HasExplicitLogFile(const base::CommandLine& command_line) { return !filename.empty(); } -LoggingDestination DetermineLoggingDestination( - const base::CommandLine& command_line, - bool is_preinit) { +std::pair +DetermineLoggingDestination(const base::CommandLine& command_line, + bool is_preinit) { bool enable_logging = false; std::string logging_destination; if (command_line.HasSwitch(::switches::kEnableLogging)) { @@ -64,7 +94,7 @@ LoggingDestination DetermineLoggingDestination( } } if (!enable_logging) - return LOG_NONE; + return {LOG_NONE, false}; bool also_log_to_stderr = false; #if !defined(NDEBUG) @@ -75,6 +105,16 @@ LoggingDestination DetermineLoggingDestination( also_log_to_stderr = true; #endif +#if BUILDFLAG(IS_WIN) + if (logging_destination == "handle" && + command_line.HasSwitch(::switches::kProcessType) && + command_line.HasSwitch(::switches::kLogFile)) { + // Child processes can log to a handle duplicated from the parent, and + // provided in the log-file switch value. + return {LOG_TO_FILE, true}; + } +#endif // BUILDFLAG(IS_WIN) + // --enable-logging logs to stderr, --enable-logging=file logs to a file. // NB. this differs from Chromium, in which --enable-logging logs to a file // and --enable-logging=stderr logs to stderr, because that's how Electron @@ -90,8 +130,8 @@ LoggingDestination DetermineLoggingDestination( // given. if (HasExplicitLogFile(command_line) || (logging_destination == "file" && !is_preinit)) - return LOG_TO_FILE | (also_log_to_stderr ? LOG_TO_STDERR : 0); - return LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR; + return {LOG_TO_FILE | (also_log_to_stderr ? LOG_TO_STDERR : 0), false}; + return {LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR, false}; } } // namespace @@ -100,10 +140,13 @@ void InitElectronLogging(const base::CommandLine& command_line, bool is_preinit) { const std::string process_type = command_line.GetSwitchValueASCII(::switches::kProcessType); - LoggingDestination logging_dest = + auto [logging_dest, filename_is_handle] = DetermineLoggingDestination(command_line, is_preinit); LogLockingState log_locking_state = LOCK_LOG_FILE; base::FilePath log_path; +#if BUILDFLAG(IS_WIN) + base::win::ScopedHandle log_handle; +#endif if (command_line.HasSwitch(::switches::kLoggingLevel) && GetMinLogLevel() >= 0) { @@ -121,7 +164,19 @@ void InitElectronLogging(const base::CommandLine& command_line, // Don't resolve the log path unless we need to. Otherwise we leave an open // ALPC handle after sandbox lockdown on Windows. if ((logging_dest & LOG_TO_FILE) != 0) { - log_path = GetLogFileName(command_line); + if (filename_is_handle) { +#if BUILDFLAG(IS_WIN) + // Child processes on Windows are provided a file handle if logging is + // enabled as sandboxed processes cannot open files. + log_handle = GetLogInheritedHandle(command_line); + if (!log_handle.is_valid()) { + LOG(ERROR) << "Unable to initialize logging from handle."; + return; + } +#endif + } else { + log_path = GetLogFileName(command_line); + } } else { log_locking_state = DONT_LOCK_LOG_FILE; } @@ -133,6 +188,13 @@ void InitElectronLogging(const base::CommandLine& command_line, LoggingSettings settings; settings.logging_dest = logging_dest; settings.log_file_path = log_path.value().c_str(); +#if BUILDFLAG(IS_WIN) + // Avoid initializing with INVALID_HANDLE_VALUE. + // This handle is owned by the logging framework and is closed when the + // process exits. + // TODO(crbug.com/328285906) Use a ScopedHandle in logging settings. + settings.log_file = log_handle.is_valid() ? log_handle.release() : nullptr; +#endif settings.lock_log = log_locking_state; // If we're logging to an explicit file passed with --log-file, we don't want // to delete the log file on our second initialization. diff --git a/spec/fixtures/log-test.js b/spec/fixtures/log-test.js new file mode 100644 index 0000000000000..840b66791e2dd --- /dev/null +++ b/spec/fixtures/log-test.js @@ -0,0 +1,3 @@ +const binding = process._linkedBinding('electron_common_testing'); +binding.log(1, 'CHILD_PROCESS_TEST_LOG'); +binding.log(1, `CHILD_PROCESS_DESTINATION_${binding.getLoggingDestination()}`); diff --git a/spec/logging-spec.ts b/spec/logging-spec.ts index cde8fdb170d0f..30258f9a7f47e 100644 --- a/spec/logging-spec.ts +++ b/spec/logging-spec.ts @@ -7,7 +7,7 @@ import { once } from 'node:events'; import * as fs from 'node:fs/promises'; import * as path from 'node:path'; -import { startRemoteControlApp, ifdescribe } from './lib/spec-helpers'; +import { startRemoteControlApp, ifdescribe, ifit } from './lib/spec-helpers'; function isTestingBindingAvailable () { try { @@ -127,6 +127,34 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { expect(contents).to.match(/TEST_LOG/); }); + ifit(process.platform === 'win32')('child process logs to the given file when --log-file is passed', async () => { + const logFilePath = path.join(app.getPath('temp'), 'test-log-file-' + uuid.v4()); + const preloadPath = path.resolve(__dirname, 'fixtures', 'log-test.js'); + const rc = await startRemoteControlApp(['--enable-logging', `--log-file=${logFilePath}`, `--boot-eval=preloadPath=${JSON.stringify(preloadPath)}`]); + rc.remotely(() => { + process._linkedBinding('electron_common_testing').log(0, 'MAIN_PROCESS_TEST_LOG'); + const { app, BrowserWindow } = require('electron'); + const w = new BrowserWindow({ + show: false, + webPreferences: { + preload: preloadPath, + additionalArguments: ['--unsafely-expose-electron-internals-for-testing'] + } + }); + w.loadURL('about:blank'); + w.webContents.once('did-finish-load', () => { + setTimeout(() => { app.quit(); }); + }); + }); + await once(rc.process, 'exit'); + const stat = await fs.stat(logFilePath); + expect(stat.isFile()).to.be.true(); + const contents = await fs.readFile(logFilePath, 'utf8'); + expect(contents).to.match(/MAIN_PROCESS_TEST_LOG/); + expect(contents).to.match(/CHILD_PROCESS_TEST_LOG/); + expect(contents).to.match(/CHILD_PROCESS_DESTINATION_handle/); + }); + it('logs to the given file when ELECTRON_LOG_FILE is set', async () => { const logFilePath = path.join(app.getPath('temp'), 'test-log-file-' + uuid.v4()); const rc = await startRemoteControlApp([], { env: { ...process.env, ELECTRON_ENABLE_LOGGING: '1', ELECTRON_LOG_FILE: logFilePath } }); From aada99e53d6ef6eca74c6d6efb6d5d05c67296e5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 12:59:31 -0400 Subject: [PATCH 192/339] refactor: devirtualize `NativeWindow` methods (#46930) * refactor: devirtualize NativeWindow::SetSize() refactor: devirtualize NativeWindow::GetSize() refactor: devirtualize NativeWindow::SetPosition() refactor: devirtualize NativeWindow::GetPosition() Co-authored-by: Charles Kerr * refactor: devirtualize NativeWinodw::SetMinimumSize() refactor: devirtualize NativeWinodw::GetMinimumSize() refactor: devirtualize NativeWinodw::SetMaximumSize() refactor: devirtualize NativeWinodw::GetMaximumSize() Co-authored-by: Charles Kerr * refactor: devirtualize NativeWindow::SetSheetOffset() refactor: devirtualize NativeWindow::GetSheetOffsetX() refactor: devirtualize NativeWindow::GetSheetOffsetY() Co-authored-by: Charles Kerr * refactor: devirtualize NativeWindow::GetContentMinimumSize() refactor: devirtualize NativeWindow::GetContentMaximumSize() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.h | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 831cf8947aa85..e434b8db7a690 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -103,10 +103,13 @@ class NativeWindow : public base::SupportsUserData, virtual bool IsFullscreen() const = 0; virtual void SetBounds(const gfx::Rect& bounds, bool animate = false) = 0; virtual gfx::Rect GetBounds() const = 0; - virtual void SetSize(const gfx::Size& size, bool animate = false); - virtual gfx::Size GetSize() const; - virtual void SetPosition(const gfx::Point& position, bool animate = false); - virtual gfx::Point GetPosition() const; + + void SetSize(const gfx::Size& size, bool animate = false); + [[nodiscard]] gfx::Size GetSize() const; + + void SetPosition(const gfx::Point& position, bool animate = false); + [[nodiscard]] gfx::Point GetPosition() const; + virtual void SetContentSize(const gfx::Size& size, bool animate = false); virtual gfx::Size GetContentSize() const; virtual void SetContentBounds(const gfx::Rect& bounds, bool animate = false); @@ -119,15 +122,20 @@ class NativeWindow : public base::SupportsUserData, virtual void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints); virtual extensions::SizeConstraints GetContentSizeConstraints() const; - virtual void SetMinimumSize(const gfx::Size& size); - virtual gfx::Size GetMinimumSize() const; - virtual void SetMaximumSize(const gfx::Size& size); - virtual gfx::Size GetMaximumSize() const; - virtual gfx::Size GetContentMinimumSize() const; - virtual gfx::Size GetContentMaximumSize() const; - virtual void SetSheetOffset(const double offsetX, const double offsetY); - virtual double GetSheetOffsetX() const; - virtual double GetSheetOffsetY() const; + + void SetMinimumSize(const gfx::Size& size); + [[nodiscard]] gfx::Size GetMinimumSize() const; + + void SetMaximumSize(const gfx::Size& size); + [[nodiscard]] gfx::Size GetMaximumSize() const; + + [[nodiscard]] gfx::Size GetContentMinimumSize() const; + [[nodiscard]] gfx::Size GetContentMaximumSize() const; + + void SetSheetOffset(const double offsetX, const double offsetY); + [[nodiscard]] double GetSheetOffsetX() const; + [[nodiscard]] double GetSheetOffsetY() const; + virtual void SetResizable(bool resizable) = 0; virtual bool MoveAbove(const std::string& sourceId) = 0; virtual void MoveTop() = 0; From c27bd7bb3c493a8b0f3f554e4d18a25a66778a46 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 5 May 2025 13:54:00 -0500 Subject: [PATCH 193/339] refactor: simplify `NativeWindow::FullScreenTransitionState` (36-x-y) (#46933) refactor: simplify `NativeWindow::FullScreenTransitionState` (#46918) * refactor: make NativeWindow::fullscreen_transition_state_ private * refactor: add NativeWindow::is_transitioning_fullscreen() helper * refactor: remove unused NativeWindow::fullscreen_transition_state() * refactor: replace NativeWindow::set_fullscreen_transition_state() with NativeWindow::set_is_transitioning_fullscreen() refactor: remove unused NativeWindow::FullScreenTransitionState --- shell/browser/api/electron_api_web_contents.cc | 3 +-- shell/browser/native_window.h | 15 +++++++-------- shell/browser/native_window_mac.mm | 13 +++++-------- .../ui/cocoa/electron_ns_window_delegate.mm | 12 +++++------- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 83afa1e0e7933..76eee04dd63b7 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -3973,8 +3973,7 @@ bool WebContents::IsFullscreenForTabOrPending( if (!owner_window()) return is_html_fullscreen(); - bool in_transition = owner_window()->fullscreen_transition_state() != - NativeWindow::FullScreenTransitionState::kNone; + const bool in_transition = owner_window()->is_transitioning_fullscreen(); bool is_html_transition = owner_window()->fullscreen_transition_type() == NativeWindow::FullScreenTransitionType::kHTML; diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index e434b8db7a690..80eb951c879d5 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -362,13 +362,12 @@ class NativeWindow : public base::SupportsUserData, // Handle fullscreen transitions. void HandlePendingFullscreenTransitions(); - enum class FullScreenTransitionState { kEntering, kExiting, kNone }; - - void set_fullscreen_transition_state(FullScreenTransitionState state) { - fullscreen_transition_state_ = state; + constexpr void set_is_transitioning_fullscreen(const bool val) { + is_transitioning_fullscreen_ = val; } - FullScreenTransitionState fullscreen_transition_state() const { - return fullscreen_transition_state_; + + [[nodiscard]] constexpr bool is_transitioning_fullscreen() const { + return is_transitioning_fullscreen_; } enum class FullScreenTransitionType { kHTML, kNative, kNone }; @@ -470,8 +469,6 @@ class NativeWindow : public base::SupportsUserData, std::optional content_size_constraints_; std::queue pending_transitions_; - FullScreenTransitionState fullscreen_transition_state_ = - FullScreenTransitionState::kNone; FullScreenTransitionType fullscreen_transition_type_ = FullScreenTransitionType::kNone; @@ -519,6 +516,8 @@ class NativeWindow : public base::SupportsUserData, // Is this a modal window. bool is_modal_ = false; + bool is_transitioning_fullscreen_ = false; + std::list draggable_region_providers_; // Observers of this window. diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 008c84a516b7f..c7c0ee23406e6 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -336,7 +336,7 @@ static bool FromV8(v8::Isolate* isolate, return; } - if (fullscreen_transition_state() != FullScreenTransitionState::kNone) { + if (is_transitioning_fullscreen()) { SetHasDeferredWindowClose(true); return; } @@ -628,7 +628,7 @@ static bool FromV8(v8::Isolate* isolate, // that it's possible to call it while a fullscreen transition is currently // in process. This can create weird behavior (incl. phantom windows), // so we want to schedule a transition for when the current one has completed. - if (fullscreen_transition_state() != FullScreenTransitionState::kNone) { + if (is_transitioning_fullscreen()) { if (!pending_transitions_.empty()) { bool last_pending = pending_transitions_.back(); // Only push new transitions if they're different than the last transition @@ -652,12 +652,10 @@ static bool FromV8(v8::Isolate* isolate, // SetFullScreen is called by a user before windowWillEnterFullScreen // or windowWillExitFullScreen are invoked, and so a potential transition // could be dropped. - fullscreen_transition_state_ = fullscreen - ? FullScreenTransitionState::kEntering - : FullScreenTransitionState::kExiting; + set_is_transitioning_fullscreen(true); if (![window_ toggleFullScreenMode:nil]) - fullscreen_transition_state_ = FullScreenTransitionState::kNone; + set_is_transitioning_fullscreen(false); } bool NativeWindowMac::IsFullscreen() const { @@ -785,8 +783,7 @@ static bool FromV8(v8::Isolate* isolate, } bool NativeWindowMac::IsResizable() const { - bool in_fs_transition = - fullscreen_transition_state() != FullScreenTransitionState::kNone; + const bool in_fs_transition = is_transitioning_fullscreen(); bool has_rs_mask = HasStyleMask(NSWindowStyleMaskResizable); return has_rs_mask && !IsFullscreen() && !in_fs_transition; } diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm index 04fa6781dd1fe..d2a64514f12e3 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -18,8 +18,6 @@ #include "ui/views/widget/native_widget_mac.h" using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; -using FullScreenTransitionState = - electron::NativeWindow::FullScreenTransitionState; @implementation ElectronNSWindowDelegate @@ -302,7 +300,7 @@ - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Store resizable mask so it can be restored after exiting fullscreen. is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable); - shell_->set_fullscreen_transition_state(FullScreenTransitionState::kEntering); + shell_->set_is_transitioning_fullscreen(true); shell_->NotifyWindowWillEnterFullScreen(); @@ -311,7 +309,7 @@ - (void)windowWillEnterFullScreen:(NSNotification*)notification { } - (void)windowDidEnterFullScreen:(NSNotification*)notification { - shell_->set_fullscreen_transition_state(FullScreenTransitionState::kNone); + shell_->set_is_transitioning_fullscreen(false); shell_->NotifyWindowEnterFullScreen(); @@ -322,7 +320,7 @@ - (void)windowDidEnterFullScreen:(NSNotification*)notification { } - (void)windowDidFailToEnterFullScreen:(NSWindow*)window { - shell_->set_fullscreen_transition_state(FullScreenTransitionState::kNone); + shell_->set_is_transitioning_fullscreen(false); shell_->SetResizable(is_resizable_); shell_->NotifyWindowDidFailToEnterFullScreen(); @@ -334,13 +332,13 @@ - (void)windowDidFailToEnterFullScreen:(NSWindow*)window { } - (void)windowWillExitFullScreen:(NSNotification*)notification { - shell_->set_fullscreen_transition_state(FullScreenTransitionState::kExiting); + shell_->set_is_transitioning_fullscreen(true); shell_->NotifyWindowWillLeaveFullScreen(); } - (void)windowDidExitFullScreen:(NSNotification*)notification { - shell_->set_fullscreen_transition_state(FullScreenTransitionState::kNone); + shell_->set_is_transitioning_fullscreen(false); shell_->SetResizable(is_resizable_); shell_->NotifyWindowLeaveFullScreen(); From 5045ba29a5e06bbaa75213f0d8eb9c5b48d3aac8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 16:27:56 -0500 Subject: [PATCH 194/339] docs: use correct heading level for API function (#46940) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao --- docs/api/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/app.md b/docs/api/app.md index 7bc011cf3d54e..281f606f64ecf 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1490,7 +1490,7 @@ and internal requests made by the runtime (ex: geolocation queries). This method can only be called after app is ready. -#### `app.resolveProxy(url)` +### `app.resolveProxy(url)` * `url` URL From 5223225fb54d205f8d8201376776819dcdcc4546 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 6 May 2025 02:51:11 -0500 Subject: [PATCH 195/339] refactor: add `NativeWindow::FromWidget()` helper (36-x-y) (#46932) refactor: add `NativeWindow::FromWidget()` helper (#46917) refactor: add NativeWindow::FromWidet() helper refactor: make kElectronNativeWindowKey a protected field --- shell/browser/api/electron_api_web_contents_view.cc | 13 ++++++------- shell/browser/native_window.cc | 7 +++++++ shell/browser/native_window.h | 8 +++++--- shell/browser/native_window_mac.mm | 2 +- shell/browser/native_window_views.cc | 2 +- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents_view.cc b/shell/browser/api/electron_api_web_contents_view.cc index f76209fbdf5be..32ca981adcfe1 100644 --- a/shell/browser/api/electron_api_web_contents_view.cc +++ b/shell/browser/api/electron_api_web_contents_view.cc @@ -99,12 +99,11 @@ void WebContentsView::WebContentsDestroyed() { void WebContentsView::OnViewAddedToWidget(views::View* observed_view) { DCHECK_EQ(observed_view, view()); - views::Widget* widget = view()->GetWidget(); - auto* native_window = - static_cast(widget->GetNativeWindowProperty( - electron::kElectronNativeWindowKey.c_str())); + + NativeWindow* native_window = NativeWindow::FromWidget(view()->GetWidget()); if (!native_window) return; + // We don't need to call SetOwnerWindow(nullptr) in OnViewRemovedFromWidget // because that's handled in the WebContents dtor called prior. api_web_contents_->SetOwnerWindow(native_window); @@ -114,11 +113,11 @@ void WebContentsView::OnViewAddedToWidget(views::View* observed_view) { void WebContentsView::OnViewRemovedFromWidget(views::View* observed_view) { DCHECK_EQ(observed_view, view()); - views::Widget* widget = view()->GetWidget(); - auto* native_window = static_cast( - widget->GetNativeWindowProperty(kElectronNativeWindowKey.c_str())); + + NativeWindow* native_window = NativeWindow::FromWidget(view()->GetWidget()); if (!native_window) return; + native_window->RemoveDraggableRegionProvider(this); } diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index fb8a4314b68c5..64ed9083e62d5 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -284,6 +284,13 @@ bool NativeWindow::IsClosed() const { return is_closed_; } +// static +NativeWindow* NativeWindow::FromWidget(const views::Widget* widget) { + DCHECK(widget); + return static_cast( + widget->GetNativeWindowProperty(kNativeWindowKey.c_str())); +} + void NativeWindow::SetSize(const gfx::Size& size, bool animate) { SetBounds(gfx::Rect(GetPosition(), size), animate); } diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 80eb951c879d5..aecd0a9178147 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -47,9 +47,6 @@ class PersistentDictionary; namespace electron { -inline constexpr base::cstring_view kElectronNativeWindowKey = - "__ELECTRON_NATIVE_WINDOW__"; - class ElectronMenuModel; class BackgroundThrottlingSource; @@ -78,6 +75,8 @@ class NativeWindow : public base::SupportsUserData, const gin_helper::Dictionary& options, NativeWindow* parent = nullptr); + [[nodiscard]] static NativeWindow* FromWidget(const views::Widget* widget); + void InitFromOptions(const gin_helper::Dictionary& options); virtual void SetContentView(views::View* view) = 0; @@ -451,6 +450,9 @@ class NativeWindow : public base::SupportsUserData, void set_content_view(views::View* view) { content_view_ = view; } + static inline constexpr base::cstring_view kNativeWindowKey = + "__ELECTRON_NATIVE_WINDOW__"; + // The boolean parsing of the "titleBarOverlay" option bool titlebar_overlay_ = false; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index c7c0ee23406e6..3effcda4e1f38 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -204,7 +204,7 @@ static bool FromV8(v8::Isolate* isolate, params.native_widget = new ElectronNativeWidgetMac(this, windowType, styleMask, widget()); widget()->Init(std::move(params)); - widget()->SetNativeWindowProperty(kElectronNativeWindowKey.c_str(), this); + widget()->SetNativeWindowProperty(kNativeWindowKey.c_str(), this); SetCanResize(resizable); window_ = static_cast( widget()->GetNativeWindow().GetNativeNSWindow()); diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index d8f9210e17bfe..86dcf5be954a2 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -312,7 +312,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, #endif widget()->Init(std::move(params)); - widget()->SetNativeWindowProperty(kElectronNativeWindowKey.c_str(), this); + widget()->SetNativeWindowProperty(kNativeWindowKey.c_str(), this); SetCanResize(resizable_); bool fullscreen = false; From 3f23f01762a1782d1474f57c6ed5e9ea3c9f6624 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 13:07:59 +0200 Subject: [PATCH 196/339] chore: bump node to v22.15.0 (36-x-y) (#46741) --- DEPS | 2 +- ...ld_allow_electron_to_use_exec_script.patch | 5 +- patches/node/.patches | 4 - patches/node/build_add_gn_build_files.patch | 119 +-- ...w_unbundling_of_node_js_dependencies.patch | 10 +- ...etraits_signatures_to_avoid_conflict.patch | 4 +- .../build_compile_with_c_20_support.patch | 6 +- patches/node/build_enable_perfetto.patch | 6 +- ...compilation_fails_if_not_using_a_new.patch | 14 +- ...f_original-fs_and_custom_embedder_js.patch | 2 +- ...o_use_custom_inspector_protocol_path.patch | 8 +- ...xplicit_linker_call_to_libm_on_macos.patch | 59 -- ...e_clang_as_default_compiler_on_macos.patch | 4 +- ...deprecation_ftbfs_in_simdjson_header.patch | 21 +- ...e_expose_importmoduledynamically_and.patch | 12 +- ...cli_move_--trace-atomics-wait_to_eol.patch | 754 +----------------- .../node/cli_remove_deprecated_v8_flag.patch | 12 +- .../expose_get_builtin_module_function.patch | 2 +- ...ror_callback_in_node_isolatesettings.patch | 42 - patches/node/fix_-wnonnull_warning.patch | 43 - ..._values_for_variables_in_common_gypi.patch | 4 +- ...g_fileexists_fn_to_legacymainresolve.patch | 10 +- ...ssert_module_in_the_renderer_process.patch | 4 +- .../node/fix_cppgc_initializing_twice.patch | 2 +- .../fix_crypto_tests_to_run_with_bssl.patch | 69 +- ..._do_not_resolve_electron_entrypoints.patch | 4 +- ...se_readfilesync_override_for_modules.patch | 4 +- ...n_electron_module_via_the_esm_loader.patch | 12 +- ...ingssl_and_openssl_incompatibilities.patch | 81 +- ...in_esm_loaders_to_apply_asar_patches.patch | 15 +- ...ix_remove_deprecated_errno_constants.patch | 2 +- .../fix_remove_fastapitypedarray_usage.patch | 20 +- ...rmony-import-assertions_from_node_cc.patch | 2 +- .../pass_all_globals_through_require.patch | 6 +- ...dder_overriding_of_internal_fs_calls.patch | 4 +- ...ch_cppgc_heap_on_v8_isolate_creation.patch | 23 +- ..._on_wrapper-descriptor-based_cppheap.patch | 18 +- ...ted_fields_of_fastapicallbackoptions.patch | 8 +- .../node/support_v8_sandboxed_pointers.patch | 20 +- ...st_formally_mark_some_tests_as_flaky.patch | 2 +- ...ke_eval_snapshot_tests_more_flexible.patch | 83 -- patches/node/zlib_fix_pointer_alignment.patch | 8 +- script/node-disabled-tests.json | 1 + 43 files changed, 256 insertions(+), 1275 deletions(-) delete mode 100644 patches/node/build_remove_explicit_linker_call_to_libm_on_macos.patch delete mode 100644 patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch delete mode 100644 patches/node/fix_-wnonnull_warning.patch delete mode 100644 patches/node/test_make_eval_snapshot_tests_more_flexible.patch diff --git a/DEPS b/DEPS index 1350957645393..2d26ec6305270 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.49', 'node_version': - 'v22.14.0', + 'v22.15.0', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/patches/chromium/build_allow_electron_to_use_exec_script.patch b/patches/chromium/build_allow_electron_to_use_exec_script.patch index 31c3790c85250..e79b8783f03f5 100644 --- a/patches/chromium/build_allow_electron_to_use_exec_script.patch +++ b/patches/chromium/build_allow_electron_to_use_exec_script.patch @@ -6,10 +6,10 @@ Subject: build: allow electron to use exec_script This is similar to the //build usecase so we're OK adding ourselves here diff --git a/.gn b/.gn -index 54d2631ec203207f44038a36439613709fec1669..c8b8f604f9e9d960e8144a93d958ab125aae96c5 100644 +index 54d2631ec203207f44038a36439613709fec1669..d7e197dc75dd711b1b2eb179a58de9030bde0465 100644 --- a/.gn +++ b/.gn -@@ -173,4 +173,26 @@ exec_script_allowlist = +@@ -173,4 +173,27 @@ exec_script_allowlist = "//tools/grit/grit_rule.gni", "//tools/gritsettings/BUILD.gn", @@ -34,5 +34,6 @@ index 54d2631ec203207f44038a36439613709fec1669..c8b8f604f9e9d960e8144a93d958ab12 + "//third_party/electron_node/deps/sqlite/unofficial.gni", + "//third_party/electron_node/deps/uv/unofficial.gni", + "//third_party/electron_node/deps/uvwasi/unofficial.gni", ++ "//third_party/electron_node/deps/zstd/unofficial.gni", + "//third_party/electron_node/src/inspector/unofficial.gni", ] diff --git a/patches/node/.patches b/patches/node/.patches index 7e230586f067e..7b326ff7bbbd2 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -38,14 +38,10 @@ build_use_third_party_simdutf.patch fix_remove_fastapitypedarray_usage.patch test_handle_explicit_resource_management_globals.patch linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch -build_remove_explicit_linker_call_to_libm_on_macos.patch build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch -test_make_eval_snapshot_tests_more_flexible.patch build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch -feat_add_oom_error_callback_in_node_isolatesettings.patch -fix_-wnonnull_warning.patch refactor_attach_cppgc_heap_on_v8_isolate_creation.patch fix_ensure_traverseparent_bails_on_resource_path_exit.patch cli_move_--trace-atomics-wait_to_eol.patch diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 7076c3671a84a..d1d2ec6d8a637 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -54,71 +54,11 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f } assert(!node_enable_inspector || node_use_openssl, -diff --git a/src/inspector/unofficial.gni b/src/inspector/unofficial.gni -index 5d87f3c901ab509e534598ed1eb0796a96355b5e..3d7aa148678b2646b88fa7c32abec91791b02b82 100644 ---- a/src/inspector/unofficial.gni -+++ b/src/inspector/unofficial.gni -@@ -13,7 +13,7 @@ template("inspector_gn_build") { - } - - node_gen_dir = get_label_info("../..", "target_gen_dir") -- protocol_tool_path = "../../tools/inspector_protocol" -+ protocol_tool_path = "../../deps/inspector_protocol" - - gypi_values = exec_script( - "../../tools/gypi_to_gn.py", -@@ -35,6 +35,8 @@ template("inspector_gn_build") { - ] - - args = [ -+ "--inspector_protocol_dir", -+ rebase_path(protocol_tool_path, root_build_dir), - "--jinja_dir", - # jinja is in third_party. - rebase_path("//third_party/", root_build_dir), -@@ -72,4 +74,37 @@ template("inspector_gn_build") { - outputs = [ "$node_gen_dir/src/{{source_name_part}}.json" ] - args = [ "{{source}}" ] + rebase_path(outputs, root_build_dir) - } -+ -+ config("crdtp_config") { -+ include_dirs = [ protocol_tool_path ] -+ } -+ -+ static_library("crdtp") { -+ public_configs = [ ":crdtp_config" ] -+ sources = [ -+ "$protocol_tool_path/crdtp/cbor.cc", -+ "$protocol_tool_path/crdtp/cbor.h", -+ "$protocol_tool_path/crdtp/dispatch.cc", -+ "$protocol_tool_path/crdtp/dispatch.h", -+ "$protocol_tool_path/crdtp/error_support.cc", -+ "$protocol_tool_path/crdtp/error_support.h", -+ "$protocol_tool_path/crdtp/export.h", -+ "$protocol_tool_path/crdtp/find_by_first.h", -+ "$protocol_tool_path/crdtp/frontend_channel.h", -+ "$protocol_tool_path/crdtp/glue.h", -+ "$protocol_tool_path/crdtp/json.cc", -+ "$protocol_tool_path/crdtp/json.h", -+ "$protocol_tool_path/crdtp/parser_handler.h", -+ "$protocol_tool_path/crdtp/protocol_core.cc", -+ "$protocol_tool_path/crdtp/protocol_core.h", -+ "$protocol_tool_path/crdtp/serializable.cc", -+ "$protocol_tool_path/crdtp/serializable.h", -+ "$protocol_tool_path/crdtp/span.cc", -+ "$protocol_tool_path/crdtp/span.h", -+ "$protocol_tool_path/crdtp/status.cc", -+ "$protocol_tool_path/crdtp/status.h", -+ "$protocol_tool_path/crdtp/json_platform.cc", -+ "$protocol_tool_path/crdtp/json_platform.h", -+ ] -+ } - } diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index 894fd515202cc3a1f933c2bbc618dd09869ad904..4f1ed661e9c432f3b50f2e7e348ad9794ff773d0 100644 +index e85860de93dd5753dd4542ecee9f0888af93898a..04eab49c368c8f86837ed2c1384bf3c63e4bde24 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -781,6 +781,7 @@ void BuiltinLoader::RegisterExternalReferences( +@@ -783,6 +783,7 @@ void BuiltinLoader::RegisterExternalReferences( registry->Register(GetNatives); RegisterExternalReferencesForInternalizedBuiltinCode(registry); @@ -306,7 +246,7 @@ index 21992cbe894a880e3223c379326b62db22f2f12d..1296a5457422099035ba34f2b02624f2 } // namespace js2c } // namespace node diff --git a/tools/search_files.py b/tools/search_files.py -index 65d0e1be42f0a85418491ebb548278cf431aa6a0..d4a31342f1c6107b029394c6e1d00a1d1e877e03 100755 +index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd17a16c20b 100755 --- a/tools/search_files.py +++ b/tools/search_files.py @@ -14,6 +14,7 @@ if __name__ == '__main__': @@ -314,14 +254,19 @@ index 65d0e1be42f0a85418491ebb548278cf431aa6a0..d4a31342f1c6107b029394c6e1d00a1d files = SearchFiles(*sys.argv[2:]) files = [ os.path.relpath(x, sys.argv[1]) for x in files ] + files = [os.path.normpath(x).replace(os.sep, '/') for x in files] - print('\n'.join(files)) - except Exception as e: - print(str(e)) + # Apply the same transform in SearchFiles after relpath + if sys.platform == 'win32': + files = [ x.replace('\\', '/') for x in files ] diff --git a/unofficial.gni b/unofficial.gni -index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5aeab1340d 100644 +index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50ae50a3d8 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -145,6 +145,7 @@ template("node_gn_build") { +@@ -142,32 +142,39 @@ template("node_gn_build") { + public_configs = [ + ":node_external_config", + "deps/googletest:googletest_config", ++ ":zstd_include_config" + ] public_deps = [ "deps/ada", "deps/uv", @@ -329,7 +274,11 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a "deps/simdjson", "$node_v8_path", ] -@@ -156,7 +157,6 @@ template("node_gn_build") { + deps = [ + ":run_node_js2c", +- "deps/brotli", + "deps/cares", + "deps/histogram", "deps/llhttp", "deps/nbytes", "deps/nghttp2", @@ -337,7 +286,13 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a "deps/postject", "deps/sqlite", "deps/uvwasi", -@@ -165,7 +165,11 @@ template("node_gn_build") { +- "deps/zstd", + "//third_party/zlib", ++ "//third_party/brotli:dec", ++ "//third_party/brotli:enc", ++ "//third_party/zstd:decompress", ++ "//third_party/zstd:headers", + "$node_simdutf_path", "$node_v8_path:v8_libplatform", ] @@ -349,7 +304,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a "$target_gen_dir/node_javascript.cc", ] + gypi_values.node_sources -@@ -185,11 +189,12 @@ template("node_gn_build") { +@@ -190,7 +197,7 @@ template("node_gn_build") { } if (node_use_openssl) { deps += [ "deps/ncrypto" ] @@ -358,12 +313,18 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a sources += gypi_values.node_crypto_sources } if (node_enable_inspector) { - deps += [ -+ "src/inspector:crdtp", - "src/inspector:node_protocol_generated_sources", - "src/inspector:v8_inspector_compress_protocol_json", - ] -@@ -282,6 +287,7 @@ template("node_gn_build") { +@@ -214,6 +221,10 @@ template("node_gn_build") { + } + } + ++ config("zstd_include_config") { ++ include_dirs = [ "//third_party/zstd/src/lib" ] ++ } ++ + executable(target_name) { + forward_variables_from(invoker, "*") + +@@ -288,6 +299,7 @@ template("node_gn_build") { } executable("node_js2c") { @@ -371,7 +332,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a deps = [ "deps/uv", "$node_simdutf_path", -@@ -292,26 +298,75 @@ template("node_gn_build") { +@@ -298,26 +310,75 @@ template("node_gn_build") { "src/embedded_data.cc", "src/embedded_data.h", ] @@ -457,7 +418,7 @@ index 9e496d99d7141bf42ef7374a3c676c7b333eeeab..a2f3a769ceaa08db6d7438223884dc5a outputs = [ "$target_gen_dir/node_javascript.cc" ] # Get the path to node_js2c executable of the host toolchain. -@@ -325,11 +380,11 @@ template("node_gn_build") { +@@ -331,11 +392,11 @@ template("node_gn_build") { get_label_info(":node_js2c($host_toolchain)", "name") + host_executable_suffix diff --git a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch index 952cc525820fc..a38d9cc5f0074 100644 --- a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch +++ b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch @@ -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 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d26c4db206 100644 +index 672e97436d9220e8d5046b0c92025f50ae50a3d8..a8ce18acfe333350f91b3e5f235db5f756b2e34a 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -153,7 +153,6 @@ template("node_gn_build") { +@@ -155,7 +155,6 @@ template("node_gn_build") { ":run_node_js2c", "deps/cares", "deps/histogram", @@ -25,7 +25,7 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 "deps/nbytes", "deps/nghttp2", "deps/postject", -@@ -184,7 +183,17 @@ template("node_gn_build") { +@@ -191,7 +190,17 @@ template("node_gn_build") { configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] configs += [ "//build/config/gcc:symbol_visibility_default" ] } @@ -44,7 +44,7 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 if (v8_enable_i18n_support) { deps += [ "//third_party/icu" ] } -@@ -212,6 +221,19 @@ template("node_gn_build") { +@@ -219,6 +228,19 @@ template("node_gn_build") { sources += node_inspector.node_inspector_sources + node_inspector.node_inspector_generated_sources } @@ -63,4 +63,4 @@ index 08603eaef2da51fd92f9bf977647b56409eff48c..cd0eae52ca9bf244e43643a2034fa9d2 + } } - executable(target_name) { + config("zstd_include_config") { diff --git a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch index 9f5b3c70a4ef8..68cbb15ef5eaa 100644 --- a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch +++ b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch @@ -14,7 +14,7 @@ error: duplicate symbol: crdtp::ProtocolTypeTraits -Date: Mon, 3 Feb 2025 21:44:36 +0900 -Subject: build: remove explicit linker call to libm on macOS - -/usr/lib/libm.tbd is available via libSystem.*.dylib and -reexports sanitizer symbols. When building for asan -this becomes an issue as the linker will resolve the symbols -from the system library rather from libclang_rt.* - -For V8 that rely on specific version of these symbols -that get bundled as part of clang, for ex: -https://source.chromium.org/chromium/chromium/src/+/main:v8/src/heap/cppgc/platform.cc;l=93-97 -accepting nullptr for shadow_offset in `asan_get_shadow_mapping`, -linking to system version that doesn't support this will lead to -a crash. - -Clang driver eventually links with `-lSystem` -https://github.com/llvm/llvm-project/blob/e82f93890daefeb38fe2a22ee3db87a89948ec57/clang/lib/Driver/ToolChains/Darwin.cpp#L1628-L1631, -this is done after linking the sanitizer libraries which -ensures right order of resolution for the symbols. - -PR-URL: https://github.com/nodejs/node/pull/56901 -Reviewed-By: Joyee Cheung -Reviewed-By: Chengzhong Wu -Reviewed-By: Luigi Pinca -Reviewed-By: Shelley Vohr - -diff --git a/deps/brotli/unofficial.gni b/deps/brotli/unofficial.gni -index 5e07e106672a04508a77584c109c97a67926c858..91001fa43ea4807d061f296eaeccb7512e34863e 100644 ---- a/deps/brotli/unofficial.gni -+++ b/deps/brotli/unofficial.gni -@@ -25,7 +25,7 @@ template("brotli_gn_build") { - } else if (target_os == "freebsd") { - defines = [ "OS_FREEBSD" ] - } -- if (!is_win) { -+ if (is_linux) { - libs = [ "m" ] - } - if (is_clang || !is_win) { -diff --git a/deps/uv/unofficial.gni b/deps/uv/unofficial.gni -index 348d2f0703e47ca7c5326a4b4c1d6ae31157eeb5..0944d6ddd241b113970ab6aa5804f9534fde882a 100644 ---- a/deps/uv/unofficial.gni -+++ b/deps/uv/unofficial.gni -@@ -87,11 +87,11 @@ template("uv_gn_build") { - ] - } - if (is_posix) { -- libs = [ "m" ] - ldflags = [ "-pthread" ] - } - if (is_linux) { -- libs += [ -+ libs = [ -+ "m", - "dl", - "rt", - ] diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index be9e0aeffa012..7c7f11672aebd 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,10 +11,10 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index a7a0ffde7209de51ffcbf0db0ed7efcf09ad606d..20fd68eeb878b51f361d72070d87338db3d9a8d4 100644 +index 99b147482b636706b1372b89298f35b60ca2bb31..5024e5fb0aee210f4986572638a523db6d26b4cc 100644 --- a/common.gypi +++ b/common.gypi -@@ -125,6 +125,7 @@ +@@ -127,6 +127,7 @@ 'v8_base': '<(PRODUCT_DIR)/obj.target/tools/v8_gypfiles/libv8_snapshot.a', }], ['OS=="mac"', { diff --git a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch index b07d10c2715ec..a51aa2f27749d 100644 --- a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch +++ b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch @@ -11,10 +11,10 @@ Without this patch, building with simdjson fails with This patch can be removed once this is fixed upstream in simdjson. diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h -index f21cd9381eef59ec43502c796fcaddb1b96525f5..e691fd24aa24d225f8c00fa5638be07265bfeeab 100644 +index c1535ee81300b9cb93eb9ee6e769246793f936c3..3350287401e181e1d4ee432b8bd16081d0d7a73e 100644 --- a/deps/simdjson/simdjson.h +++ b/deps/simdjson/simdjson.h -@@ -3654,12 +3654,17 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result padded_string::load(std::string_view filen +@@ -4242,6 +4247,9 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-literal-operator" + - inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { + inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } -@@ -4045,6 +4053,8 @@ inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t le +@@ -4250,6 +4258,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len return simdjson::padded_string(reinterpret_cast(str), len); } #endif @@ -51,10 +51,3 @@ index f21cd9381eef59ec43502c796fcaddb1b96525f5..e691fd24aa24d225f8c00fa5638be072 #endif // SIMDJSON_PADDED_STRING_INL_H /* end file simdjson/padded_string-inl.h */ /* skipped duplicate #include "simdjson/padded_string_view.h" */ -@@ -118292,4 +118302,4 @@ namespace simdjson { - /* end file simdjson/ondemand.h */ - - #endif // SIMDJSON_H --/* end file simdjson.h */ -+/* end file simdjson.h */ -\ No newline at end of file diff --git a/patches/node/chore_expose_importmoduledynamically_and.patch b/patches/node/chore_expose_importmoduledynamically_and.patch index 2596e1482f10d..5e9f79e6ff54d 100644 --- a/patches/node/chore_expose_importmoduledynamically_and.patch +++ b/patches/node/chore_expose_importmoduledynamically_and.patch @@ -11,7 +11,7 @@ its own blended handler between Node and Blink. Not upstreamable. diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js -index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be57620717e 100644 +index fd17ce8695c55f8f38ed19d59960acc1a7692bc8..96754db3277b3a0445b69275368602166c6d5423 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -30,7 +30,7 @@ const { @@ -23,7 +23,7 @@ index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be5 const { loadPreloadModules, initializeFrozenIntrinsics, -@@ -274,12 +274,13 @@ let _forceDefaultLoader = false; +@@ -280,12 +280,13 @@ let _forceDefaultLoader = false; * @param {boolean} [forceDefaultLoader=false] - A boolean indicating disabling custom loaders. */ function initializeESM(forceDefaultLoader = false) { @@ -40,10 +40,10 @@ index 99061e62976e7cb24be81e8632b0e21d1e9adf9a..bbc9311c059e8ab0328c5f92b21a6be5 /** diff --git a/src/module_wrap.cc b/src/module_wrap.cc -index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba430915fd79d8 100644 +index 912acc8da815405531d8b527383f19c3731be100..8d48f4693c3b5f0d1d94d3edadc48c4f36d1bf97 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc -@@ -832,7 +832,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( +@@ -858,7 +858,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( return module->module_.Get(isolate); } @@ -52,7 +52,7 @@ index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba4309 Local context, Local host_defined_options, Local resource_name, -@@ -897,12 +897,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( +@@ -923,12 +923,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( Realm* realm = Realm::GetCurrent(args); HandleScope handle_scope(isolate); @@ -68,7 +68,7 @@ index 649ec428e2dd6fbf0082b3f1ba9fdfbfab892a94..168912fe6ede3b71ab3029c292ba4309 } void ModuleWrap::HostInitializeImportMetaObjectCallback( -@@ -944,13 +945,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( +@@ -970,13 +971,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( Realm* realm = Realm::GetCurrent(args); Isolate* isolate = realm->isolate(); diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch index a9a2aa7f1d2cc..70ae224eebb1a 100644 --- a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -15,747 +15,17 @@ Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 431a6aa7a2cf4d537cb719f15c2749254e0433b3..1d1672a01c4fae6b339cb144d0d6e35b49209a14 100644 +index 114b7bbf6b1e105fc1696ed8a064065db73ff519..ad863e52761332c3249a86af0e3d239cd0f73b03 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -32,11 +32,11 @@ is passed. If no corresponding file is found, an error is thrown. - If a file is found, its path will be passed to the - [ES module loader][Modules loaders] under any of the following conditions: - --* The program was started with a command-line flag that forces the entry -+- The program was started with a command-line flag that forces the entry - point to be loaded with ECMAScript module loader, such as `--import` or - [`--experimental-default-type=module`][]. --* The file has an `.mjs` extension. --* The file does not have a `.cjs` extension, and the nearest parent -+- The file has an `.mjs` extension. -+- The file does not have a `.cjs` extension, and the nearest parent - `package.json` file contains a top-level [`"type"`][] field with a value of - `"module"`. - -@@ -164,7 +164,10 @@ Example: - ```js - const childProcess = require('node:child_process'); - // Attempt to bypass the permission --childProcess.spawn('node', ['-e', 'require("fs").writeFileSync("/new-file", "example")']); -+childProcess.spawn('node', [ -+ '-e', -+ 'require("fs").writeFileSync("/new-file", "example")', -+]); - ``` - - ```console -@@ -206,8 +209,8 @@ the [Permission Model][]. - - The valid arguments for the `--allow-fs-read` flag are: - --* `*` - To allow all `FileSystemRead` operations. --* Multiple paths can be allowed using multiple `--allow-fs-read` flags. -+- `*` - To allow all `FileSystemRead` operations. -+- Multiple paths can be allowed using multiple `--allow-fs-read` flags. - Example `--allow-fs-read=/folder1/ --allow-fs-read=/folder1/` - - Examples can be found in the [File System Permissions][] documentation. -@@ -251,8 +254,8 @@ the [Permission Model][]. - - The valid arguments for the `--allow-fs-write` flag are: - --* `*` - To allow all `FileSystemWrite` operations. --* Multiple paths can be allowed using multiple `--allow-fs-write` flags. -+- `*` - To allow all `FileSystemWrite` operations. -+- Multiple paths can be allowed using multiple `--allow-fs-write` flags. - Example `--allow-fs-write=/folder1/ --allow-fs-write=/folder1/` - - Paths delimited by comma (`,`) are no longer allowed. -@@ -399,10 +402,10 @@ creation behavior. - - The following options are currently supported: - --* `builder` {string} Required. Provides the name to the script that is executed -+- `builder` {string} Required. Provides the name to the script that is executed - before building the snapshot, as if [`--build-snapshot`][] had been passed - with `builder` as the main script name. --* `withoutCodeCache` {boolean} Optional. Including the code cache reduces the -+- `withoutCodeCache` {boolean} Optional. Including the code cache reduces the - time spent on compiling functions included in the snapshot at the expense - of a bigger snapshot size and potentially breaking portability of the - snapshot. -@@ -550,9 +553,9 @@ Defaults to current working directory. - - Affects the default output directory of: - --* [`--cpu-prof-dir`][] --* [`--heap-prof-dir`][] --* [`--redirect-warnings`][] -+- [`--cpu-prof-dir`][] -+- [`--heap-prof-dir`][] -+- [`--redirect-warnings`][] - - ### `--disable-proto=mode` - -@@ -697,9 +700,9 @@ changes: - Set the default value of `order` in [`dns.lookup()`][] and - [`dnsPromises.lookup()`][]. The value could be: - --* `ipv4first`: sets default `order` to `ipv4first`. --* `ipv6first`: sets default `order` to `ipv6first`. --* `verbatim`: sets default `order` to `verbatim`. -+- `ipv4first`: sets default `order` to `ipv4first`. -+- `ipv6first`: sets default `order` to `ipv6first`. -+- `verbatim`: sets default `order` to `verbatim`. - - The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher - priority than `--dns-result-order`. -@@ -890,7 +893,7 @@ added: v22.7.0 - > Stability: 1 - Experimental - - Enables the use of [`AsyncLocalStorage`][] backed by `AsyncContextFrame` rather --than the default implementation which relies on async\_hooks. This new model is -+than the default implementation which relies on async_hooks. This new model is - implemented very differently and so could have differences in how context data - flows within the application. As such, it is presently recommended to be sure - your application behaviour is unaffected by this change before using it in -@@ -909,12 +912,12 @@ added: - - Define which module system, `module` or `commonjs`, to use for the following: - --* String input provided via `--eval` or STDIN, if `--input-type` is unspecified. -+- String input provided via `--eval` or STDIN, if `--input-type` is unspecified. - --* Files ending in `.js` or with no extension, if there is no `package.json` file -+- Files ending in `.js` or with no extension, if there is no `package.json` file - present in the same folder or any parent folder. - --* Files ending in `.js` or with no extension, if the nearest parent -+- Files ending in `.js` or with no extension, if the nearest parent - `package.json` field lacks a `"type"` field; unless the `package.json` folder - or any parent folder is inside a `node_modules` folder. - -@@ -1450,15 +1453,15 @@ interoperability with non-conformant HTTP implementations. - - When enabled, the parser will accept the following: - --* Invalid HTTP headers values. --* Invalid HTTP versions. --* Allow message containing both `Transfer-Encoding` -+- Invalid HTTP headers values. -+- Invalid HTTP versions. -+- Allow message containing both `Transfer-Encoding` - and `Content-Length` headers. --* Allow extra data after message when `Connection: close` is present. --* Allow extra transfer encodings after `chunked` has been provided. --* Allow `\n` to be used as token separator instead of `\r\n`. --* Allow `\r\n` not to be provided after a chunk. --* Allow spaces to be present after a chunk size and before `\r\n`. -+- Allow extra data after message when `Connection: close` is present. -+- Allow extra transfer encodings after `chunked` has been provided. -+- Allow `\n` to be used as token separator instead of `\r\n`. -+- Allow `\r\n` not to be provided after a chunk. -+- Allow spaces to be present after a chunk size and before `\r\n`. - - All the above will expose your application to request smuggling - or poisoning attack. Avoid using this option. -@@ -1536,8 +1539,8 @@ a [remote code execution][] attack. - - If specifying a host, make sure that either: - --* The host is not accessible from public networks. --* A firewall disallows unwanted connections on the port. -+- The host is not accessible from public networks. -+- A firewall disallows unwanted connections on the port. - - **More specifically, `--inspect=0.0.0.0` is insecure if the port (`9229` by - default) is not firewall-protected.** -@@ -1799,7 +1802,7 @@ added: - --> - - Enable OpenSSL 3.0 legacy provider. For more information please see --[OSSL\_PROVIDER-legacy][OSSL_PROVIDER-legacy]. -+[OSSL_PROVIDER-legacy][OSSL_PROVIDER-legacy]. - - ### `--openssl-shared-config` - -@@ -1849,12 +1852,12 @@ changes: - Enable the Permission Model for current process. When enabled, the - following permissions are restricted: - --* File System - manageable through -+- File System - manageable through - [`--allow-fs-read`][], [`--allow-fs-write`][] flags --* Child Process - manageable through [`--allow-child-process`][] flag --* Worker Threads - manageable through [`--allow-worker`][] flag --* WASI - manageable through [`--allow-wasi`][] flag --* Addons - manageable through [`--allow-addons`][] flag -+- Child Process - manageable through [`--allow-child-process`][] flag -+- Worker Threads - manageable through [`--allow-worker`][] flag -+- WASI - manageable through [`--allow-wasi`][] flag -+- Addons - manageable through [`--allow-addons`][] flag - - ### `--preserve-symlinks` - -@@ -2194,16 +2197,16 @@ cases. - Some features of other `run` implementations that are intentionally excluded - are: - --* Running `pre` or `post` scripts in addition to the specified script. --* Defining package manager-specific environment variables. -+- Running `pre` or `post` scripts in addition to the specified script. -+- Defining package manager-specific environment variables. - - #### Environment variables - - The following environment variables are set when running a script with `--run`: - --* `NODE_RUN_SCRIPT_NAME`: The name of the script being run. For example, if -+- `NODE_RUN_SCRIPT_NAME`: The name of the script being run. For example, if - `--run` is used to run `test`, the value of this variable will be `test`. --* `NODE_RUN_PACKAGE_JSON_PATH`: The path to the `package.json` that is being -+- `NODE_RUN_PACKAGE_JSON_PATH`: The path to the `package.json` that is being - processed. - - ### `--secure-heap-min=n` -@@ -2601,39 +2604,6 @@ added: v12.0.0 - Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.3'. Use to disable support - for TLSv1.2, which is not as secure as TLSv1.3. - --### `--trace-atomics-wait` -- -- -- --> Stability: 0 - Deprecated -- --Print short summaries of calls to [`Atomics.wait()`][] to stderr. --The output could look like this: -- --```text --(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) started --(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the values mismatched --(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) started --(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out --(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) started --(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) started --(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread --(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread --``` -- --The fields here correspond to: -- --* The thread id as given by [`worker_threads.threadId`][] --* The base address of the `SharedArrayBuffer` in question, as well as the -- byte offset corresponding to the index passed to `Atomics.wait()` --* The expected value that was passed to `Atomics.wait()` --* The timeout passed to `Atomics.wait` -- - ### `--trace-deprecation` - - - --* `--allow-addons` --* `--allow-child-process` --* `--allow-fs-read` --* `--allow-fs-write` --* `--allow-wasi` --* `--allow-worker` --* `--conditions`, `-C` --* `--diagnostic-dir` --* `--disable-proto` --* `--disable-sigusr1` --* `--disable-warning` --* `--disable-wasm-trap-handler` --* `--dns-result-order` --* `--enable-fips` --* `--enable-network-family-autoselection` --* `--enable-source-maps` --* `--entry-url` --* `--experimental-abortcontroller` --* `--experimental-async-context-frame` --* `--experimental-default-type` --* `--experimental-detect-module` --* `--experimental-eventsource` --* `--experimental-import-meta-resolve` --* `--experimental-json-modules` --* `--experimental-loader` --* `--experimental-modules` --* `--experimental-permission` --* `--experimental-print-required-tla` --* `--experimental-require-module` --* `--experimental-shadow-realm` --* `--experimental-specifier-resolution` --* `--experimental-strip-types` --* `--experimental-top-level-await` --* `--experimental-transform-types` --* `--experimental-vm-modules` --* `--experimental-wasi-unstable-preview1` --* `--experimental-wasm-modules` --* `--experimental-webstorage` --* `--force-context-aware` --* `--force-fips` --* `--force-node-api-uncaught-exceptions-policy` --* `--frozen-intrinsics` --* `--heap-prof-dir` --* `--heap-prof-interval` --* `--heap-prof-name` --* `--heap-prof` --* `--heapsnapshot-near-heap-limit` --* `--heapsnapshot-signal` --* `--http-parser` --* `--icu-data-dir` --* `--import` --* `--input-type` --* `--insecure-http-parser` --* `--inspect-brk` --* `--inspect-port`, `--debug-port` --* `--inspect-publish-uid` --* `--inspect-wait` --* `--inspect` --* `--localstorage-file` --* `--max-http-header-size` --* `--napi-modules` --* `--network-family-autoselection-attempt-timeout` --* `--no-addons` --* `--no-deprecation` --* `--no-experimental-fetch` --* `--no-experimental-global-customevent` --* `--no-experimental-global-navigator` --* `--no-experimental-global-webcrypto` --* `--no-experimental-repl-await` --* `--no-experimental-sqlite` --* `--no-experimental-websocket` --* `--no-extra-info-on-fatal-exception` --* `--no-force-async-hooks-checks` --* `--no-global-search-paths` --* `--no-network-family-autoselection` --* `--no-warnings` --* `--node-memory-debug` --* `--openssl-config` --* `--openssl-legacy-provider` --* `--openssl-shared-config` --* `--pending-deprecation` --* `--permission` --* `--preserve-symlinks-main` --* `--preserve-symlinks` --* `--prof-process` --* `--redirect-warnings` --* `--report-compact` --* `--report-dir`, `--report-directory` --* `--report-exclude-env` --* `--report-exclude-network` --* `--report-filename` --* `--report-on-fatalerror` --* `--report-on-signal` --* `--report-signal` --* `--report-uncaught-exception` --* `--require`, `-r` --* `--secure-heap-min` --* `--secure-heap` --* `--snapshot-blob` --* `--test-coverage-branches` --* `--test-coverage-exclude` --* `--test-coverage-functions` --* `--test-coverage-include` --* `--test-coverage-lines` --* `--test-name-pattern` --* `--test-only` --* `--test-reporter-destination` --* `--test-reporter` --* `--test-shard` --* `--test-skip-pattern` --* `--throw-deprecation` --* `--title` --* `--tls-cipher-list` --* `--tls-keylog` --* `--tls-max-v1.2` --* `--tls-max-v1.3` --* `--tls-min-v1.0` --* `--tls-min-v1.1` --* `--tls-min-v1.2` --* `--tls-min-v1.3` +@@ -3313,7 +3313,6 @@ one is included in the list below. + * `--tls-min-v1.1` + * `--tls-min-v1.2` + * `--tls-min-v1.3` -* `--trace-atomics-wait` --* `--trace-deprecation` --* `--trace-env-js-stack` --* `--trace-env-native-stack` --* `--trace-env` --* `--trace-event-categories` --* `--trace-event-file-pattern` --* `--trace-events-enabled` --* `--trace-exit` --* `--trace-require-module` --* `--trace-sigint` --* `--trace-sync-io` --* `--trace-tls` --* `--trace-uncaught` --* `--trace-warnings` --* `--track-heap-objects` --* `--unhandled-rejections` --* `--use-bundled-ca` --* `--use-largepages` --* `--use-openssl-ca` --* `--v8-pool-size` --* `--watch-path` --* `--watch-preserve-output` --* `--watch` --* `--zero-fill-buffers` -+- `--allow-addons` -+- `--allow-child-process` -+- `--allow-fs-read` -+- `--allow-fs-write` -+- `--allow-wasi` -+- `--allow-worker` -+- `--conditions`, `-C` -+- `--diagnostic-dir` -+- `--disable-proto` -+- `--disable-sigusr1` -+- `--disable-warning` -+- `--disable-wasm-trap-handler` -+- `--dns-result-order` -+- `--enable-fips` -+- `--enable-network-family-autoselection` -+- `--enable-source-maps` -+- `--entry-url` -+- `--experimental-abortcontroller` -+- `--experimental-async-context-frame` -+- `--experimental-default-type` -+- `--experimental-detect-module` -+- `--experimental-eventsource` -+- `--experimental-import-meta-resolve` -+- `--experimental-json-modules` -+- `--experimental-loader` -+- `--experimental-modules` -+- `--experimental-permission` -+- `--experimental-print-required-tla` -+- `--experimental-require-module` -+- `--experimental-shadow-realm` -+- `--experimental-specifier-resolution` -+- `--experimental-strip-types` -+- `--experimental-top-level-await` -+- `--experimental-transform-types` -+- `--experimental-vm-modules` -+- `--experimental-wasi-unstable-preview1` -+- `--experimental-wasm-modules` -+- `--experimental-webstorage` -+- `--force-context-aware` -+- `--force-fips` -+- `--force-node-api-uncaught-exceptions-policy` -+- `--frozen-intrinsics` -+- `--heap-prof-dir` -+- `--heap-prof-interval` -+- `--heap-prof-name` -+- `--heap-prof` -+- `--heapsnapshot-near-heap-limit` -+- `--heapsnapshot-signal` -+- `--http-parser` -+- `--icu-data-dir` -+- `--import` -+- `--input-type` -+- `--insecure-http-parser` -+- `--inspect-brk` -+- `--inspect-port`, `--debug-port` -+- `--inspect-publish-uid` -+- `--inspect-wait` -+- `--inspect` -+- `--localstorage-file` -+- `--max-http-header-size` -+- `--napi-modules` -+- `--network-family-autoselection-attempt-timeout` -+- `--no-addons` -+- `--no-deprecation` -+- `--no-experimental-fetch` -+- `--no-experimental-global-customevent` -+- `--no-experimental-global-navigator` -+- `--no-experimental-global-webcrypto` -+- `--no-experimental-repl-await` -+- `--no-experimental-sqlite` -+- `--no-experimental-websocket` -+- `--no-extra-info-on-fatal-exception` -+- `--no-force-async-hooks-checks` -+- `--no-global-search-paths` -+- `--no-network-family-autoselection` -+- `--no-warnings` -+- `--node-memory-debug` -+- `--openssl-config` -+- `--openssl-legacy-provider` -+- `--openssl-shared-config` -+- `--pending-deprecation` -+- `--permission` -+- `--preserve-symlinks-main` -+- `--preserve-symlinks` -+- `--prof-process` -+- `--redirect-warnings` -+- `--report-compact` -+- `--report-dir`, `--report-directory` -+- `--report-exclude-env` -+- `--report-exclude-network` -+- `--report-filename` -+- `--report-on-fatalerror` -+- `--report-on-signal` -+- `--report-signal` -+- `--report-uncaught-exception` -+- `--require`, `-r` -+- `--secure-heap-min` -+- `--secure-heap` -+- `--snapshot-blob` -+- `--test-coverage-branches` -+- `--test-coverage-exclude` -+- `--test-coverage-functions` -+- `--test-coverage-include` -+- `--test-coverage-lines` -+- `--test-name-pattern` -+- `--test-only` -+- `--test-reporter-destination` -+- `--test-reporter` -+- `--test-shard` -+- `--test-skip-pattern` -+- `--throw-deprecation` -+- `--title` -+- `--tls-cipher-list` -+- `--tls-keylog` -+- `--tls-max-v1.2` -+- `--tls-max-v1.3` -+- `--tls-min-v1.0` -+- `--tls-min-v1.1` -+- `--tls-min-v1.2` -+- `--tls-min-v1.3` -+- `--trace-deprecation` -+- `--trace-env-js-stack` -+- `--trace-env-native-stack` -+- `--trace-env` -+- `--trace-event-categories` -+- `--trace-event-file-pattern` -+- `--trace-events-enabled` -+- `--trace-exit` -+- `--trace-require-module` -+- `--trace-sigint` -+- `--trace-sync-io` -+- `--trace-tls` -+- `--trace-uncaught` -+- `--trace-warnings` -+- `--track-heap-objects` -+- `--unhandled-rejections` -+- `--use-bundled-ca` -+- `--use-largepages` -+- `--use-openssl-ca` -+- `--v8-pool-size` -+- `--watch-path` -+- `--watch-preserve-output` -+- `--watch` -+- `--zero-fill-buffers` - - - -@@ -3266,19 +3235,19 @@ V8 options that are allowed are: - - - --* `--abort-on-uncaught-exception` --* `--disallow-code-generation-from-strings` --* `--enable-etw-stack-walking` --* `--expose-gc` --* `--interpreted-frames-native-stack` --* `--jitless` --* `--max-old-space-size` --* `--max-semi-space-size` --* `--perf-basic-prof-only-functions` --* `--perf-basic-prof` --* `--perf-prof-unwinding-info` --* `--perf-prof` --* `--stack-trace-limit` -+- `--abort-on-uncaught-exception` -+- `--disallow-code-generation-from-strings` -+- `--enable-etw-stack-walking` -+- `--expose-gc` -+- `--interpreted-frames-native-stack` -+- `--jitless` -+- `--max-old-space-size` -+- `--max-semi-space-size` -+- `--perf-basic-prof-only-functions` -+- `--perf-basic-prof` -+- `--perf-prof-unwinding-info` -+- `--perf-prof` -+- `--stack-trace-limit` - - - -@@ -3446,23 +3415,12 @@ and the line lengths of the source file (in the key `lineLengths`). - "url": "./path-to-map.json", - "data": { - "version": 3, -- "sources": [ -- "file:///absolute/path/to/original.js" -- ], -- "names": [ -- "Foo", -- "console", -- "info" -- ], -+ "sources": ["file:///absolute/path/to/original.js"], -+ "names": ["Foo", "console", "info"], - "mappings": "MAAMA,IACJC,YAAaC", - "sourceRoot": "./" - }, -- "lineLengths": [ -- 13, -- 62, -- 38, -- 27 -- ] -+ "lineLengths": [13, 62, 38, 27] - } - } - } -@@ -3470,7 +3428,7 @@ and the line lengths of the source file (in the key `lineLengths`). - - ### `NO_COLOR=` - --[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the -+[`NO_COLOR`][] is an alias for `NODE_DISABLE_COLORS`. The value of the - environment variable is arbitrary. - - ### `OPENSSL_CONF=file` -@@ -3552,12 +3510,12 @@ Asynchronous system APIs are used by Node.js whenever possible, but where they - do not exist, libuv's threadpool is used to create asynchronous node APIs based - on synchronous system APIs. Node.js APIs that use the threadpool are: - --* all `fs` APIs, other than the file watcher APIs and those that are explicitly -+- all `fs` APIs, other than the file watcher APIs and those that are explicitly - synchronous --* asynchronous crypto APIs such as `crypto.pbkdf2()`, `crypto.scrypt()`, -+- asynchronous crypto APIs such as `crypto.pbkdf2()`, `crypto.scrypt()`, - `crypto.randomBytes()`, `crypto.randomFill()`, `crypto.generateKeyPair()` --* `dns.lookup()` --* all `zlib` APIs, other than those that are explicitly synchronous -+- `dns.lookup()` -+- all `zlib` APIs, other than those that are explicitly synchronous - - Because libuv's threadpool has a fixed size, it means that if for whatever - reason any of these APIs takes a long time, other (seemingly unrelated) APIs -@@ -3723,7 +3681,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 - [`--redirect-warnings`]: #--redirect-warningsfile - [`--require`]: #-r---require-module - [`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage --[`Atomics.wait()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait - [`Buffer`]: buffer.md#class-buffer - [`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html - [`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax -@@ -3746,7 +3703,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 - [`tls.DEFAULT_MIN_VERSION`]: tls.md#tlsdefault_min_version - [`unhandledRejection`]: process.md#event-unhandledrejection - [`v8.startupSnapshot` API]: v8.md#startup-snapshot-api --[`worker_threads.threadId`]: worker_threads.md#workerthreadid - [collecting code coverage from tests]: test.md#collecting-code-coverage - [conditional exports]: packages.md#conditional-exports - [context-aware]: addons.md#context-aware-addons -diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md -index 0df7dce60058d518c9607094344461b60e540e60..f5f8ac77c848edf62b0a82f0644f61077a02aedd 100644 ---- a/doc/api/deprecations.md -+++ b/doc/api/deprecations.md -@@ -3313,6 +3313,9 @@ Values other than `undefined`, `null`, integer numbers, and integer strings - - - --Type: Runtime -+Type: End-of-Life - --The [`--trace-atomics-wait`][] flag is deprecated because -+The `--trace-atomics-wait` flag has been removed because - it uses the V8 hook `SetAtomicsWaitCallback`, - that will be removed in a future V8 release. - -@@ -3737,7 +3740,6 @@ deprecated, as their values are guaranteed to be identical to that of `process.f - [`--force-node-api-uncaught-exceptions-policy`]: cli.md#--force-node-api-uncaught-exceptions-policy - [`--pending-deprecation`]: cli.md#--pending-deprecation - [`--throw-deprecation`]: cli.md#--throw-deprecation --[`--trace-atomics-wait`]: cli.md#--trace-atomics-wait - [`--unhandled-rejections`]: cli.md#--unhandled-rejectionsmode - [`Buffer.allocUnsafeSlow(size)`]: buffer.md#static-method-bufferallocunsafeslowsize - [`Buffer.from(array)`]: buffer.md#static-method-bufferfromarray + * `--trace-deprecation` + * `--trace-env-js-stack` + * `--trace-env-native-stack` diff --git a/doc/node.1 b/doc/node.1 index 9f534746ef9d9c1c1ee2edd6c195573a2e228600..e01fc511a1034518c0fb9bc5fa925524aecad927 100644 --- a/doc/node.1 @@ -773,7 +43,7 @@ index 9f534746ef9d9c1c1ee2edd6c195573a2e228600..e01fc511a1034518c0fb9bc5fa925524 Print stack traces for deprecations. . diff --git a/src/node.cc b/src/node.cc -index 0ed78ab6b52906e980eebf1f625a1c7cbfc8097b..2ff08a9cb6124316049a91bda70cf6985045286a 100644 +index 2f58a6fa69069dabb99b5ddb8011991b07fa5f02..9f6fa646ba6673f67f5f625e157ed850633a26da 100644 --- a/src/node.cc +++ b/src/node.cc @@ -226,44 +226,6 @@ void Environment::WaitForInspectorFrontendByOptions() { @@ -840,10 +110,10 @@ index 0ed78ab6b52906e980eebf1f625a1c7cbfc8097b..2ff08a9cb6124316049a91bda70cf698 isolate_->SetPromiseHook(TracePromises); } diff --git a/src/node_options.cc b/src/node_options.cc -index 866f2a39a5e51a8bf434ccd54d76c685bcdd1502..2bedaa88582c369a4a420ff20a5204af96feb52e 100644 +index 54b253aa54f5cdebdb04315f9c6c2506977555c0..acf390bd456c7ddfa6987e440fb45940aec6b1ff 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -758,10 +758,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { +@@ -762,10 +762,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "throw an exception on deprecations", &EnvironmentOptions::throw_deprecation, kAllowedInEnvvar); @@ -855,7 +125,7 @@ index 866f2a39a5e51a8bf434ccd54d76c685bcdd1502..2bedaa88582c369a4a420ff20a5204af "show stack traces on deprecations", &EnvironmentOptions::trace_deprecation, diff --git a/src/node_options.h b/src/node_options.h -index 41dd04f5e2b1cd54c32df70830389d44d7b39aa2..b10287d3eadc49b44e2e3fb8150a48be6866b0b4 100644 +index 065457acfde6ba4d04ed570cc72005cfd2798fd5..150f833bb21bd6d37f652f0785a4a98f3de5f67d 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -203,7 +203,6 @@ class EnvironmentOptions : public Options { diff --git a/patches/node/cli_remove_deprecated_v8_flag.patch b/patches/node/cli_remove_deprecated_v8_flag.patch index 5acad06fbdca2..caff01e09ec1d 100644 --- a/patches/node/cli_remove_deprecated_v8_flag.patch +++ b/patches/node/cli_remove_deprecated_v8_flag.patch @@ -18,10 +18,10 @@ Reviewed-By: Michaël Zasso Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 8105592764abca6036e8e029ca9b6a32402e7156..431a6aa7a2cf4d537cb719f15c2749254e0433b3 100644 +index 1b42c5a7f4715e56fa5bc39cd6f78a76473406f2..114b7bbf6b1e105fc1696ed8a064065db73ff519 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3270,7 +3270,6 @@ V8 options that are allowed are: +@@ -3350,7 +3350,6 @@ V8 options that are allowed are: * `--disallow-code-generation-from-strings` * `--enable-etw-stack-walking` * `--expose-gc` @@ -30,10 +30,10 @@ index 8105592764abca6036e8e029ca9b6a32402e7156..431a6aa7a2cf4d537cb719f15c274925 * `--jitless` * `--max-old-space-size` diff --git a/src/node_options.cc b/src/node_options.cc -index 620776c06d835eb1bfeed060751c570e8d435b29..866f2a39a5e51a8bf434ccd54d76c685bcdd1502 100644 +index b22fbb0a285f6f323779d6ebb2b027a3990b031e..54b253aa54f5cdebdb04315f9c6c2506977555c0 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -980,11 +980,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( +@@ -984,11 +984,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( "disallow eval and friends", V8Option{}, kAllowedInEnvvar); @@ -46,10 +46,10 @@ index 620776c06d835eb1bfeed060751c570e8d435b29..866f2a39a5e51a8bf434ccd54d76c685 "disable runtime allocation of executable memory", V8Option{}, diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js -index b1d5c44ceeeebc674938d85736aace2a6ef570e2..9e89200e9f6dfd89340cd04afb76e271ffc82b54 100644 +index c5d74f40e7894980b45713c77cc36f836be73528..53bca572c3405c0357f868aae71fc2c82d973c04 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js -@@ -73,7 +73,6 @@ if (common.hasCrypto) { +@@ -76,7 +76,6 @@ if (common.hasCrypto) { expect('--abort_on-uncaught_exception', 'B\n'); expect('--disallow-code-generation-from-strings', 'B\n'); expect('--expose-gc', 'B\n'); diff --git a/patches/node/expose_get_builtin_module_function.patch b/patches/node/expose_get_builtin_module_function.patch index 39d3c031d4ef4..bee428c167591 100644 --- a/patches/node/expose_get_builtin_module_function.patch +++ b/patches/node/expose_get_builtin_module_function.patch @@ -9,7 +9,7 @@ modules to sandboxed renderers. TODO(codebytere): remove and replace with a public facing API. diff --git a/src/node_binding.cc b/src/node_binding.cc -index c2ef9b36d5b2967c798c123b6cbbd099b15c2791..b5c0a93d83ab4d4f6792d0eb648e7198de874bcf 100644 +index 6c337232149ccb8bdb1188e72d59a052b1cb79c1..44ad4a480a33a2c39494a7d961318270a25620d8 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -653,6 +653,10 @@ void GetInternalBinding(const FunctionCallbackInfo& args) { diff --git a/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch b/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch deleted file mode 100644 index 3eecb039f5d7e..0000000000000 --- a/patches/node/feat_add_oom_error_callback_in_node_isolatesettings.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Yang Liu -Date: Wed, 5 Mar 2025 17:22:39 +0800 -Subject: feat: add oom_error_callback in node::IsolateSettings - -Expose v8::OOMErrorCallback to allow setting OOM error handler outside Node.js - -As described in this issue https://github.com/electron/electron/issues/45894, -Electron fails to capture js heap oom because node::OOMErrorHandler just -terminates the process without raising an exception. - -To fix this issue, provide the interface oom_error_callback to enable a -custom oom error callback set from Electron. - -diff --git a/src/api/environment.cc b/src/api/environment.cc -index fc9b056d2f7e25109100fbde5f3ab0aebc8c619a..9b155213ce301df7e396a4a113992499fc7e9910 100644 ---- a/src/api/environment.cc -+++ b/src/api/environment.cc -@@ -241,7 +241,10 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { - auto* fatal_error_cb = s.fatal_error_callback ? - s.fatal_error_callback : OnFatalError; - isolate->SetFatalErrorHandler(fatal_error_cb); -- isolate->SetOOMErrorHandler(OOMErrorHandler); -+ -+ auto* oom_error_cb = s.oom_error_callback ? -+ s.oom_error_callback : OOMErrorHandler; -+ isolate->SetOOMErrorHandler(oom_error_cb); - - if ((s.flags & SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK) == 0) { - auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ? -diff --git a/src/node.h b/src/node.h -index afb26ec5690ccd65a3c36f8b8a1b2de416b9d843..98ad0ea649eaef43d1f5231f7bc4044e100e08d7 100644 ---- a/src/node.h -+++ b/src/node.h -@@ -489,6 +489,7 @@ struct IsolateSettings { - v8::Isolate::AbortOnUncaughtExceptionCallback - should_abort_on_uncaught_exception_callback = nullptr; - v8::FatalErrorCallback fatal_error_callback = nullptr; -+ v8::OOMErrorCallback oom_error_callback = nullptr; - v8::PrepareStackTraceCallback prepare_stack_trace_callback = nullptr; - - // Miscellaneous callbacks diff --git a/patches/node/fix_-wnonnull_warning.patch b/patches/node/fix_-wnonnull_warning.patch deleted file mode 100644 index a75eaf4b373dd..0000000000000 --- a/patches/node/fix_-wnonnull_warning.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Charles Kerr -Date: Thu, 6 Mar 2025 19:31:29 -0600 -Subject: fix: -Wnonnull warning -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fixes this warning: - -> 2025-03-07T01:05:01.8637705Z ../../third_party/electron_node/src/debug_utils.cc(257,12): error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] -> 2025-03-07T01:05:01.8638267Z 257 | return nullptr; -> 2025-03-07T01:05:01.8638481Z | ^~~~~~~ -> 2025-03-07T01:05:01.8638700Z 1 error generated. - -Not sure why this warning was never triggered before; `git blame` -indicates this code hasn't changed in ages: - -> c40a8273ef2 (Michaël Zasso 2024-05-10 09:50:20 +0200 255) #endif // DEBUG -> 8e2d33f1562 (Anna Henningsen 2018-06-07 16:54:29 +0200 256) } -> 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 257) return nullptr; -> 247b5130595 (Refael Ackermann 2018-10-22 15:07:00 -0400 258) } - -Presumably this is failing in this Chromium roll due to a -clang version bump. - -We should remove this patch after upstreaming it. - -Upstream PR: https://github.com/nodejs/node/pull/57354 - -diff --git a/src/debug_utils.cc b/src/debug_utils.cc -index c8b3b11ee34d7ac98163aa563fd7f6f1fb0a3b79..8a85e066fd9e6f3d6131eca89d52495f904cd5a6 100644 ---- a/src/debug_utils.cc -+++ b/src/debug_utils.cc -@@ -254,7 +254,7 @@ class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext { - USE(GetLastError()); - #endif // DEBUG - } -- return nullptr; -+ return {}; - } - - SymbolInfo LookupSymbol(void* address) override { diff --git a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch index e1722fdc64262..45edf948a98da 100644 --- a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch +++ b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch @@ -7,10 +7,10 @@ common.gypi is a file that's included in the node header bundle, despite the fact that we do not build node with gyp. diff --git a/common.gypi b/common.gypi -index 62f26bb07d27a02aedf18fdd1191b282f9340cac..5d74876ab28f8c10bb9543f7652478514414d8d2 100644 +index 372409f4b09cc3b3be9809f697816498e5c021fe..f2a45f0f0bbfce93e61d3696a18425af4d022a00 100644 --- a/common.gypi +++ b/common.gypi -@@ -88,6 +88,23 @@ +@@ -90,6 +90,23 @@ ##### end V8 defaults ##### diff --git a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch index 79eff671572bd..72074233f7954 100644 --- a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch +++ b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch @@ -53,10 +53,10 @@ index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..03082f0ec4f91382933eec48e77331cd const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? packageConfig.main || './' : ''; diff --git a/src/node_file.cc b/src/node_file.cc -index 1d22e19f16d5ad82466b0724971b2e4a685682f7..3d7e303741a73134e140152bed637fe5ae8bc1db 100644 +index a492b3aa113c4e1d50cc42721bd5eb58380a6264..c7915e2c8161a5d0fa05ccce368ce9c44345c05d 100644 --- a/src/node_file.cc +++ b/src/node_file.cc -@@ -3220,13 +3220,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { +@@ -3237,13 +3237,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { } BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile( @@ -83,7 +83,7 @@ index 1d22e19f16d5ad82466b0724971b2e4a685682f7..3d7e303741a73134e140152bed637fe5 uv_fs_t req; int rc = uv_fs_stat(env->event_loop(), &req, file_path.c_str(), nullptr); -@@ -3284,6 +3296,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3301,6 +3313,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { std::optional initial_file_path; std::string file_path; @@ -95,7 +95,7 @@ index 1d22e19f16d5ad82466b0724971b2e4a685682f7..3d7e303741a73134e140152bed637fe5 if (args.Length() >= 2 && args[1]->IsString()) { auto package_config_main = Utf8Value(isolate, args[1]).ToString(); -@@ -3304,7 +3321,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3321,7 +3338,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { BufferValue buff_file_path(isolate, local_file_path); ToNamespacedPath(env, &buff_file_path); @@ -104,7 +104,7 @@ index 1d22e19f16d5ad82466b0724971b2e4a685682f7..3d7e303741a73134e140152bed637fe5 case BindingData::FilePathIsFileReturnType::kIsFile: return args.GetReturnValue().Set(i); case BindingData::FilePathIsFileReturnType::kIsNotFile: -@@ -3341,7 +3358,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3358,7 +3375,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { BufferValue buff_file_path(isolate, local_file_path); ToNamespacedPath(env, &buff_file_path); diff --git a/patches/node/fix_assert_module_in_the_renderer_process.patch b/patches/node/fix_assert_module_in_the_renderer_process.patch index 86cfb0a302ffe..e6ab076547afe 100644 --- a/patches/node/fix_assert_module_in_the_renderer_process.patch +++ b/patches/node/fix_assert_module_in_the_renderer_process.patch @@ -44,10 +44,10 @@ index 59b5a16f1309a5e4055bccfdb7a529045ad30402..bfdaf6211466a01b64b7942f7b16c480 let filename = call.getFileName(); const line = call.getLineNumber() - 1; diff --git a/src/node_options.cc b/src/node_options.cc -index 3608ab2b4aeb09e985ca98e23f2dff23567ade71..620776c06d835eb1bfeed060751c570e8d435b29 100644 +index 23cb558a22b4462f5ce4f74833d25c7f712f8139..b22fbb0a285f6f323779d6ebb2b027a3990b031e 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -1527,14 +1527,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { +@@ -1535,14 +1535,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { } Isolate* isolate = args.GetIsolate(); diff --git a/patches/node/fix_cppgc_initializing_twice.patch b/patches/node/fix_cppgc_initializing_twice.patch index f03ffed13d79f..6ecf3b9675de9 100644 --- a/patches/node/fix_cppgc_initializing_twice.patch +++ b/patches/node/fix_cppgc_initializing_twice.patch @@ -12,7 +12,7 @@ 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 2ff08a9cb6124316049a91bda70cf6985045286a..5de93329cbd40a2ca314d602b123092ed15741c5 100644 +index 9f6fa646ba6673f67f5f625e157ed850633a26da..63a462876c00588d22abdd6ed8ee41ecf6590d03 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1173,7 +1173,7 @@ InitializeOncePerProcessInternal(const std::vector& args, diff --git a/patches/node/fix_crypto_tests_to_run_with_bssl.patch b/patches/node/fix_crypto_tests_to_run_with_bssl.patch index 31ecfb8700b29..ce3b8f49c39a5 100644 --- a/patches/node/fix_crypto_tests_to_run_with_bssl.patch +++ b/patches/node/fix_crypto_tests_to_run_with_bssl.patch @@ -11,7 +11,7 @@ before it's acceptable to upstream, as this patch comments out a couple of tests that upstream probably cares about. diff --git a/test/common/index.js b/test/common/index.js -index 92c7294e6f6298f511b5a289e1e0e3a4be68cc77..63a350c5ed912a785b042a238c064c98ed423af4 100644 +index 8f5af57a83dc6b426f1b11bd2e3a8c6c0f2d9a85..f6e00c9f3f3ac4b42662eed6c8d190586f92ab99 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -56,6 +56,8 @@ const hasCrypto = Boolean(process.versions.openssl) && @@ -23,7 +23,7 @@ index 92c7294e6f6298f511b5a289e1e0e3a4be68cc77..63a350c5ed912a785b042a238c064c98 function parseTestFlags(filename = process.argv[1]) { // The copyright notice is relatively big and the flags could come afterwards. const bytesToRead = 1500; -@@ -889,6 +891,7 @@ const common = { +@@ -901,6 +903,7 @@ const common = { mustNotMutateObjectDeep, mustSucceed, nodeProcessAborted, @@ -294,24 +294,6 @@ index 48cd1ed4df61aaddeee8785cb90f83bdd9628187..a18aeb2bdffcc7a7e9ef12328b849994 }); // No-pad encrypted string should return the same: -diff --git a/test/parallel/test-crypto-prime.js b/test/parallel/test-crypto-prime.js -index 5ffdc1394282be046150e3759f82f8787f5604f7..e1c7a7b4824a2df20a405355696022398216fa4f 100644 ---- a/test/parallel/test-crypto-prime.js -+++ b/test/parallel/test-crypto-prime.js -@@ -259,11 +259,11 @@ for (const checks of [-(2 ** 31), -1, 2 ** 31, 2 ** 32 - 1, 2 ** 32, 2 ** 50]) { - bytes[0] = 0x1; - assert.throws(() => checkPrime(bytes, common.mustNotCall()), { - code: 'ERR_OSSL_BN_BIGNUM_TOO_LONG', -- message: /bignum too long/ -+ message: /bignum[_ ]too[_ ]long/i - }); - assert.throws(() => checkPrimeSync(bytes), { - code: 'ERR_OSSL_BN_BIGNUM_TOO_LONG', -- message: /bignum too long/ -+ message: /bignum[_ ]too[_ ]long/i - }); - } - diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js index dcd5045daaf58c60e27c1e2f7941033302241339..6ac75565792b92a97c622baba73f821d754b8d01 100644 --- a/test/parallel/test-crypto-rsa-dsa.js @@ -353,10 +335,10 @@ index dcd5045daaf58c60e27c1e2f7941033302241339..6ac75565792b92a97c622baba73f821d // DSA signatures vary across runs so there is no static string to verify diff --git a/test/parallel/test-crypto-scrypt.js b/test/parallel/test-crypto-scrypt.js -index 338a19b0e88ad6f08d2f6b6a5d38b9980996ce11..a4ee215575d072450ba66c558ddca88bfb23d85f 100644 +index 03a18c7522531c7317f12705550117dc389a0245..2f0f46f2c6ddc62de89877cfa0ca80949a0f4c5e 100644 --- a/test/parallel/test-crypto-scrypt.js +++ b/test/parallel/test-crypto-scrypt.js -@@ -178,7 +178,7 @@ for (const options of bad) { +@@ -176,7 +176,7 @@ for (const options of bad) { for (const options of toobig) { const expected = { @@ -599,6 +581,49 @@ index 6f88e81e9ff29defe73800fc038b0d96d1ebd846..c0b92e2bdf86d3d2638c973f8be3110d }; // Create TLS1.2 server +diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js +index cba5bebaa29b6f8ac4fd0fcedaadb2f7bb3eb321..019d95df499892b14ab088f99013ee32c432779c 100644 +--- a/test/parallel/test-tls-alert-handling.js ++++ b/test/parallel/test-tls-alert-handling.js +@@ -35,7 +35,7 @@ let iter = 0; + + const errorHandler = common.mustCall((err) => { + let expectedErrorCode = 'ERR_SSL_WRONG_VERSION_NUMBER'; +- let expectedErrorReason = 'wrong version number'; ++ let expectedErrorReason = /wrong[\s_]version[\s_]number/i; + if (hasOpenSSL(3, 2)) { + expectedErrorCode = 'ERR_SSL_PACKET_LENGTH_TOO_LONG'; + expectedErrorReason = 'packet length too long'; +@@ -43,8 +43,8 @@ const errorHandler = common.mustCall((err) => { + + assert.strictEqual(err.code, expectedErrorCode); + assert.strictEqual(err.library, 'SSL routines'); +- if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); +- assert.strictEqual(err.reason, expectedErrorReason); ++ if (!hasOpenSSL3 && !common.openSSLIsBoringSSL) assert.strictEqual(err.function, 'ssl3_get_record'); ++ assert.match(err.reason, expectedErrorReason); + errorReceived = true; + if (canCloseServer()) + server.close(); +@@ -98,15 +98,15 @@ function sendBADTLSRecord() { + })); + client.on('error', common.mustCall((err) => { + let expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'; +- let expectedErrorReason = 'tlsv1 alert protocol version'; ++ let expectedErrorReason = /tlsv1[\s_]alert[\s_]protocol[\s_]version/i; + if (hasOpenSSL(3, 2)) { + expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_RECORD_OVERFLOW'; + expectedErrorReason = 'tlsv1 alert record overflow'; + } + assert.strictEqual(err.code, expectedErrorCode); + assert.strictEqual(err.library, 'SSL routines'); +- if (!hasOpenSSL3) ++ if (!hasOpenSSL3 && !common.openSSLIsBoringSSL) + assert.strictEqual(err.function, 'ssl3_read_bytes'); +- assert.strictEqual(err.reason, expectedErrorReason); ++ assert.match(err.reason, expectedErrorReason); + })); + } diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js index b1eab88fd6517e3698934dea17752ef2bb8d8d54..3ad6db20316baa8490e3787dd55903b58a54ad06 100644 --- a/test/parallel/test-tls-getprotocol.js diff --git a/patches/node/fix_do_not_resolve_electron_entrypoints.patch b/patches/node/fix_do_not_resolve_electron_entrypoints.patch index 73608aa7e06e7..68ec69cf3ee8f 100644 --- a/patches/node/fix_do_not_resolve_electron_entrypoints.patch +++ b/patches/node/fix_do_not_resolve_electron_entrypoints.patch @@ -19,10 +19,10 @@ index c9d4a3536d0f60375ae623b48ca2fa7095c88d42..d818320fbbc430d06a0c2852e4723981 context = { __proto__: context, source }; } diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index 0caba80bb213e0edfb1f834250f895ccc05d0d1c..6feab19d24a3524e36c5ed3bbac53cba0fa298c7 100644 +index 49aacb6262502ced54e817f99dd596db85b9659c..f9f065bb743275e9b2ce71375e6a9f06e00c0f36 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js -@@ -286,6 +286,9 @@ function cjsPreparseModuleExports(filename, source) { +@@ -291,6 +291,9 @@ function cjsPreparseModuleExports(filename, source, isMain, format) { if (module && module[kModuleExportNames] !== undefined) { return { module, exportNames: module[kModuleExportNames] }; } diff --git a/patches/node/fix_expose_readfilesync_override_for_modules.patch b/patches/node/fix_expose_readfilesync_override_for_modules.patch index d30767517d58a..c54548cd8b874 100644 --- a/patches/node/fix_expose_readfilesync_override_for_modules.patch +++ b/patches/node/fix_expose_readfilesync_override_for_modules.patch @@ -8,10 +8,10 @@ an API override to replace the native `ReadFileSync` in the `modules` binding. diff --git a/src/env_properties.h b/src/env_properties.h -index 9f89823170782242093bc5ee0df6a2a2ef5b919f..b9374ee1acceb3d0aab51c6c5ae6a79be1cc71a9 100644 +index ba8c80ff2842c63f16cae51cfa8084617bb35bf5..820aebd18d22bcef4992b09ffc8924e9b758fd3e 100644 --- a/src/env_properties.h +++ b/src/env_properties.h -@@ -478,6 +478,7 @@ +@@ -485,6 +485,7 @@ V(maybe_cache_generated_source_map, v8::Function) \ V(messaging_deserialize_create_object, v8::Function) \ V(message_port, v8::Object) \ diff --git a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch index f6652a3aeefaa..baa89612d412d 100644 --- a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch +++ b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch @@ -70,19 +70,19 @@ index bfd9bd3d127404de1cbb6f30c43ab0342590759d..9e7d8ef0adef3b68a3ec186e4b218f59 const packageConfig = packageJsonReader.read(packageJSONPath, { __proto__: null, specifier, base, isESM: true }); diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index a587246e329b41f33a3fdfe5ef92910915911611..1b94d923b6d83cc7806d793497a4f9f978c5938c 100644 +index 7dcd7afe8ce3c25ceb314cdf15c330f3d66eb84f..4445bcc4c4c6c6f87ac45e693012a18a93739f9b 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js -@@ -182,7 +182,7 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) { +@@ -186,7 +186,7 @@ function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModul - const { exportNames, module } = cjsPreparseModuleExports(filename, source); + const { exportNames, module } = cjsPreparseModuleExports(filename, source, isMain, format); cjsCache.set(url, module); - const namesWithDefault = exportNames.has('default') ? + const namesWithDefault = filename === 'electron' ? ['default', ...Object.keys(module.exports)] : exportNames.has('default') ? [...exportNames] : ['default', ...exportNames]; if (isMain) { -@@ -204,8 +204,8 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) { +@@ -208,8 +208,8 @@ function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModul ({ exports } = module); } for (const exportName of exportNames) { @@ -93,8 +93,8 @@ index a587246e329b41f33a3fdfe5ef92910915911611..1b94d923b6d83cc7806d793497a4f9f9 continue; } // We might trigger a getter -> dont fail. -@@ -239,6 +239,10 @@ translators.set('require-commonjs', (url, source, isMain) => { - return createCJSModuleWrap(url, source); +@@ -243,6 +243,10 @@ translators.set('require-commonjs', (url, source, isMain) => { + return createCJSModuleWrap(url, source, isMain, 'commonjs'); }); +translators.set('electron', () => { diff --git a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch index cdd487fd9f6c2..3d8bea2f74f95 100644 --- a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch +++ b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch @@ -238,20 +238,10 @@ index d94f6e1c82c4a62547b3b395f375c86ce4deb5de..b81b9005365272217c77e2b9289bd9f8 X509View ca(sk_X509_value(peer_certs.get(), i)); if (!cert->view().isIssuedBy(ca)) continue; diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc -index c89d591c6804ab7d41199d61452d10d12cdf7398..05740c7dc599954bca0779b8c8d6bd615183288a 100644 +index a054e4c1285208c9ba8b9679c284f459f1ace690..3de8ef4fafcdbdc2cb0ce31de162663d5272340f 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc -@@ -26,7 +26,9 @@ using ncrypto::BIOPointer; - using ncrypto::ClearErrorOnReturn; - using ncrypto::CryptoErrorList; - using ncrypto::DHPointer; -+#ifndef OPENSSL_NO_ENGINE - using ncrypto::EnginePointer; -+#endif // !OPENSSL_NO_ENGINE - using ncrypto::EVPKeyPointer; - using ncrypto::MarkPopErrorOnReturn; - using ncrypto::SSLPointer; -@@ -105,7 +107,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, +@@ -123,7 +123,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, // the CA certificates. SSL_CTX_clear_extra_chain_certs(ctx); @@ -260,7 +250,7 @@ index c89d591c6804ab7d41199d61452d10d12cdf7398..05740c7dc599954bca0779b8c8d6bd61 X509* ca = sk_X509_value(extra_certs, i); // NOTE: Increments reference count on `ca` -@@ -931,11 +933,12 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { +@@ -1584,11 +1584,12 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo& args) { // If the user specified "auto" for dhparams, the JavaScript layer will pass // true to this function instead of the original string. Any other string // value will be interpreted as custom DH parameters below. @@ -274,7 +264,7 @@ index c89d591c6804ab7d41199d61452d10d12cdf7398..05740c7dc599954bca0779b8c8d6bd61 DHPointer dh; { BIOPointer bio(LoadBIO(env, args[0])); -@@ -1161,7 +1164,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) { +@@ -1814,7 +1815,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) { } // Add CA certs too @@ -416,7 +406,7 @@ index 471fee77531139ce988292470dff443fdfb05b07..931f7c2ae3d7e12afce471545d610d22 return EVPKeyCtxPointer(); diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc -index f66c57b1079af6cd040dc6d11e72f353507b75e5..abd2bccb9669e06dd8355f66220f8b06c8e863dc 100644 +index b38a9a377738fd5fe6cc89c3a27c403bf6a97715..0cd43c2005b431e180b7483cb89825a75e1fe03f 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -949,6 +949,7 @@ void KeyObjectHandle::GetAsymmetricKeyType( @@ -469,20 +459,20 @@ index 05a3882c7e17d78e27aabb29891aa250789a47c0..1f2fccce6ed8f14525557644e0bdd130 if (target diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc -index e255288f6e013ce122f317c415d73d9c93d38580..25fa9af8153852f49d5289aa253f3c8f7268d89c 100644 +index 7c548d32b40365343f0e208c3aa856a1c847f4c3..6346f8f7199cf7b7d3736c59571606fff102fbb6 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc -@@ -29,7 +29,9 @@ namespace node { - using ncrypto::BignumPointer; - using ncrypto::BIOPointer; - using ncrypto::CryptoErrorList; -+#ifndef OPENSSL_NO_ENGINE - using ncrypto::EnginePointer; -+#endif // !OPENSSL_NO_ENGINE - using ncrypto::EVPKeyCtxPointer; - using v8::ArrayBuffer; - using v8::BackingStore; -@@ -502,24 +504,15 @@ Maybe Decorate(Environment* env, +@@ -207,7 +207,8 @@ void TestFipsCrypto(const v8::FunctionCallbackInfo& args) { + + void GetOpenSSLSecLevelCrypto(const FunctionCallbackInfo& args) { + // for BoringSSL assume the same as the default +- int sec_level = OPENSSL_TLS_SECURITY_LEVEL; ++ // value of OPENSSL_TLS_SECURITY_LEVEL. ++ int sec_level = 1; + #ifndef OPENSSL_IS_BORINGSSL + Environment* env = Environment::GetCurrent(args); + +@@ -527,24 +528,15 @@ Maybe Decorate(Environment* env, V(BIO) \ V(PKCS7) \ V(X509V3) \ @@ -508,7 +498,7 @@ index e255288f6e013ce122f317c415d73d9c93d38580..25fa9af8153852f49d5289aa253f3c8f V(USER) \ #define V(name) case ERR_LIB_##name: lib = #name "_"; break; -@@ -661,7 +654,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { +@@ -686,7 +678,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { CHECK(args[0]->IsUint32()); Environment* env = Environment::GetCurrent(args); uint32_t len = args[0].As()->Value(); @@ -517,7 +507,7 @@ index e255288f6e013ce122f317c415d73d9c93d38580..25fa9af8153852f49d5289aa253f3c8f if (data == nullptr) { // There's no memory available for the allocation. // Return nothing. -@@ -672,7 +665,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { +@@ -697,7 +689,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { data, len, [](void* data, size_t len, void* deleter_data) { @@ -526,7 +516,7 @@ index e255288f6e013ce122f317c415d73d9c93d38580..25fa9af8153852f49d5289aa253f3c8f }, data); Local buffer = ArrayBuffer::New(env->isolate(), store); -@@ -680,10 +673,12 @@ void SecureBuffer(const FunctionCallbackInfo& args) { +@@ -705,10 +697,12 @@ void SecureBuffer(const FunctionCallbackInfo& args) { } void SecureHeapUsed(const FunctionCallbackInfo& args) { @@ -540,7 +530,7 @@ index e255288f6e013ce122f317c415d73d9c93d38580..25fa9af8153852f49d5289aa253f3c8f } // namespace diff --git a/src/env.h b/src/env.h -index 1239cbdbf2d375a50ada37ee0ed5592c751d4c5c..aed066852d7c257076cc7ca8b173fd2a3a353a00 100644 +index b6bdff9b8580d2588a39f00b594c4c480157d78a..cfe917c797a6e4bb0f0284ec56be82637f840129 100644 --- a/src/env.h +++ b/src/env.h @@ -50,7 +50,7 @@ @@ -552,7 +542,7 @@ index 1239cbdbf2d375a50ada37ee0ed5592c751d4c5c..aed066852d7c257076cc7ca8b173fd2a #include #endif -@@ -1071,7 +1071,7 @@ class Environment final : public MemoryRetainer { +@@ -1073,7 +1073,7 @@ class Environment final : public MemoryRetainer { kExitInfoFieldCount }; @@ -562,7 +552,7 @@ index 1239cbdbf2d375a50ada37ee0ed5592c751d4c5c..aed066852d7c257076cc7ca8b173fd2a // We declare another alias here to avoid having to include crypto_util.h using EVPMDPointer = DeleteFnPtr; diff --git a/src/node_metadata.h b/src/node_metadata.h -index c59e65ad1fe3fac23f1fc25ca77e6133d1ccaccd..f2f07434e076e2977755ef7dac7d489aedb760b0 100644 +index 6f8cb433ff8059c63d5cf16c8783139ae92fbf61..603ac3dde7d1a1109afbc451b69c8d0935b81641 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -6,7 +6,7 @@ @@ -575,7 +565,7 @@ index c59e65ad1fe3fac23f1fc25ca77e6133d1ccaccd..f2f07434e076e2977755ef7dac7d489a #if NODE_OPENSSL_HAS_QUIC #include diff --git a/src/node_options.cc b/src/node_options.cc -index 1d81079a9b7d8a69ad2d87835090be88ae507bd8..3608ab2b4aeb09e985ca98e23f2dff23567ade71 100644 +index 1444acd59d42f64831cead5f153419f7c12a88bf..23cb558a22b4462f5ce4f74833d25c7f712f8139 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -6,7 +6,7 @@ @@ -588,7 +578,7 @@ index 1d81079a9b7d8a69ad2d87835090be88ae507bd8..3608ab2b4aeb09e985ca98e23f2dff23 #endif diff --git a/src/node_options.h b/src/node_options.h -index 621f5eca96b10685734a39e56cce7cee6c8a25bf..41dd04f5e2b1cd54c32df70830389d44d7b39aa2 100644 +index e68a41b60832c4b000e17dd15ce16c1bdaf4b54b..065457acfde6ba4d04ed570cc72005cfd2798fd5 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -11,7 +11,7 @@ @@ -600,24 +590,3 @@ index 621f5eca96b10685734a39e56cce7cee6c8a25bf..41dd04f5e2b1cd54c32df70830389d44 #include "openssl/opensslv.h" #endif -diff --git a/unofficial.gni b/unofficial.gni -index a2f3a769ceaa08db6d7438223884dc5aeab1340d..08603eaef2da51fd92f9bf977647b56409eff48c 100644 ---- a/unofficial.gni -+++ b/unofficial.gni -@@ -151,7 +151,6 @@ template("node_gn_build") { - ] - deps = [ - ":run_node_js2c", -- "deps/brotli", - "deps/cares", - "deps/histogram", - "deps/llhttp", -@@ -161,6 +160,8 @@ template("node_gn_build") { - "deps/sqlite", - "deps/uvwasi", - "//third_party/zlib", -+ "//third_party/brotli:dec", -+ "//third_party/brotli:enc", - "$node_simdutf_path", - "$node_v8_path:v8_libplatform", - ] diff --git a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch index fa54a035afce3..8de78da28f2fe 100644 --- a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch +++ b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch @@ -60,7 +60,7 @@ index 9e7d8ef0adef3b68a3ec186e4b218f591aa69266..2879e5cf541fb4d226cfd7cc0fe367ca }); const { search, hash } = resolved; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index 1b94d923b6d83cc7806d793497a4f9f978c5938c..0caba80bb213e0edfb1f834250f895ccc05d0d1c 100644 +index 4445bcc4c4c6c6f87ac45e693012a18a93739f9b..49aacb6262502ced54e817f99dd596db85b9659c 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -24,7 +24,7 @@ const { @@ -69,10 +69,10 @@ index 1b94d923b6d83cc7806d793497a4f9f978c5938c..0caba80bb213e0edfb1f834250f895cc const assert = require('internal/assert'); -const { readFileSync } = require('fs'); +const fs = require('fs'); - const { dirname, extname, isAbsolute } = require('path'); + const { dirname, extname } = require('path'); const { assertBufferSource, -@@ -268,7 +268,7 @@ translators.set('commonjs', function commonjsStrategy(url, source, isMain) { +@@ -272,7 +272,7 @@ translators.set('commonjs', function commonjsStrategy(url, source, isMain) { try { // We still need to read the FS to detect the exports. @@ -81,12 +81,3 @@ index 1b94d923b6d83cc7806d793497a4f9f978c5938c..0caba80bb213e0edfb1f834250f895cc } catch { // Continue regardless of error. } -@@ -335,7 +335,7 @@ function cjsPreparseModuleExports(filename, source) { - isAbsolute(resolved)) { - // TODO: this should be calling the `load` hook chain to get the source - // (and fallback to reading the FS only if the source is nullish). -- const source = readFileSync(resolved, 'utf-8'); -+ const source = fs.readFileSync(resolved, 'utf-8'); - const { exportNames: reexportNames } = cjsPreparseModuleExports(resolved, source); - for (const name of reexportNames) { - exportNames.add(name); diff --git a/patches/node/fix_remove_deprecated_errno_constants.patch b/patches/node/fix_remove_deprecated_errno_constants.patch index ab580184a3177..0e9ce33bc3b42 100644 --- a/patches/node/fix_remove_deprecated_errno_constants.patch +++ b/patches/node/fix_remove_deprecated_errno_constants.patch @@ -86,7 +86,7 @@ index 13263149c111beede83b7063fa54f56655aea54c..99068098e1867af4846e18a5d039a256 NODE_DEFINE_CONSTANT(target, ETIMEDOUT); #endif diff --git a/src/node_errors.cc b/src/node_errors.cc -index 609601328f7f5ff5f121151e81c2df82e7ef4253..6b9b944c11af917fe4e296961e6ed7df7b89a123 100644 +index 5f51add4cdf68a9487edfc9382f586cc94539571..befb642f1effa3c4139e4cd99ff64d9c5175fd72 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -862,10 +862,6 @@ const char* errno_string(int errorno) { diff --git a/patches/node/fix_remove_fastapitypedarray_usage.patch b/patches/node/fix_remove_fastapitypedarray_usage.patch index b1dad662b7165..7ce2dea2c4fe0 100644 --- a/patches/node/fix_remove_fastapitypedarray_usage.patch +++ b/patches/node/fix_remove_fastapitypedarray_usage.patch @@ -48,7 +48,7 @@ index 867a1c4aca54b9d41490d23a5eb55088b7e941cc..09f4c65a18efea262b1f854f993c6f18 static v8::CFunction fast_equal(v8::CFunction::Make(FastTimingSafeEqual)); diff --git a/src/node_buffer.cc b/src/node_buffer.cc -index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe8b747a61 100644 +index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee4d8ef8f0 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -44,6 +44,14 @@ @@ -74,7 +74,7 @@ index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe using v8::FunctionCallbackInfo; using v8::Global; using v8::HandleScope; -@@ -582,19 +589,24 @@ void SlowCopy(const FunctionCallbackInfo& args) { +@@ -586,19 +593,24 @@ void SlowCopy(const FunctionCallbackInfo& args) { // Assume caller has properly validated args. uint32_t FastCopy(Local receiver, @@ -107,7 +107,7 @@ index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe return to_copy; } -@@ -858,19 +870,17 @@ void Compare(const FunctionCallbackInfo &args) { +@@ -867,19 +879,17 @@ void Compare(const FunctionCallbackInfo &args) { } int32_t FastCompare(v8::Local, @@ -135,7 +135,7 @@ index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe } static v8::CFunction fast_compare(v8::CFunction::Make(FastCompare)); -@@ -1141,14 +1151,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo& args) { +@@ -1150,14 +1160,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo& args) { } int32_t FastIndexOfNumber(v8::Local, @@ -153,7 +153,7 @@ index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe } static v8::CFunction fast_index_of_number( -@@ -1501,21 +1510,31 @@ void SlowWriteString(const FunctionCallbackInfo& args) { +@@ -1511,21 +1520,31 @@ void SlowWriteString(const FunctionCallbackInfo& args) { template uint32_t FastWriteString(Local receiver, @@ -194,10 +194,10 @@ index cd51d9acf9540d506ec35812b9d2c530fce24912..07068344262f7b73a83073d4da75bffe static v8::CFunction fast_write_string_ascii( diff --git a/src/node_external_reference.h b/src/node_external_reference.h -index 8d49a119c218323674e29a522ecf71bd22cdaf1b..d39693f2f45f39e45960453112b0f6460a1b3a4d 100644 +index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d46afdce75 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h -@@ -40,16 +40,16 @@ using CFunctionCallbackWithStrings = +@@ -43,16 +43,16 @@ using CFunctionCallbackWithStrings = const v8::FastOneByteString& base); using CFunctionCallbackWithTwoUint8Arrays = int32_t (*)(v8::Local, @@ -219,7 +219,7 @@ index 8d49a119c218323674e29a522ecf71bd22cdaf1b..d39693f2f45f39e45960453112b0f646 uint32_t, int64_t, bool); -@@ -68,18 +68,20 @@ using CFunctionWithBool = void (*)(v8::Local, +@@ -71,18 +71,20 @@ using CFunctionWithBool = void (*)(v8::Local, using CFunctionWriteString = uint32_t (*)(v8::Local receiver, @@ -246,7 +246,7 @@ index 8d49a119c218323674e29a522ecf71bd22cdaf1b..d39693f2f45f39e45960453112b0f646 // This class manages the external references from the V8 heap // to the C++ addresses in Node.js. diff --git a/src/util.h b/src/util.h -index 0d4676ddade8d91d101b6aeb8763886a234f0bae..7af9ed01a919927ae3897d4989206ec23cfe50d3 100644 +index 48d3c7b8a304049cdb4d4ab2c027b300dc533dc0..f9d5a5b36701b3c65fda65ed8920521ff68e32cd 100644 --- a/src/util.h +++ b/src/util.h @@ -59,6 +59,7 @@ @@ -257,7 +257,7 @@ index 0d4676ddade8d91d101b6aeb8763886a234f0bae..7af9ed01a919927ae3897d4989206ec2 #ifdef _WIN32 /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ -@@ -579,6 +580,16 @@ class BufferValue : public MaybeStackBuffer { +@@ -584,6 +585,16 @@ class BufferValue : public MaybeStackBuffer { static_cast(name->Buffer()->Data()) + name##_offset; \ if (name##_length > 0) CHECK_NE(name##_data, nullptr); diff --git a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch index 593eb1542b256..adb0fcc8506a3 100644 --- a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch +++ b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch @@ -11,7 +11,7 @@ This patch can be removed when we upgrade to a V8 version that contains the above CL. diff --git a/src/node.cc b/src/node.cc -index f4365c0eda7330bd02a921608951902f41004f77..b2b10ffb572f010992f221de752618fd56b5d50e 100644 +index a0f1deadfc58f18f23467889680219360386f9dd..8da5f5344051663f92d72848fbac9d041ac4fac3 100644 --- a/src/node.cc +++ b/src/node.cc @@ -808,7 +808,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, diff --git a/patches/node/pass_all_globals_through_require.patch b/patches/node/pass_all_globals_through_require.patch index c6c29f00a2ea3..024cfdbfdddd5 100644 --- a/patches/node/pass_all_globals_through_require.patch +++ b/patches/node/pass_all_globals_through_require.patch @@ -6,10 +6,10 @@ Subject: Pass all globals through "require" (cherry picked from commit 7d015419cb7a0ecfe6728431a4ed2056cd411d62) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 62c33730ed17cb98b6dd8d69b61360a419518ba5..9b5772fe9b8babbb892c7a5ec79258472da55a76 100644 +index 33385fa792b71ea3802904dd3c59ce845342c595..92b368394e17a9257578cd5b7422391689732d6d 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js -@@ -185,6 +185,13 @@ const { +@@ -200,6 +200,13 @@ const { CHAR_FORWARD_SLASH, } = require('internal/constants'); @@ -23,7 +23,7 @@ index 62c33730ed17cb98b6dd8d69b61360a419518ba5..9b5772fe9b8babbb892c7a5ec7925847 const { isProxy, } = require('internal/util/types'); -@@ -1549,10 +1556,12 @@ Module.prototype._compile = function(content, filename, format) { +@@ -1725,10 +1732,12 @@ Module.prototype._compile = function(content, filename, format) { if (this[kIsMainSymbol] && getOptionValue('--inspect-brk')) { const { callAndPauseOnStart } = internalBinding('inspector'); result = callAndPauseOnStart(compiledWrapper, thisValue, exports, diff --git a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch index 091bdc3abcec9..e7344756b3855 100644 --- a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch +++ b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch @@ -7,10 +7,10 @@ We use this to allow node's 'fs' module to read from ASAR files as if they were a real filesystem. diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js -index f5c0208864084a234a05898e793845681b6e80cc..48d809f61eaf09097acb3e996e956e39cf7b4ef3 100644 +index 4fd535f730e6382672b853bf2098b3fefc1f83b4..bd6724de52ee1f01fa42084c7652c71054f0a9c6 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js -@@ -134,6 +134,10 @@ process.domain = null; +@@ -132,6 +132,10 @@ process.domain = null; } process._exiting = false; diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch index 7ed48324d3c39..7d775d9ef61ba 100644 --- a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -18,7 +18,7 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs from the above issue. diff --git a/src/api/environment.cc b/src/api/environment.cc -index 9b155213ce301df7e396a4a113992499fc7e9910..8fe560014216f1fcea7f6e804816765999cebaa2 100644 +index 7f4f233b26425493a58ce71dfc0c3a92b7c0bef8..c3f422c6b212bf737a906d2a89df85b63c218617 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -312,6 +312,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, @@ -47,10 +47,10 @@ index 9b155213ce301df7e396a4a113992499fc7e9910..8fe560014216f1fcea7f6e8048167659 event_loop, platform, diff --git a/src/env.cc b/src/env.cc -index 1c1062a3996f2bb7de9e91f7f4385c8f8d20f490..b0156ee26c29ebe5b79c97834f3bfe8b56df50c6 100644 +index ddf82c8a18c29c355641e33974c1e5e67867379d..ae43803d8c2de90fb191a8e10605f6f3b18816ed 100644 --- a/src/env.cc +++ b/src/env.cc -@@ -575,14 +575,6 @@ IsolateData::IsolateData(Isolate* isolate, +@@ -577,14 +577,6 @@ IsolateData::IsolateData(Isolate* isolate, // We do not care about overflow since we just want this to be different // from the cppgc id. uint16_t non_cppgc_id = cppgc_id + 1; @@ -65,12 +65,13 @@ index 1c1062a3996f2bb7de9e91f7f4385c8f8d20f490..b0156ee26c29ebe5b79c97834f3bfe8b { // GC could still be run after the IsolateData is destroyed, so we store // the ids in a static map to ensure pointers to them are still valid -@@ -605,14 +597,6 @@ IsolateData::IsolateData(Isolate* isolate, +@@ -607,15 +599,6 @@ IsolateData::IsolateData(Isolate* isolate, } } -IsolateData::~IsolateData() { - if (cpp_heap_ != nullptr) { +- v8::Locker locker(isolate_); - // The CppHeap must be detached before being terminated. - isolate_->DetachCppHeap(); - cpp_heap_->Terminate(); @@ -81,7 +82,7 @@ index 1c1062a3996f2bb7de9e91f7f4385c8f8d20f490..b0156ee26c29ebe5b79c97834f3bfe8b void SetCppgcReference(Isolate* isolate, Local object, diff --git a/src/env.h b/src/env.h -index 1f8dc8f88d40ca95ba13d6517b2b5ed83184e1ce..ec3a813b3b864a0eda2e4aa0748b1447001fca9a 100644 +index 9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f..c429eecd937d1df32a2ff90bc0a22a2e58df3a3d 100644 --- a/src/env.h +++ b/src/env.h @@ -155,7 +155,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { @@ -101,7 +102,7 @@ index 1f8dc8f88d40ca95ba13d6517b2b5ed83184e1ce..ec3a813b3b864a0eda2e4aa0748b1447 worker::Worker* worker_context_ = nullptr; PerIsolateWrapperData* wrapper_data_; diff --git a/src/node.cc b/src/node.cc -index b2b10ffb572f010992f221de752618fd56b5d50e..0ed78ab6b52906e980eebf1f625a1c7cbfc8097b 100644 +index 8da5f5344051663f92d72848fbac9d041ac4fac3..2f58a6fa69069dabb99b5ddb8011991b07fa5f02 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1222,10 +1222,6 @@ InitializeOncePerProcessInternal(const std::vector& args, @@ -263,10 +264,10 @@ index edd413ae9b956b2e59e8166785adef6a8ff06d51..d1c1549efcb0320bc0f7d354db2101ac // Check that all the objects are created and destroyed properly. EXPECT_EQ(CppGCed::kConstructCount, 100); diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc -index 14e82cc80ff73084fb43b2ef07febfd2667a0abc..b6a92f1685d1083c8f0c0b3ed110509f6d76b59f 100644 +index 008cda77b650dc2d904ae00e7629b5ad05d297ad..103931516cea9beb7f25c53526928e67b3c90d2d 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc -@@ -623,6 +623,9 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { +@@ -625,6 +625,9 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { // Allocate and initialize Isolate. v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = allocator.get(); @@ -276,7 +277,7 @@ index 14e82cc80ff73084fb43b2ef07febfd2667a0abc..b6a92f1685d1083c8f0c0b3ed110509f v8::Isolate* isolate = v8::Isolate::Allocate(); CHECK_NOT_NULL(isolate); platform->RegisterIsolate(isolate, ¤t_loop); -@@ -672,8 +675,8 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { +@@ -675,8 +678,8 @@ TEST_F(NodeZeroIsolateTestFixture, CtrlCWithOnlySafeTerminationTest) { } // Cleanup. @@ -287,10 +288,10 @@ index 14e82cc80ff73084fb43b2ef07febfd2667a0abc..b6a92f1685d1083c8f0c0b3ed110509f #endif // _WIN32 diff --git a/test/cctest/test_platform.cc b/test/cctest/test_platform.cc -index c2d7893813000601502d050f21ad5c740c6a3b8f..3042f63201bd0df3ad316e09f0f54e210cea8831 100644 +index 53644accf29749bf8fc18b641ae1eaef93cd6f98..7e5b143fb4b633e18a4b2d7440cba7e077c50950 100644 --- a/test/cctest/test_platform.cc +++ b/test/cctest/test_platform.cc -@@ -101,8 +101,8 @@ TEST_F(NodeZeroIsolateTestFixture, IsolatePlatformDelegateTest) { +@@ -102,8 +102,8 @@ TEST_F(NodeZeroIsolateTestFixture, IsolatePlatformDelegateTest) { // Graceful shutdown delegate->Shutdown(); diff --git a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch index b99712eaf2547..eb31ed5e0e4a6 100644 --- a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch +++ b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch @@ -16,7 +16,7 @@ patch: (cherry picked from commit 30329d06235a9f9733b1d4da479b403462d1b326) diff --git a/src/env-inl.h b/src/env-inl.h -index 49c3513ee9b67b407a2bb6609ac89a5959a78cd1..9a8216354e646e86d692b25c2bed5134e267528c 100644 +index d4b211dfb2f77a65f311701d95365e66d92f8875..4c1a5f2b92d7fdddb8c2e7bbfd6788fdff3645b9 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -62,31 +62,6 @@ inline uv_loop_t* IsolateData::event_loop() const { @@ -52,7 +52,7 @@ index 49c3513ee9b67b407a2bb6609ac89a5959a78cd1..9a8216354e646e86d692b25c2bed5134 return &(wrapper_data_->cppgc_id); } diff --git a/src/env.cc b/src/env.cc -index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f8d20f490 100644 +index f8c24e422756d5101a258175a2f28a96648bea47..ddf82c8a18c29c355641e33974c1e5e67867379d 100644 --- a/src/env.cc +++ b/src/env.cc @@ -23,6 +23,7 @@ @@ -63,7 +63,7 @@ index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f #include #include -@@ -70,7 +71,6 @@ using v8::TryCatch; +@@ -72,7 +73,6 @@ using v8::TryCatch; using v8::Uint32; using v8::Undefined; using v8::Value; @@ -71,7 +71,7 @@ index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f using worker::Worker; int const ContextEmbedderTag::kNodeContextTag = 0x6e6f64; -@@ -527,6 +527,14 @@ void IsolateData::CreateProperties() { +@@ -529,6 +529,14 @@ void IsolateData::CreateProperties() { CreateEnvProxyTemplate(this); } @@ -86,7 +86,7 @@ index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f constexpr uint16_t kDefaultCppGCEmbedderID = 0x90de; Mutex IsolateData::isolate_data_mutex_; std::unordered_map> -@@ -564,36 +572,16 @@ IsolateData::IsolateData(Isolate* isolate, +@@ -566,36 +574,16 @@ IsolateData::IsolateData(Isolate* isolate, v8::CppHeap* cpp_heap = isolate->GetCppHeap(); uint16_t cppgc_id = kDefaultCppGCEmbedderID; @@ -130,7 +130,7 @@ index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f { // GC could still be run after the IsolateData is destroyed, so we store -@@ -625,11 +613,12 @@ IsolateData::~IsolateData() { +@@ -628,11 +616,12 @@ IsolateData::~IsolateData() { } } @@ -146,7 +146,7 @@ index b428c922ed218cbdf19cdba50d18b1f81a56b8ca..1c1062a3996f2bb7de9e91f7f4385c8f void IsolateData::MemoryInfo(MemoryTracker* tracker) const { diff --git a/src/env.h b/src/env.h -index aed066852d7c257076cc7ca8b173fd2a3a353a00..1f8dc8f88d40ca95ba13d6517b2b5ed83184e1ce 100644 +index cfe917c797a6e4bb0f0284ec56be82637f840129..9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f 100644 --- a/src/env.h +++ b/src/env.h @@ -175,10 +175,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { @@ -161,10 +161,10 @@ index aed066852d7c257076cc7ca8b173fd2a3a353a00..1f8dc8f88d40ca95ba13d6517b2b5ed8 inline MultiIsolatePlatform* platform() const; inline const SnapshotData* snapshot_data() const; diff --git a/src/node.h b/src/node.h -index 120e3a1042e29590cbbf4be258a1cd2d3d4f0043..afb26ec5690ccd65a3c36f8b8a1b2de416b9d843 100644 +index bdc77f8eb7abffa9e6c98cd254daedad3e44b981..98ad0ea649eaef43d1f5231f7bc4044e100e08d7 100644 --- a/src/node.h +++ b/src/node.h -@@ -1552,24 +1552,14 @@ void RegisterSignalHandler(int signal, +@@ -1553,24 +1553,14 @@ void RegisterSignalHandler(int signal, bool reset_handler = false); #endif // _WIN32 diff --git a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch index 8c61c3b3d788c..1e68d7e6b68f3 100644 --- a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch +++ b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch @@ -40,10 +40,10 @@ index 0f0cde7be431dcb80c5314b1a9da49886c436d1c..f6d2bd439cad8b9f91c9d9a6cdb302e6 } HistogramBase* histogram; diff --git a/src/node_file.cc b/src/node_file.cc -index 3d7e303741a73134e140152bed637fe5ae8bc1db..5e744bc34b9dc364e8f20adfd37ee41d76451170 100644 +index c7915e2c8161a5d0fa05ccce368ce9c44345c05d..23347379328794467a497c86cbae0b428b7ba791 100644 --- a/src/node_file.cc +++ b/src/node_file.cc -@@ -1061,13 +1061,8 @@ static int32_t FastInternalModuleStat( +@@ -1071,13 +1071,8 @@ static int32_t FastInternalModuleStat( // NOLINTNEXTLINE(runtime/references) This is V8 api. FastApiCallbackOptions& options) { // This needs a HandleScope which needs an isolate. @@ -60,10 +60,10 @@ index 3d7e303741a73134e140152bed637fe5ae8bc1db..5e744bc34b9dc364e8f20adfd37ee41d auto path = std::filesystem::path(input.data, input.data + input.length); diff --git a/src/node_wasi.cc b/src/node_wasi.cc -index 468c2e59903fefe58d9c178d3afac3ef5b09f611..23a376e52e08a8af49dd47c47488552e01287426 100644 +index 090866960beb8f1759c99e95536924b8b61fb723..3f91b651b83a20e70d5b368e012f5ee4b9d16092 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc -@@ -251,17 +251,19 @@ R WASI::WasiFunction::FastCallback( +@@ -275,17 +275,19 @@ R WASI::WasiFunction::FastCallback( return EinvalError(); } diff --git a/patches/node/support_v8_sandboxed_pointers.patch b/patches/node/support_v8_sandboxed_pointers.patch index 100326397f400..f909667671ec9 100644 --- a/patches/node/support_v8_sandboxed_pointers.patch +++ b/patches/node/support_v8_sandboxed_pointers.patch @@ -7,7 +7,7 @@ This refactors several allocators to allocate within the V8 memory cage, allowing them to be compatible with the V8_SANDBOXED_POINTERS feature. diff --git a/src/api/environment.cc b/src/api/environment.cc -index ad323fc800a33c010b0504a4aa55c107498dee26..fc9b056d2f7e25109100fbde5f3ab0aebc8c619a 100644 +index 88c2c932a6b045317c83c911b1cd8267b60d9334..7f4f233b26425493a58ce71dfc0c3a92b7c0bef8 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -102,6 +102,14 @@ MaybeLocal PrepareStackTraceCallback(Local context, @@ -64,10 +64,10 @@ index 5387d9625a28bb7d11f7f0f05a5f07d1fee2c216..1b3b8c7b70073926f8dbf02759c2e1af return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc -index 25fa9af8153852f49d5289aa253f3c8f7268d89c..a67268f78b18cf71b90b9e31ad733eb0c8d93af3 100644 +index 6346f8f7199cf7b7d3736c59571606fff102fbb6..7eea2eaefcad5780663a6b87985925ae5d70a5f9 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc -@@ -335,10 +335,35 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept { +@@ -359,10 +359,35 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept { return *this; } @@ -104,7 +104,7 @@ index 25fa9af8153852f49d5289aa253f3c8f7268d89c..a67268f78b18cf71b90b9e31ad733eb0 std::unique_ptr ptr = ArrayBuffer::NewBackingStore( allocated_data_, size(), -@@ -350,10 +375,11 @@ std::unique_ptr ByteSource::ReleaseToBackingStore() { +@@ -374,10 +399,11 @@ std::unique_ptr ByteSource::ReleaseToBackingStore() { data_ = nullptr; size_ = 0; return ptr; @@ -117,7 +117,7 @@ index 25fa9af8153852f49d5289aa253f3c8f7268d89c..a67268f78b18cf71b90b9e31ad733eb0 return ArrayBuffer::New(env->isolate(), std::move(store)); } -@@ -650,6 +676,16 @@ namespace { +@@ -674,6 +700,16 @@ namespace { // in which case this has the same semantics as // using OPENSSL_malloc. However, if the secure heap is // initialized, SecureBuffer will automatically use it. @@ -134,7 +134,7 @@ index 25fa9af8153852f49d5289aa253f3c8f7268d89c..a67268f78b18cf71b90b9e31ad733eb0 void SecureBuffer(const FunctionCallbackInfo& args) { CHECK(args[0]->IsUint32()); Environment* env = Environment::GetCurrent(args); -@@ -671,6 +707,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { +@@ -695,6 +731,7 @@ void SecureBuffer(const FunctionCallbackInfo& args) { Local buffer = ArrayBuffer::New(env->isolate(), store); args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len)); } @@ -143,10 +143,10 @@ index 25fa9af8153852f49d5289aa253f3c8f7268d89c..a67268f78b18cf71b90b9e31ad733eb0 void SecureHeapUsed(const FunctionCallbackInfo& args) { #ifndef OPENSSL_IS_BORINGSSL diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h -index a5967c7d24b8365eb64ab63ec0b3ef8dc23c727e..2acebc3ac2ed9f631fc572f42f19f2e3dccfeb12 100644 +index b85c8daeb464097c2e93bdc7ffdfcfe16b76234b..470a0c4adadcd092dd0105c384e87917ac6fe69a 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h -@@ -241,7 +241,7 @@ class ByteSource { +@@ -242,7 +242,7 @@ class ByteSource { // Creates a v8::BackingStore that takes over responsibility for // any allocated data. The ByteSource will be reset with size = 0 // after being called. @@ -189,7 +189,7 @@ index 3465454e4de4a78912b81e7eca0de395fbe89911..c8ae863460107c69dd77d67c76c11843 Local ret; if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {}; diff --git a/src/node_i18n.cc b/src/node_i18n.cc -index 0bcf10a0b35accb8d6d5fe9891d4f52b27d40346..606c2021242e6967ea4195af3e2493a7d5745dae 100644 +index ea7810e41e2667713a896250dc1b904b0a7cf198..865b3128c1edfe7074769f25a0b87878ca094f31 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -104,7 +104,7 @@ namespace { @@ -253,7 +253,7 @@ index 382df89a2312f76b5293412a8d51969ae5d9fa9c..1c90da9bbcb9547ab36de4d01088c03f // Delegate to V8's allocator for compatibility with the V8 memory cage. diff --git a/src/node_serdes.cc b/src/node_serdes.cc -index 7a70997bc024efa4f3ff4cabe30d5e88dcc7bc78..6552af3ed0acede41c1b16ef77eb359dc54f088a 100644 +index c55a2e28066147ae5ca5def10ec76ccc03c634b4..c54183c72944989219b6437c9e571a3f7f3f8dd5 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -29,6 +29,26 @@ using v8::ValueSerializer; diff --git a/patches/node/test_formally_mark_some_tests_as_flaky.patch b/patches/node/test_formally_mark_some_tests_as_flaky.patch index 974c8eeeb6c27..5dd4584847450 100644 --- a/patches/node/test_formally_mark_some_tests_as_flaky.patch +++ b/patches/node/test_formally_mark_some_tests_as_flaky.patch @@ -7,7 +7,7 @@ Instead of disabling the tests, flag them as flaky so they still run but don't cause CI failures on flakes. diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status -index fd42444c7b216a4a1fa026efc1bbc1b5df8c7394..26f78764842aaaa781a9409dda2a7d3265351178 100644 +index cc99efd7a743d683d5210ad83e258560c28cbd16..b2f0f7fb49665f0dc924cdd3e344c2579d617fbf 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -5,6 +5,16 @@ prefix parallel diff --git a/patches/node/test_make_eval_snapshot_tests_more_flexible.patch b/patches/node/test_make_eval_snapshot_tests_more_flexible.patch deleted file mode 100644 index 8d930ce9360d2..0000000000000 --- a/patches/node/test_make_eval_snapshot_tests_more_flexible.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Wed, 12 Feb 2025 21:01:13 +0100 -Subject: test: make eval snapshot tests more flexible - -Upstreamed in X - -diff --git a/test/fixtures/eval/eval_messages.snapshot b/test/fixtures/eval/eval_messages.snapshot -index f6fc803e0e3ec3f6a0c7cd056f42ac860012c907..998a06584b3bf3648e6703774aeb31c07bdb5d0e 100644 ---- a/test/fixtures/eval/eval_messages.snapshot -+++ b/test/fixtures/eval/eval_messages.snapshot -@@ -35,7 +35,7 @@ Node.js * - var ______________________________________________; throw 10 - ^ - 10 --(Use `node --trace-uncaught ...` to show where the exception was thrown) -+(Use `* --trace-uncaught ...` to show where the exception was thrown) - - Node.js * - -@@ -43,7 +43,7 @@ Node.js * - var ______________________________________________; throw 10 - ^ - 10 --(Use `node --trace-uncaught ...` to show where the exception was thrown) -+(Use `* --trace-uncaught ...` to show where the exception was thrown) - - Node.js * - done -diff --git a/test/fixtures/eval/stdin_messages.snapshot b/test/fixtures/eval/stdin_messages.snapshot -index 66bd506f758ca93906f850a9c773b617745eb834..0382a6ae3ccd792523cc19847bbdeef4d53e1579 100644 ---- a/test/fixtures/eval/stdin_messages.snapshot -+++ b/test/fixtures/eval/stdin_messages.snapshot -@@ -40,7 +40,7 @@ Node.js * - let ______________________________________________; throw 10 - ^ - 10 --(Use `node --trace-uncaught ...` to show where the exception was thrown) -+(Use `* --trace-uncaught ...` to show where the exception was thrown) - - Node.js * - -@@ -48,7 +48,7 @@ Node.js * - let ______________________________________________; throw 10 - ^ - 10 --(Use `node --trace-uncaught ...` to show where the exception was thrown) -+(Use `* --trace-uncaught ...` to show where the exception was thrown) - - Node.js * - done -diff --git a/test/parallel/test-node-output-eval.mjs b/test/parallel/test-node-output-eval.mjs -index d8c52176b1c3c3a0664d7f6b6750da03aa960587..8a3cc59574206769e4c80a04f05b03586f511ac2 100644 ---- a/test/parallel/test-node-output-eval.mjs -+++ b/test/parallel/test-node-output-eval.mjs -@@ -1,6 +1,7 @@ - import '../common/index.mjs'; - import * as fixtures from '../common/fixtures.mjs'; - import * as snapshot from '../common/assertSnapshot.js'; -+import { basename } from 'node:path'; - import { describe, it } from 'node:test'; - - describe('eval output', { concurrency: true }, () => { -@@ -16,6 +17,7 @@ describe('eval output', { concurrency: true }, () => { - snapshot.replaceNodeVersion, - removeStackTraces, - filterEmptyLines, -+ generalizeProcessName, - ); - - function removeStackTraces(output) { -@@ -26,6 +28,11 @@ describe('eval output', { concurrency: true }, () => { - return output.replaceAll(/^\s*$/gm, ''); - } - -+ function generalizeProcessName(output) { -+ const baseName = basename(process.argv0 || 'node', '.exe'); -+ return output.replaceAll(`${baseName} --`, '* --'); -+ } -+ - const tests = [ - { name: 'eval/eval_messages.js' }, - { name: 'eval/stdin_messages.js' }, diff --git a/patches/node/zlib_fix_pointer_alignment.patch b/patches/node/zlib_fix_pointer_alignment.patch index 47c51a49e62ab..418e74d53ccdd 100644 --- a/patches/node/zlib_fix_pointer_alignment.patch +++ b/patches/node/zlib_fix_pointer_alignment.patch @@ -16,10 +16,10 @@ a bus error killing node. see https://github.com/google/brotli/issues/1159 diff --git a/src/node_zlib.cc b/src/node_zlib.cc -index 90307cd4984ae5aa55386f2980ad9cd540322dfd..6f12b5034d1a98da50c064cf2cfdf12fc88137eb 100644 +index 0b7c47b326c7c5480086228b3d40d54c260ef16a..7e6b38ecd1aa360012c0d73e94412530a48cb8c3 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc -@@ -493,7 +493,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { +@@ -608,7 +608,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { } static void* AllocForBrotli(void* data, size_t size) { @@ -29,7 +29,7 @@ index 90307cd4984ae5aa55386f2980ad9cd540322dfd..6f12b5034d1a98da50c064cf2cfdf12f CompressionStream* ctx = static_cast(data); char* memory = UncheckedMalloc(size); if (memory == nullptr) [[unlikely]] { -@@ -502,7 +503,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { +@@ -617,7 +618,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { *reinterpret_cast(memory) = size; ctx->unreported_allocations_.fetch_add(size, std::memory_order_relaxed); @@ -38,7 +38,7 @@ index 90307cd4984ae5aa55386f2980ad9cd540322dfd..6f12b5034d1a98da50c064cf2cfdf12f } static void FreeForZlib(void* data, void* pointer) { -@@ -510,7 +511,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { +@@ -625,7 +626,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { return; } CompressionStream* ctx = static_cast(data); diff --git a/script/node-disabled-tests.json b/script/node-disabled-tests.json index 97852d1ca1021..5c23b69386d60 100644 --- a/script/node-disabled-tests.json +++ b/script/node-disabled-tests.json @@ -31,6 +31,7 @@ "parallel/test-http2-https-fallback", "parallel/test-http2-server-unknown-protocol", "parallel/test-https-agent-session-reuse", + "parallel/test-https-client-renegotiation-limit", "parallel/test-https-options-boolean-check", "parallel/test-icu-env", "parallel/test-icu-minimum-version", From 7240a141265dd3d7f65360ac137fb319f002de22 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 07:44:48 -0500 Subject: [PATCH 197/339] refactor: remove some `NativeWindow` public API (36-x-y) (#46944) refactor: remove some `NativeWindow` public API (#46919) * refactor: make NativeWindow::titlebar_overlay_height_ private * refactor: make NativeWindow::set_has_frame() protected * refactor: remove NativeWindow::background_material() It's only used once, in NativeWindow, so use |background_material_| directly. * refactor: remove NativeWindow::vibrancy() It's only used once, in a NativeWindow method, so use |vibrancy_| directly. * refactor: unfriend api::BrowserView It was added in Oct 2022 by 23d4a25 for access to protected NativeWindow methods add_inspectable_view() and remove_inspectable_view(). That dependency was removed in Nov 2022 by 184ac2b, so BrowserView doesn't need access to NativeWindow's private fields & methods anymore. * refactor: make NativeWindow::ContentBoundsToWindowBounds() protected refactor: make NativeWindow::WindowBoundsToContentBounds() protected Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 7 ++----- shell/browser/native_window.h | 27 +++++++++++---------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 64ed9083e62d5..658c11950b4cd 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -803,17 +803,14 @@ bool NativeWindow::IsTranslucent() const { #if BUILDFLAG(IS_MAC) // Windows with vibrancy set are translucent - if (!vibrancy().empty()) { + if (!vibrancy_.empty()) return true; - } #endif #if BUILDFLAG(IS_WIN) // Windows with certain background materials may be translucent - const std::string& bg_material = background_material(); - if (!bg_material.empty() && bg_material != "none") { + if (!background_material_.empty() && background_material_ != "none") return true; - } #endif return false; diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index aecd0a9178147..a88d17a6d8dd4 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -229,12 +229,8 @@ class NativeWindow : public base::SupportsUserData, virtual void SetAutoHideCursor(bool auto_hide) {} // Vibrancy API - const std::string& vibrancy() const { return vibrancy_; } virtual void SetVibrancy(const std::string& type, int duration); - const std::string& background_material() const { - return background_material_; - } virtual void SetBackgroundMaterial(const std::string& type); // Traffic Light API @@ -290,12 +286,6 @@ class NativeWindow : public base::SupportsUserData, virtual void SetGTKDarkThemeEnabled(bool use_dark_theme) {} - // Converts between content bounds and window bounds. - virtual gfx::Rect ContentBoundsToWindowBounds( - const gfx::Rect& bounds) const = 0; - virtual gfx::Rect WindowBoundsToContentBounds( - const gfx::Rect& bounds) const = 0; - base::WeakPtr GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -406,7 +396,6 @@ class NativeWindow : public base::SupportsUserData, } bool has_frame() const { return has_frame_; } - void set_has_frame(bool has_frame) { has_frame_ = has_frame; } bool has_client_frame() const { return has_client_frame_; } bool transparent() const { return transparent_; } @@ -439,10 +428,16 @@ class NativeWindow : public base::SupportsUserData, void UpdateBackgroundThrottlingState(); protected: - friend class api::BrowserView; + constexpr void set_has_frame(const bool val) { has_frame_ = val; } NativeWindow(const gin_helper::Dictionary& options, NativeWindow* parent); + // Converts between content bounds and window bounds. + virtual gfx::Rect ContentBoundsToWindowBounds( + const gfx::Rect& bounds) const = 0; + virtual gfx::Rect WindowBoundsToContentBounds( + const gfx::Rect& bounds) const = 0; + // views::WidgetDelegate: views::Widget* GetWidget() override; const views::Widget* GetWidget() const override; @@ -456,10 +451,6 @@ class NativeWindow : public base::SupportsUserData, // The boolean parsing of the "titleBarOverlay" option bool titlebar_overlay_ = false; - // The custom height parsed from the "height" option in a Object - // "titleBarOverlay" - int titlebar_overlay_height_ = 0; - // The "titleBarStyle" option. TitleBarStyle title_bar_style_ = TitleBarStyle::kNormal; @@ -485,6 +476,10 @@ class NativeWindow : public base::SupportsUserData, // The content view, weak ref. raw_ptr content_view_ = nullptr; + // The custom height parsed from the "height" option in a Object + // "titleBarOverlay" + int titlebar_overlay_height_ = 0; + // Whether window has standard frame. bool has_frame_ = true; From 13fe994d0dc88356f3339ef4d78a2826a08d7da2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 16:12:09 +0200 Subject: [PATCH 198/339] fix: allowed dialog file types with one filter (#46945) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/file_dialog_mac.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index 99c46738256cc..881054f0445d6 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -131,10 +131,6 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { [file_types_list addObject:content_types_set]; } - // Don't add file format picker. - if ([file_types_list count] <= 1) - return; - NSArray* content_types = [file_types_list objectAtIndex:0]; __block BOOL allowAllFiles = NO; @@ -148,6 +144,10 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { [dialog setAllowedContentTypes:allowAllFiles ? @[] : content_types]; + // Don't add file format picker. + if ([file_types_list count] <= 1) + return; + // Add file format picker. ElectronAccessoryView* accessoryView = [[ElectronAccessoryView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; From fdc322c93f63de1f556509de9588775a82fb8a9f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 14:11:37 -0500 Subject: [PATCH 199/339] fix: crash on macOS dialog after `window-all-closed` (#46951) fix: crash on dialog after window-all-closed Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/cocoa/electron_ns_window.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/ui/cocoa/electron_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm index 6555f47f5bc3c..fabd68cd7f1f1 100644 --- a/shell/browser/ui/cocoa/electron_ns_window.mm +++ b/shell/browser/ui/cocoa/electron_ns_window.mm @@ -221,7 +221,7 @@ - (void)rotateWithEvent:(NSEvent*)event { } - (NSRect)contentRectForFrameRect:(NSRect)frameRect { - if (shell_->has_frame()) + if (shell_ && shell_->has_frame()) return [super contentRectForFrameRect:frameRect]; else return frameRect; From 695448e14249e73b501b1a0f42d68f8c87d101f5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 14:37:34 -0500 Subject: [PATCH 200/339] refactor: pass `gfx::ResizeEdge` by value (#46962) refactor: pass gfx::ResizeEdge by value It is an enum class, so no reason to pass by reference Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_base_window.cc | 2 +- shell/browser/api/electron_api_base_window.h | 2 +- shell/browser/native_window.cc | 2 +- shell/browser/native_window.h | 2 +- shell/browser/native_window_observer.h | 2 +- shell/common/gin_converters/gfx_converter.cc | 2 +- shell/common/gin_converters/gfx_converter.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 506350baa80e1..4140db9e0b630 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -254,7 +254,7 @@ void BaseWindow::OnWindowRestore() { } void BaseWindow::OnWindowWillResize(const gfx::Rect& new_bounds, - const gfx::ResizeEdge& edge, + const gfx::ResizeEdge edge, bool* prevent_default) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 68619364a8fbf..0a44c592781eb 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -71,7 +71,7 @@ class BaseWindow : public gin_helper::TrackableObject, void OnWindowMinimize() override; void OnWindowRestore() override; void OnWindowWillResize(const gfx::Rect& new_bounds, - const gfx::ResizeEdge& edge, + gfx::ResizeEdge edge, bool* prevent_default) override; void OnWindowResize() override; void OnWindowResized() override; diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 658c11950b4cd..9fa01c2fdc098 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -587,7 +587,7 @@ void NativeWindow::NotifyWindowRestore() { } void NativeWindow::NotifyWindowWillResize(const gfx::Rect& new_bounds, - const gfx::ResizeEdge& edge, + const gfx::ResizeEdge edge, bool* prevent_default) { observers_.Notify(&NativeWindowObserver::OnWindowWillResize, new_bounds, edge, prevent_default); diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index a88d17a6d8dd4..8c4618073a028 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -317,7 +317,7 @@ class NativeWindow : public base::SupportsUserData, void NotifyWindowRestore(); void NotifyWindowMove(); void NotifyWindowWillResize(const gfx::Rect& new_bounds, - const gfx::ResizeEdge& edge, + gfx::ResizeEdge edge, bool* prevent_default); void NotifyWindowResize(); void NotifyWindowResized(); diff --git a/shell/browser/native_window_observer.h b/shell/browser/native_window_observer.h index 22efa174aedd4..1adf24d474043 100644 --- a/shell/browser/native_window_observer.h +++ b/shell/browser/native_window_observer.h @@ -78,7 +78,7 @@ class NativeWindowObserver : public base::CheckedObserver { virtual void OnWindowMinimize() {} virtual void OnWindowRestore() {} virtual void OnWindowWillResize(const gfx::Rect& new_bounds, - const gfx::ResizeEdge& edge, + gfx::ResizeEdge edge, bool* prevent_default) {} virtual void OnWindowResize() {} virtual void OnWindowResized() {} diff --git a/shell/common/gin_converters/gfx_converter.cc b/shell/common/gin_converters/gfx_converter.cc index c6c75da3f1379..c54ec7062eabd 100644 --- a/shell/common/gin_converters/gfx_converter.cc +++ b/shell/common/gin_converters/gfx_converter.cc @@ -197,7 +197,7 @@ v8::Local Converter::ToV8( v8::Local Converter::ToV8( v8::Isolate* isolate, - const gfx::ResizeEdge& val) { + const gfx::ResizeEdge val) { switch (val) { case gfx::ResizeEdge::kRight: return StringToV8(isolate, "right"); diff --git a/shell/common/gin_converters/gfx_converter.h b/shell/common/gin_converters/gfx_converter.h index 43903b464ed21..700ce8134c3f7 100644 --- a/shell/common/gin_converters/gfx_converter.h +++ b/shell/common/gin_converters/gfx_converter.h @@ -77,7 +77,7 @@ struct Converter { template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, - const gfx::ResizeEdge& val); + const gfx::ResizeEdge val); }; template <> From 7779b6a4ad3bc8b171d1b1d647243c2832eb8234 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 15:56:15 -0500 Subject: [PATCH 201/339] test: test menu rendering accelerators (#46966) * test: test menu rendering accelerators Co-authored-by: Shelley Vohr * Update spec/api-menu-spec.ts Co-authored-by: John Kleinschmidt Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- spec/api-global-shortcut-spec.ts | 38 +---------------------------- spec/api-menu-spec.ts | 30 +++++++++++++---------- spec/lib/accelerator-helpers.ts | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 spec/lib/accelerator-helpers.ts diff --git a/spec/api-global-shortcut-spec.ts b/spec/api-global-shortcut-spec.ts index a88d08647347b..cf6f9fc7d752b 100644 --- a/spec/api-global-shortcut-spec.ts +++ b/spec/api-global-shortcut-spec.ts @@ -2,51 +2,15 @@ import { globalShortcut } from 'electron/main'; import { expect } from 'chai'; +import { singleModifierCombinations, doubleModifierCombinations } from './lib/accelerator-helpers'; import { ifdescribe } from './lib/spec-helpers'; -const modifiers = [ - 'CmdOrCtrl', - 'Alt', - process.platform === 'darwin' ? 'Option' : null, - 'AltGr', - 'Shift', - 'Super', - 'Meta' -].filter(Boolean); - -const keyCodes = [ - ...Array.from({ length: 10 }, (_, i) => `${i}`), // 0 to 9 - ...Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i)), // A to Z - ...Array.from({ length: 24 }, (_, i) => `F${i + 1}`), // F1 to F24 - ')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ':', ';', ':', '+', '=', - '<', ',', '_', '-', '>', '.', '?', '/', '~', '`', '{', ']', '[', '|', '\\', - '}', '"', 'Plus', 'Space', 'Tab', 'Capslock', 'Numlock', 'Scrolllock', - 'Backspace', 'Delete', 'Insert', 'Return', 'Enter', 'Up', 'Down', 'Left', - 'Right', 'Home', 'End', 'PageUp', 'PageDown', 'Escape', 'Esc', 'PrintScreen', - 'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9', - 'numdec', 'numadd', 'numsub', 'nummult', 'numdiv' -]; - ifdescribe(process.platform !== 'win32')('globalShortcut module', () => { beforeEach(() => { globalShortcut.unregisterAll(); }); it('can register and unregister single accelerators', () => { - const singleModifierCombinations = modifiers.flatMap( - mod => keyCodes.map(key => { - return key === '+' ? `${mod}+Plus` : `${mod}+${key}`; - }) - ); - - const doubleModifierCombinations = modifiers.flatMap( - (mod1, i) => modifiers.slice(i + 1).flatMap( - mod2 => keyCodes.map(key => { - return key === '+' ? `${mod1}+${mod2}+Plus` : `${mod1}+${mod2}+${key}`; - }) - ) - ); - const combinations = [...singleModifierCombinations, ...doubleModifierCombinations]; combinations.forEach((accelerator) => { diff --git a/spec/api-menu-spec.ts b/spec/api-menu-spec.ts index e3343e8cf1077..9e8143e94ac82 100644 --- a/spec/api-menu-spec.ts +++ b/spec/api-menu-spec.ts @@ -7,6 +7,7 @@ import { once } from 'node:events'; import * as path from 'node:path'; import { setTimeout } from 'node:timers/promises'; +import { singleModifierCombinations } from './lib/accelerator-helpers'; import { ifit } from './lib/spec-helpers'; import { closeWindow } from './lib/window-helpers'; import { sortMenuItems } from '../lib/browser/api/menu-utils'; @@ -927,19 +928,22 @@ describe('Menu module', function () { w.show(); }); - it('does not crash when rendering menu item with Super or meta accelerator', async () => { - const menu = Menu.buildFromTemplate([{ - label: 'Test Super', - accelerator: 'Super+Ctrl+T' - }, { - label: 'Test Meta', - accelerator: 'Meta+Ctrl+T' - }]); - const menuWillClose = once(menu, 'menu-will-close'); - menu.popup({ window: w }); - menu.closePopup(); - await menuWillClose; - }); + const chunkSize = 10; + let chunkCount = 0; + const totalChunks = Math.ceil(singleModifierCombinations.length / chunkSize); + for (let i = 0; i < singleModifierCombinations.length; i += chunkSize) { + const chunk = singleModifierCombinations.slice(i, i + chunkSize); + it(`does not crash when rendering menu item with single accelerator combinations ${++chunkCount}/${totalChunks}`, async () => { + const menu = Menu.buildFromTemplate([ + ...chunk.map(combination => ({ + label: `Test ${combination}`, + accelerator: combination + })) + ]); + menu.popup({ window: w }); + menu.closePopup(); + }); + } }); describe('Menu.setApplicationMenu', () => { diff --git a/spec/lib/accelerator-helpers.ts b/spec/lib/accelerator-helpers.ts new file mode 100644 index 0000000000000..0ed3dd807b853 --- /dev/null +++ b/spec/lib/accelerator-helpers.ts @@ -0,0 +1,41 @@ +/** + * @fileoverview A set of helper functions to make it easier to work + * with accelerators across tests. + */ + +const modifiers = [ + 'CmdOrCtrl', + 'Alt', + process.platform === 'darwin' ? 'Option' : null, + 'AltGr', + 'Shift', + 'Super', + 'Meta' +].filter(Boolean); + +const keyCodes = [ + ...Array.from({ length: 10 }, (_, i) => `${i}`), // 0 to 9 + ...Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i)), // A to Z + ...Array.from({ length: 24 }, (_, i) => `F${i + 1}`), // F1 to F24 + ')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ':', ';', ':', '+', '=', + '<', ',', '_', '-', '>', '.', '?', '/', '~', '`', '{', ']', '[', '|', '\\', + '}', '"', 'Plus', 'Space', 'Tab', 'Capslock', 'Numlock', 'Scrolllock', + 'Backspace', 'Delete', 'Insert', 'Return', 'Enter', 'Up', 'Down', 'Left', + 'Right', 'Home', 'End', 'PageUp', 'PageDown', 'Escape', 'Esc', 'PrintScreen', + 'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9', + 'numdec', 'numadd', 'numsub', 'nummult', 'numdiv' +]; + +export const singleModifierCombinations = modifiers.flatMap( + mod => keyCodes.map(key => { + return key === '+' ? `${mod}+Plus` : `${mod}+${key}`; + }) +); + +export const doubleModifierCombinations = modifiers.flatMap( + (mod1, i) => modifiers.slice(i + 1).flatMap( + mod2 => keyCodes.map(key => { + return key === '+' ? `${mod1}+${mod2}+Plus` : `${mod1}+${mod2}+${key}`; + }) + ) +); From 366daf192a2c928d4c8bcb6c9505882e4c1f5020 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 18:50:12 -0500 Subject: [PATCH 202/339] refactor: add `gin_helper::Dictionary::ValueOrDefault()` (#46968) * feat: add gin_helper::Dictionary::ValueOrDefault() A convenience function for using a default value if the specified key isn't present in the dictionary. Co-authored-by: Charles Kerr * refactor: use ValueOrDefault() in native_window.cc Co-authored-by: Charles Kerr * refactor: use ValueOrDefault() in native_window_mac.mm Co-authored-by: Charles Kerr * refactor: use ValueOrDefault() in native_window_views.cc Co-authored-by: Charles Kerr * refactor: use ValueOrDefault() in electron_api_native_image.cc Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 16 +++--- shell/browser/native_window_mac.mm | 49 +++++++++---------- shell/browser/native_window_views.cc | 16 +++--- shell/common/api/electron_api_native_image.cc | 12 ++--- shell/common/gin_helper/dictionary.h | 9 ++++ 5 files changed, 48 insertions(+), 54 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 9fa01c2fdc098..8460f07e69430 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -164,17 +164,17 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { Center(); } - bool use_content_size = false; - options.Get(options::kUseContentSize, &use_content_size); + const bool use_content_size = + options.ValueOrDefault(options::kUseContentSize, false); // On Linux and Window we may already have maximum size defined. extensions::SizeConstraints size_constraints( use_content_size ? GetContentSizeConstraints() : GetSizeConstraints()); - int min_width = size_constraints.GetMinimumSize().width(); - int min_height = size_constraints.GetMinimumSize().height(); - options.Get(options::kMinWidth, &min_width); - options.Get(options::kMinHeight, &min_height); + const int min_width = options.ValueOrDefault( + options::kMinWidth, size_constraints.GetMinimumSize().width()); + const int min_height = options.ValueOrDefault( + options::kMinHeight, size_constraints.GetMinimumSize().height()); size_constraints.set_minimum_size(gfx::Size(min_width, min_height)); gfx::Size max_size = size_constraints.GetMaximumSize(); @@ -274,9 +274,7 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { SetTitle(title); // Then show it. - bool show = true; - options.Get(options::kShow, &show); - if (show) + if (options.ValueOrDefault(options::kShow, true)) Show(); } diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 3effcda4e1f38..51c1494ab8ff5 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -120,42 +120,37 @@ static bool FromV8(v8::Isolate* isolate, ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); display::Screen::GetScreen()->AddObserver(this); - int width = 800, height = 600; - options.Get(options::kWidth, &width); - options.Get(options::kHeight, &height); + const int width = options.ValueOrDefault(options::kWidth, 800); + const int height = options.ValueOrDefault(options::kHeight, 600); NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame]; gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2), round((NSHeight(main_screen_rect) - height) / 2), width, height); - bool resizable = true; - options.Get(options::kResizable, &resizable); + const bool resizable = options.ValueOrDefault(options::kResizable, true); options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); options.Get(options::kSimpleFullscreen, &always_simple_fullscreen_); options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_); options.Get(options::kVisualEffectState, &visual_effect_state_); - bool minimizable = true; - options.Get(options::kMinimizable, &minimizable); + const bool minimizable = options.ValueOrDefault(options::kMinimizable, true); - bool maximizable = true; - options.Get(options::kMaximizable, &maximizable); + const bool maximizable = options.ValueOrDefault(options::kMaximizable, true); - bool closable = true; - options.Get(options::kClosable, &closable); + const bool closable = options.ValueOrDefault(options::kClosable, true); - std::string tabbingIdentifier; - options.Get(options::kTabbingIdentifier, &tabbingIdentifier); + const std::string tabbingIdentifier = + options.ValueOrDefault(options::kTabbingIdentifier, std::string{}); - std::string windowType; - options.Get(options::kType, &windowType); + const std::string windowType = + options.ValueOrDefault(options::kType, std::string{}); - bool hiddenInMissionControl = false; - options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl); + const bool hiddenInMissionControl = + options.ValueOrDefault(options::kHiddenInMissionControl, false); - bool paint_when_initially_hidden = true; - options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden); + const bool paint_when_initially_hidden = + options.ValueOrDefault(options::kPaintWhenInitiallyHidden, true); // The window without titlebar is treated the same with frameless window. if (title_bar_style_ != TitleBarStyle::kNormal) @@ -165,8 +160,8 @@ static bool FromV8(v8::Isolate* isolate, // The NSWindowStyleMaskFullSizeContentView style removes rounded corners // for frameless window. - bool rounded_corner = true; - options.Get(options::kRoundedCorners, &rounded_corner); + const bool rounded_corner = + options.ValueOrDefault(options::kRoundedCorners, true); if (!rounded_corner && !has_frame()) styleMask = NSWindowStyleMaskBorderless; @@ -288,19 +283,19 @@ static bool FromV8(v8::Isolate* isolate, } // Resize to content bounds. - bool use_content_size = false; - options.Get(options::kUseContentSize, &use_content_size); + const bool use_content_size = + options.ValueOrDefault(options::kUseContentSize, false); if (!has_frame() || use_content_size) SetContentSize(gfx::Size(width, height)); // Enable the NSView to accept first mouse event. - bool acceptsFirstMouse = false; - options.Get(options::kAcceptFirstMouse, &acceptsFirstMouse); + const bool acceptsFirstMouse = + options.ValueOrDefault(options::kAcceptFirstMouse, false); [window_ setAcceptsFirstMouse:acceptsFirstMouse]; // Disable auto-hiding cursor. - bool disableAutoHideCursor = false; - options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor); + const bool disableAutoHideCursor = + options.ValueOrDefault(options::kDisableAutoHideCursor, false); [window_ setDisableAutoHideCursor:disableAutoHideCursor]; SetHiddenInMissionControl(hiddenInMissionControl); diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 86dcf5be954a2..4f27873ec0934 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -262,10 +262,9 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, SetContentSizeConstraints(extensions::SizeConstraints( gfx::Size(), gfx::Size(INT_MAX / 10, INT_MAX / 10))); - int width = 800, height = 600; - options.Get(options::kWidth, &width); - options.Get(options::kHeight, &height); - gfx::Rect bounds(0, 0, width, height); + const int width = options.ValueOrDefault(options::kWidth, 800); + const int height = options.ValueOrDefault(options::kHeight, 600); + const gfx::Rect bounds{0, 0, width, height}; widget_size_ = bounds.size(); widget()->AddObserver(this); @@ -315,18 +314,15 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, widget()->SetNativeWindowProperty(kNativeWindowKey.c_str(), this); SetCanResize(resizable_); - bool fullscreen = false; - options.Get(options::kFullscreen, &fullscreen); + const bool fullscreen = options.ValueOrDefault(options::kFullscreen, false); std::string window_type; options.Get(options::kType, &window_type); #if BUILDFLAG(IS_LINUX) // Set _GTK_THEME_VARIANT to dark if we have "dark-theme" option set. - bool use_dark_theme = false; - if (options.Get(options::kDarkTheme, &use_dark_theme) && use_dark_theme) { - SetGTKDarkThemeEnabled(use_dark_theme); - } + if (options.ValueOrDefault(options::kDarkTheme, false)) + SetGTKDarkThemeEnabled(true); if (parent) SetParentWindow(parent); diff --git a/shell/common/api/electron_api_native_image.cc b/shell/common/api/electron_api_native_image.cc index ffe3267b2999a..67970d8faea24 100644 --- a/shell/common/api/electron_api_native_image.cc +++ b/shell/common/api/electron_api_native_image.cc @@ -382,12 +382,9 @@ gin::Handle NativeImage::Crop(v8::Isolate* isolate, } void NativeImage::AddRepresentation(const gin_helper::Dictionary& options) { - int width = 0; - int height = 0; - float scale_factor = 1.0f; - options.Get("width", &width); - options.Get("height", &height); - options.Get("scaleFactor", &scale_factor); + const int width = options.ValueOrDefault("width", 0); + const int height = options.ValueOrDefault("height", 0); + const float scale_factor = options.ValueOrDefault("scaleFactor", 1.0F); bool skia_rep_added = false; gfx::ImageSkia image_skia = image_.AsImageSkia(); @@ -515,8 +512,7 @@ gin::Handle NativeImage::CreateFromBitmap( bitmap.allocN32Pixels(width, height, false); bitmap.writePixels({info, buffer_data.data(), bitmap.rowBytes()}); - float scale_factor = 1.0F; - options.Get("scaleFactor", &scale_factor); + const float scale_factor = options.ValueOrDefault("scaleFactor", 1.0F); gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFromBitmap(bitmap, scale_factor); diff --git a/shell/common/gin_helper/dictionary.h b/shell/common/gin_helper/dictionary.h index 589c24d1f7346..85d7c7786dc5a 100644 --- a/shell/common/gin_helper/dictionary.h +++ b/shell/common/gin_helper/dictionary.h @@ -67,6 +67,15 @@ class Dictionary : public gin::Dictionary { return result.FromMaybe(false); } + // Convenience function for using a default value if the + // specified key isn't present in the dictionary. + template + T ValueOrDefault(const std::string_view key, T default_value) const { + if (auto value = T{}; Get(key, &value)) + return value; + return default_value; + } + // Like normal Get but put result in an std::optional. template bool GetOptional(const std::string_view key, std::optional* out) const { From b822478057501b73970b32abb14ad7265f0fd139 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 09:36:13 +0200 Subject: [PATCH 203/339] fix: printing when no `mediaSize` specified (#46972) fix: printing when no mediaSize specified Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_web_contents.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 76eee04dd63b7..632ef385cf7c0 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -3133,11 +3133,21 @@ void WebContents::Print(gin::Arguments* args) { options.Get("duplexMode", &duplex_mode); settings.Set(printing::kSettingDuplexMode, static_cast(duplex_mode)); - // We've already done necessary parameter sanitization at the - // JS level, so we can simply pass this through. - base::Value media_size(base::Value::Type::DICT); - if (options.Get("mediaSize", &media_size)) + base::Value::Dict media_size; + if (options.Get("mediaSize", &media_size)) { settings.Set(printing::kSettingMediaSize, std::move(media_size)); + } else { + // Default to A4 paper size (210mm x 297mm) + settings.Set(printing::kSettingMediaSize, + base::Value::Dict() + .Set(printing::kSettingMediaSizeHeightMicrons, 297000) + .Set(printing::kSettingMediaSizeWidthMicrons, 210000) + .Set(printing::kSettingsImageableAreaLeftMicrons, 0) + .Set(printing::kSettingsImageableAreaTopMicrons, 297000) + .Set(printing::kSettingsImageableAreaRightMicrons, 210000) + .Set(printing::kSettingsImageableAreaBottomMicrons, 0) + .Set(printing::kSettingMediaSizeIsDefault, true)); + } // Set custom dots per inch (dpi) gin_helper::Dictionary dpi_settings; From 3c9e7f97a161e86de84287e581b6f9e2b02359c9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:37:23 -0500 Subject: [PATCH 204/339] test: enable `hasShadow` tests on Linux (#47000) refactor: enable hasShadow tests on Linux Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/api-browser-window-spec.ts | 102 ++++++++++++++++---------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index bf4e2988c261d..36004ef59ca7d 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -5428,6 +5428,57 @@ describe('BrowserWindow module', () => { }); }); }); + + describe('hasShadow state', () => { + describe('with properties', () => { + it('returns a boolean on all platforms', () => { + const w = new BrowserWindow({ show: false }); + expect(w.shadow).to.be.a('boolean'); + }); + + // On Windows there's no shadow by default & it can't be changed dynamically. + it('can be changed with hasShadow option', () => { + const hasShadow = process.platform !== 'darwin'; + const w = new BrowserWindow({ show: false, hasShadow }); + expect(w.shadow).to.equal(hasShadow); + }); + + it('can be changed with setHasShadow method', () => { + const w = new BrowserWindow({ show: false }); + w.shadow = false; + expect(w.shadow).to.be.false('hasShadow'); + w.shadow = true; + expect(w.shadow).to.be.true('hasShadow'); + w.shadow = false; + expect(w.shadow).to.be.false('hasShadow'); + }); + }); + + describe('with functions', () => { + it('returns a boolean on all platforms', () => { + const w = new BrowserWindow({ show: false }); + const hasShadow = w.hasShadow(); + expect(hasShadow).to.be.a('boolean'); + }); + + // On Windows there's no shadow by default & it can't be changed dynamically. + it('can be changed with hasShadow option', () => { + const hasShadow = process.platform !== 'darwin'; + const w = new BrowserWindow({ show: false, hasShadow }); + expect(w.hasShadow()).to.equal(hasShadow); + }); + + it('can be changed with setHasShadow method', () => { + const w = new BrowserWindow({ show: false }); + w.setHasShadow(false); + expect(w.hasShadow()).to.be.false('hasShadow'); + w.setHasShadow(true); + expect(w.hasShadow()).to.be.true('hasShadow'); + w.setHasShadow(false); + expect(w.hasShadow()).to.be.false('hasShadow'); + }); + }); + }); }); ifdescribe(process.platform !== 'linux')('window states (excluding Linux)', () => { @@ -6207,57 +6258,6 @@ describe('BrowserWindow module', () => { }); }); }); - - describe('hasShadow state', () => { - describe('with properties', () => { - it('returns a boolean on all platforms', () => { - const w = new BrowserWindow({ show: false }); - expect(w.shadow).to.be.a('boolean'); - }); - - // On Windows there's no shadow by default & it can't be changed dynamically. - it('can be changed with hasShadow option', () => { - const hasShadow = process.platform !== 'darwin'; - const w = new BrowserWindow({ show: false, hasShadow }); - expect(w.shadow).to.equal(hasShadow); - }); - - it('can be changed with setHasShadow method', () => { - const w = new BrowserWindow({ show: false }); - w.shadow = false; - expect(w.shadow).to.be.false('hasShadow'); - w.shadow = true; - expect(w.shadow).to.be.true('hasShadow'); - w.shadow = false; - expect(w.shadow).to.be.false('hasShadow'); - }); - }); - - describe('with functions', () => { - it('returns a boolean on all platforms', () => { - const w = new BrowserWindow({ show: false }); - const hasShadow = w.hasShadow(); - expect(hasShadow).to.be.a('boolean'); - }); - - // On Windows there's no shadow by default & it can't be changed dynamically. - it('can be changed with hasShadow option', () => { - const hasShadow = process.platform !== 'darwin'; - const w = new BrowserWindow({ show: false, hasShadow }); - expect(w.hasShadow()).to.equal(hasShadow); - }); - - it('can be changed with setHasShadow method', () => { - const w = new BrowserWindow({ show: false }); - w.setHasShadow(false); - expect(w.hasShadow()).to.be.false('hasShadow'); - w.setHasShadow(true); - expect(w.hasShadow()).to.be.true('hasShadow'); - w.setHasShadow(false); - expect(w.hasShadow()).to.be.false('hasShadow'); - }); - }); - }); }); describe('window.getMediaSourceId()', () => { From 12dd4d91bdad03de82a695eccff86e06efa4803f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:32:54 -0700 Subject: [PATCH 205/339] build: move release script to new hasher function (#46996) build: move to new hasher function Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond --- script/release/get-url-hash.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/release/get-url-hash.ts b/script/release/get-url-hash.ts index b4fc10b053b07..ebbf5bce54c64 100644 --- a/script/release/get-url-hash.ts +++ b/script/release/get-url-hash.ts @@ -2,12 +2,12 @@ import got from 'got'; import * as url from 'node:url'; -const HASHER_FUNCTION_HOST = 'electron-artifact-hasher.azurewebsites.net'; -const HASHER_FUNCTION_ROUTE = '/api/HashArtifact'; +const HASHER_FUNCTION_HOST = 'electron-hasher.azurewebsites.net'; +const HASHER_FUNCTION_ROUTE = '/api/hashRemoteAsset'; export async function getUrlHash (targetUrl: string, algorithm = 'sha256', attempts = 3) { const options = { - code: process.env.ELECTRON_ARTIFACT_HASHER_FUNCTION_KEY!, + code: process.env.ELECTRON_HASHER_FUNCTION_KEY!, targetUrl, algorithm }; From de7cf88f17282c3dade2f46a99731200ee069e76 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 15:34:05 -0500 Subject: [PATCH 206/339] refactor: reduce use of `NativeWidgetPrivate` (#47005) * refactor: do not use native_widget_private() in NativeWindowViews::SetContentProtection() refactor: do not use native_widget_private() in NativeWindowViews::IsContentProtected() Co-authored-by: Charles Kerr * refactor: do not use native_widget_private() in NativeWindowViews::Show() Co-authored-by: Charles Kerr * chore: remove native_widget_private #include from native_window_views_win Not needed since Feb 2025: 9199d5c6 Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 9 ++++----- shell/browser/native_window_views_win.cc | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 4f27873ec0934..9be657a501a03 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -513,8 +513,7 @@ bool NativeWindowViews::IsFocused() const { } void NativeWindowViews::Show() { - if (is_modal() && NativeWindow::parent() && - !widget()->native_widget_private()->IsVisible()) + if (is_modal() && NativeWindow::parent() && !widget()->IsVisible()) static_cast(parent())->IncrementChildModals(); widget()->native_widget_private()->Show(GetRestoredState(), gfx::Rect()); @@ -1321,15 +1320,15 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { #endif } -void NativeWindowViews::SetContentProtection(bool enable) { +void NativeWindowViews::SetContentProtection(const bool enable) { #if BUILDFLAG(IS_WIN) - widget()->native_widget_private()->SetAllowScreenshots(!enable); + widget()->SetAllowScreenshots(!enable); #endif } bool NativeWindowViews::IsContentProtected() const { #if BUILDFLAG(IS_WIN) - return !widget()->native_widget_private()->AreScreenshotsAllowed(); + return !widget()->AreScreenshotsAllowed(); #else // Not implemented on Linux return false; #endif diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 4b324bc1c3f6b..c5d72bc5bb325 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -19,7 +19,6 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/resize_utils.h" -#include "ui/views/widget/native_widget_private.h" // Must be included after other Windows headers. #include From be43a764402d529296e5ff0d7d9aaad2341dcb2a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 09:36:55 +0200 Subject: [PATCH 207/339] refactor: use `gin_helper::Dictionary::ValueOrDefault()` (#47015) refactor: use `gin_helper::Dictionary::ValueOrDefault()` (#46982) * refactor: use ValueOrDefault() in electron_api_web_contents.cc * refactor: use ValueOrDefault() in electron_api_url_loader.cc * refactor: use ValueOrDefault() in electron_download_manager_delegate.cc * refactor: use ValueOrDefault() in electron_touch_bar.mm * refactor: use ValueOrDefault() in electron_url_loader_factory.cc * refactor: use ValueOrDefault() in electron_browser_context.cc * refactor: use ValueOrDefault() in electron_touch_bar.mm * refactor: use ValueOrDefault() in blink_converter.cc * feat: add ValueOrDefault() to PersistentDictionary * empty commit * refactor: use ValueOrDefault() in blink_converter.cc * refactor: inline the rectangle base::Value::Dict * refactor: remove has_scroll temporary --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../browser/api/electron_api_web_contents.cc | 101 +++++++----------- shell/browser/electron_browser_context.cc | 6 +- .../electron_download_manager_delegate.cc | 3 +- .../net/electron_url_loader_factory.cc | 4 +- shell/browser/ui/cocoa/electron_touch_bar.mm | 34 ++---- shell/common/api/electron_api_url_loader.cc | 13 +-- .../common/gin_converters/blink_converter.cc | 20 ++-- .../common/gin_helper/persistent_dictionary.h | 9 ++ 8 files changed, 76 insertions(+), 114 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 632ef385cf7c0..324c69223d53d 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -822,8 +822,7 @@ WebContents::WebContents(v8::Isolate* isolate, // Whether to enable DevTools. options.Get("devTools", &enable_devtools_); - bool initially_shown = true; - options.Get(options::kShow, &initially_shown); + const bool initially_shown = options.ValueOrDefault(options::kShow, true); // Obtain the session. std::string partition; @@ -2332,9 +2331,7 @@ void WebContents::LoadURL(const GURL& url, params.load_type = content::NavigationController::LOAD_TYPE_DATA; } - bool reload_ignoring_cache = false; - if (options.Get("reloadIgnoringCache", &reload_ignoring_cache) && - reload_ignoring_cache) { + if (options.ValueOrDefault("reloadIgnoringCache", false)) { params.reload_type = content::ReloadType::BYPASSING_CACHE; } @@ -3011,13 +3008,10 @@ void WebContents::Print(gin::Arguments* args) { } // Set optional silent printing. - bool silent = false; - options.Get("silent", &silent); - settings.Set("silent", silent); + settings.Set("silent", options.ValueOrDefault("silent", false)); - bool print_background = false; - options.Get("printBackground", &print_background); - settings.Set(printing::kSettingShouldPrintBackgrounds, print_background); + settings.Set(printing::kSettingShouldPrintBackgrounds, + options.ValueOrDefault("printBackground", false)); // Set custom margin settings auto margins = gin_helper::Dictionary::CreateEmpty(args->isolate()); @@ -3028,20 +3022,16 @@ void WebContents::Print(gin::Arguments* args) { settings.Set(printing::kSettingMarginsType, static_cast(margin_type)); if (margin_type == printing::mojom::MarginType::kCustomMargins) { - base::Value::Dict custom_margins; - int top = 0; - margins.Get("top", &top); - custom_margins.Set(printing::kSettingMarginTop, top); - int bottom = 0; - margins.Get("bottom", &bottom); - custom_margins.Set(printing::kSettingMarginBottom, bottom); - int left = 0; - margins.Get("left", &left); - custom_margins.Set(printing::kSettingMarginLeft, left); - int right = 0; - margins.Get("right", &right); - custom_margins.Set(printing::kSettingMarginRight, right); - settings.Set(printing::kSettingMarginsCustom, std::move(custom_margins)); + settings.Set(printing::kSettingMarginsCustom, + base::Value::Dict{} + .Set(printing::kSettingMarginTop, + margins.ValueOrDefault("top", 0)) + .Set(printing::kSettingMarginBottom, + margins.ValueOrDefault("bottom", 0)) + .Set(printing::kSettingMarginLeft, + margins.ValueOrDefault("left", 0)) + .Set(printing::kSettingMarginRight, + margins.ValueOrDefault("right", 0))); } } else { settings.Set( @@ -3050,46 +3040,37 @@ void WebContents::Print(gin::Arguments* args) { } // Set whether to print color or greyscale - bool print_color = true; - options.Get("color", &print_color); - auto const color_model = print_color ? printing::mojom::ColorModel::kColor - : printing::mojom::ColorModel::kGray; - settings.Set(printing::kSettingColor, static_cast(color_model)); + settings.Set(printing::kSettingColor, + static_cast(options.ValueOrDefault("color", true) + ? printing::mojom::ColorModel::kColor + : printing::mojom::ColorModel::kGray)); // Is the orientation landscape or portrait. - bool landscape = false; - options.Get("landscape", &landscape); - settings.Set(printing::kSettingLandscape, landscape); + settings.Set(printing::kSettingLandscape, + options.ValueOrDefault("landscape", false)); // We set the default to the system's default printer and only update // if at the Chromium level if the user overrides. // Printer device name as opened by the OS. - std::u16string device_name; - options.Get("deviceName", &device_name); + const auto device_name = + options.ValueOrDefault("deviceName", std::u16string{}); - int scale_factor = 100; - options.Get("scaleFactor", &scale_factor); - settings.Set(printing::kSettingScaleFactor, scale_factor); + settings.Set(printing::kSettingScaleFactor, + options.ValueOrDefault("scaleFactor", 100)); - int pages_per_sheet = 1; - options.Get("pagesPerSheet", &pages_per_sheet); - settings.Set(printing::kSettingPagesPerSheet, pages_per_sheet); + settings.Set(printing::kSettingPagesPerSheet, + options.ValueOrDefault("pagesPerSheet", 1)); // True if the user wants to print with collate. - bool collate = true; - options.Get("collate", &collate); - settings.Set(printing::kSettingCollate, collate); + settings.Set(printing::kSettingCollate, + options.ValueOrDefault("collate", true)); // The number of individual copies to print - int copies = 1; - options.Get("copies", &copies); - settings.Set(printing::kSettingCopies, copies); + settings.Set(printing::kSettingCopies, options.ValueOrDefault("copies", 1)); // Strings to be printed as headers and footers if requested by the user. - std::string header; - options.Get("header", &header); - std::string footer; - options.Get("footer", &footer); + const auto header = options.ValueOrDefault("header", std::string{}); + const auto footer = options.ValueOrDefault("footer", std::string{}); if (!(header.empty() && footer.empty())) { settings.Set(printing::kSettingHeaderFooterEnabled, true); @@ -3128,9 +3109,8 @@ void WebContents::Print(gin::Arguments* args) { } // Duplex type user wants to use. - printing::mojom::DuplexMode duplex_mode = - printing::mojom::DuplexMode::kSimplex; - options.Get("duplexMode", &duplex_mode); + const auto duplex_mode = options.ValueOrDefault( + "duplexMode", printing::mojom::DuplexMode::kSimplex); settings.Set(printing::kSettingDuplexMode, static_cast(duplex_mode)); base::Value::Dict media_size; @@ -3150,14 +3130,11 @@ void WebContents::Print(gin::Arguments* args) { } // Set custom dots per inch (dpi) - gin_helper::Dictionary dpi_settings; - if (options.Get("dpi", &dpi_settings)) { - int horizontal = 72; - dpi_settings.Get("horizontal", &horizontal); - settings.Set(printing::kSettingDpiHorizontal, horizontal); - int vertical = 72; - dpi_settings.Get("vertical", &vertical); - settings.Set(printing::kSettingDpiVertical, vertical); + if (gin_helper::Dictionary dpi; options.Get("dpi", &dpi)) { + settings.Set(printing::kSettingDpiHorizontal, + dpi.ValueOrDefault("horizontal", 72)); + settings.Set(printing::kSettingDpiVertical, + dpi.ValueOrDefault("vertical", 72)); } print_task_runner_->PostTaskAndReplyWithResult( diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 933f783fa3745..8049b8784306c 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -764,9 +764,9 @@ void ElectronBrowserContext::DisplayMediaDeviceChosen( GetAudioDesktopMediaId(request.requested_audio_device_ids)); devices.audio_device = audio_device; } else if (result_dict.Get("audio", &rfh)) { - bool enable_local_echo = false; - result_dict.Get("enableLocalEcho", &enable_local_echo); - bool disable_local_echo = !enable_local_echo; + const bool enable_local_echo = + result_dict.ValueOrDefault("enableLocalEcho", false); + const bool disable_local_echo = !enable_local_echo; auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); blink::MediaStreamDevice audio_device( request.audio_type, diff --git a/shell/browser/electron_download_manager_delegate.cc b/shell/browser/electron_download_manager_delegate.cc index 560bf867790a3..320941208d626 100644 --- a/shell/browser/electron_download_manager_delegate.cc +++ b/shell/browser/electron_download_manager_delegate.cc @@ -294,8 +294,7 @@ void ElectronDownloadManagerDelegate::OnDownloadSaveDialogDone( if (!item) return; - bool canceled = true; - result.Get("canceled", &canceled); + const bool canceled = result.ValueOrDefault("canceled", true); base::FilePath path; diff --git a/shell/browser/net/electron_url_loader_factory.cc b/shell/browser/net/electron_url_loader_factory.cc index 1f06048ed3133..4e33330e355e9 100644 --- a/shell/browser/net/electron_url_loader_factory.cc +++ b/shell/browser/net/electron_url_loader_factory.cc @@ -125,8 +125,8 @@ network::mojom::URLResponseHeadPtr ToResponseHead( return head; } - int status_code = net::HTTP_OK; - dict.Get("statusCode", &status_code); + const int status_code = + dict.ValueOrDefault("statusCode", static_cast(net::HTTP_OK)); head->headers = base::MakeRefCounted( absl::StrFormat("HTTP/1.1 %d %s", status_code, net::GetHttpReasonPhrase( diff --git a/shell/browser/ui/cocoa/electron_touch_bar.mm b/shell/browser/ui/cocoa/electron_touch_bar.mm index 39f8a53cb8aa3..ee198098f3baf 100644 --- a/shell/browser/ui/cocoa/electron_touch_bar.mm +++ b/shell/browser/ui/cocoa/electron_touch_bar.mm @@ -398,8 +398,7 @@ - (void)updateButton:(NSCustomTouchBarItem*)item } } - bool enabled = true; - settings.Get("enabled", &enabled); + const bool enabled = settings.ValueOrDefault("enabled", true); [button setEnabled:enabled]; } @@ -501,16 +500,9 @@ - (void)updateSlider:(NSSliderTouchBarItem*)item settings.Get("label", &label); item.label = base::SysUTF8ToNSString(label); - int maxValue = 100; - int minValue = 0; - int value = 50; - settings.Get("minValue", &minValue); - settings.Get("maxValue", &maxValue); - settings.Get("value", &value); - - item.slider.minValue = minValue; - item.slider.maxValue = maxValue; - item.slider.doubleValue = value; + item.slider.minValue = settings.ValueOrDefault("minValue", 0); + item.slider.maxValue = settings.ValueOrDefault("maxValue", 100); + item.slider.doubleValue = settings.ValueOrDefault("value", 50); } - (NSTouchBarItem*)makePopoverForID:(NSString*)id @@ -540,9 +532,7 @@ - (void)updatePopover:(NSPopoverTouchBarItem*)item item.collapsedRepresentationImage = image.AsNSImage(); } - bool showCloseButton = true; - settings.Get("showCloseButton", &showCloseButton); - item.showsCloseButton = showCloseButton; + item.showsCloseButton = settings.ValueOrDefault("showCloseButton", true); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::HandleScope handle_scope(isolate); @@ -670,8 +660,7 @@ - (void)updateSegmentedControl:(NSCustomTouchBarItem*)item for (size_t i = 0; i < segments.size(); ++i) { std::string label; gfx::Image image; - bool enabled = true; - segments[i].Get("enabled", &enabled); + const bool enabled = segments[i].ValueOrDefault("enabled", true); if (segments[i].Get("label", &label)) { [control setLabel:base::SysUTF8ToNSString(label) forSegment:i]; } else { @@ -686,8 +675,7 @@ - (void)updateSegmentedControl:(NSCustomTouchBarItem*)item [control setEnabled:enabled forSegment:i]; } - int selectedIndex = 0; - settings.Get("selectedIndex", &selectedIndex); + const int selectedIndex = settings.ValueOrDefault("selectedIndex", 0); if (selectedIndex >= 0 && selectedIndex < control.segmentCount) control.selectedSegment = selectedIndex; } @@ -726,8 +714,8 @@ - (void)updateScrubber:(NSCustomTouchBarItem*)item withSettings:(const gin_helper::PersistentDictionary&)settings { NSScrubber* scrubber = item.view; - bool showsArrowButtons = false; - settings.Get("showArrowButtons", &showsArrowButtons); + const bool showsArrowButtons = + settings.ValueOrDefault("showArrowButtons", false); // The scrubber will crash if the user tries to scroll // and there are no items. if ([self numberOfItemsForScrubber:scrubber] > 0) @@ -766,9 +754,7 @@ - (void)updateScrubber:(NSCustomTouchBarItem*)item scrubber.mode = NSScrubberModeFree; } - bool continuous = true; - settings.Get("continuous", &continuous); - scrubber.continuous = continuous; + scrubber.continuous = settings.ValueOrDefault("continuous", true); [scrubber reloadData]; } diff --git a/shell/common/api/electron_api_url_loader.cc b/shell/common/api/electron_api_url_loader.cc index 607e6bee734d6..be953ab7fea19 100644 --- a/shell/common/api/electron_api_url_loader.cc +++ b/shell/common/api/electron_api_url_loader.cc @@ -615,9 +615,8 @@ gin::Handle SimpleURLLoaderWrapper::Create( } } - blink::mojom::FetchCacheMode cache_mode = - blink::mojom::FetchCacheMode::kDefault; - opts.Get("cache", &cache_mode); + const auto cache_mode = + opts.ValueOrDefault("cache", blink::mojom::FetchCacheMode::kDefault); switch (cache_mode) { case blink::mojom::FetchCacheMode::kNoStore: request->load_flags |= net::LOAD_DISABLE_CACHE; @@ -645,8 +644,8 @@ gin::Handle SimpleURLLoaderWrapper::Create( break; } - bool use_session_cookies = false; - opts.Get("useSessionCookies", &use_session_cookies); + const bool use_session_cookies = + opts.ValueOrDefault("useSessionCookies", false); int options = network::mojom::kURLLoadOptionSniffMimeType; if (!credentials_specified && !use_session_cookies) { // This is the default case, as well as the case when credentials is not @@ -656,9 +655,7 @@ gin::Handle SimpleURLLoaderWrapper::Create( options |= network::mojom::kURLLoadOptionBlockAllCookies; } - bool bypass_custom_protocol_handlers = false; - opts.Get("bypassCustomProtocolHandlers", &bypass_custom_protocol_handlers); - if (bypass_custom_protocol_handlers) + if (opts.ValueOrDefault("bypassCustomProtocolHandlers", false)) options |= kBypassCustomProtocolHandlers; v8::Local body; diff --git a/shell/common/gin_converters/blink_converter.cc b/shell/common/gin_converters/blink_converter.cc index f4f012d07fd24..f172c74cfd2e1 100644 --- a/shell/common/gin_converters/blink_converter.cc +++ b/shell/common/gin_converters/blink_converter.cc @@ -369,10 +369,8 @@ bool Converter::FromV8(v8::Isolate* isolate, if (!dict.Get("button", &out->button)) out->button = blink::WebMouseEvent::Button::kLeft; - float global_x = 0.f; - float global_y = 0.f; - dict.Get("globalX", &global_x); - dict.Get("globalY", &global_y); + const float global_x = dict.ValueOrDefault("globalX", 0.F); + const float global_y = dict.ValueOrDefault("globalY", 0.F); out->SetPositionInScreen(global_x, global_y); dict.Get("movementX", &out->movement_x); @@ -397,23 +395,19 @@ bool Converter::FromV8( dict.Get("accelerationRatioX", &out->acceleration_ratio_x); dict.Get("accelerationRatioY", &out->acceleration_ratio_y); - bool has_precise_scrolling_deltas = false; - dict.Get("hasPreciseScrollingDeltas", &has_precise_scrolling_deltas); - if (has_precise_scrolling_deltas) { - out->delta_units = ui::ScrollGranularity::kScrollByPrecisePixel; - } else { - out->delta_units = ui::ScrollGranularity::kScrollByPixel; - } + const bool precise = dict.ValueOrDefault("hasPreciseScrollingDeltas", false); + out->delta_units = precise ? ui::ScrollGranularity::kScrollByPrecisePixel + : ui::ScrollGranularity::kScrollByPixel; #if defined(USE_AURA) // Matches the behavior of ui/events/blink/web_input_event_traits.cc: - bool can_scroll = true; - if (dict.Get("canScroll", &can_scroll) && !can_scroll) { + if (!dict.ValueOrDefault("canScroll", true)) { out->delta_units = ui::ScrollGranularity::kScrollByPage; out->SetModifiers(out->GetModifiers() & ~blink::WebInputEvent::Modifiers::kControlKey); } #endif + return true; } diff --git a/shell/common/gin_helper/persistent_dictionary.h b/shell/common/gin_helper/persistent_dictionary.h index 7bb41835fd7b1..4020606d09dfd 100644 --- a/shell/common/gin_helper/persistent_dictionary.h +++ b/shell/common/gin_helper/persistent_dictionary.h @@ -41,6 +41,15 @@ class PersistentDictionary { gin::ConvertFromV8(isolate_, value, out); } + // Convenience function for using a default value if the + // specified key isn't present in the dictionary. + template + T ValueOrDefault(const std::string_view key, T default_value) const { + if (auto value = T{}; Get(key, &value)) + return value; + return default_value; + } + private: raw_ptr isolate_ = nullptr; v8::Global handle_; From c7d9fb602efdc3e350a44626a6e28a700157e7db Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 09:51:20 +0200 Subject: [PATCH 208/339] chore: bump chromium to 136.0.7103.93 (36-x-y) (#46974) * chore: bump chromium in DEPS to 136.0.7103.93 * chore: update patches * ozone/wayland: Fix bookmark dropdown right click context menu https://chromium-review.googlesource.com/c/chromium/src/+/6488801 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- ...ld_do_not_depend_on_packed_resource_integrity.patch | 2 +- ...ouse_tracking_and_message_bubbling_on_windows.patch | 2 +- ..._the_first_menu_item_when_opened_via_keyboard.patch | 10 ++++++---- ...reconversion_due_to_invalid_replacement_range.patch | 4 ++-- .../mas_avoid_private_macos_api_usage.patch.patch | 4 ++-- patches/chromium/render_widget_host_view_mac.patch | 8 ++++---- ...win32_exports_to_avoid_conflicts_with_node_js.patch | 8 ++++---- 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/DEPS b/DEPS index 2d26ec6305270..4e8a4db8eb763 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.49', + '136.0.7103.93', 'node_version': 'v22.15.0', 'nan_version': diff --git a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch index b8e6f9b1f8afb..b83cb6b76dfb7 100644 --- a/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch +++ b/patches/chromium/build_do_not_depend_on_packed_resource_integrity.patch @@ -46,7 +46,7 @@ index b5783087429ee651d21e04f6f5f11f5cb6f015a8..dae1cca8cee4fccfed3183aca1ba7cc3 # than here in :chrome_dll. deps += [ "//chrome:packed_resources_integrity_header" ] diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn -index a1338c3f145e3ae0ca0acbe20ffcec35c3cc5ba1..39d0efc3367b80b18cf3a4ef8756f147807ad79b 100644 +index 4dac075ed00691be14546b8bcd8f1b14fdc038b1..593ef717845c2c4c98aacd077d0696de207cff29 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn @@ -7096,9 +7096,12 @@ test("unit_tests") { diff --git a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch index bedbac426a774..7f823c37ff7f5 100644 --- a/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch +++ b/patches/chromium/fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch @@ -13,7 +13,7 @@ messages in the legacy window handle layer. These conditions are regularly hit with WCO-enabled windows on Windows. diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc -index ffbef89276aeaa167424b67a07080b38457ec5d3..f39a79687595d7547e3a25fc4dc1cdbc5c2495b4 100644 +index 72f220bc5ac0669994d2a7517b81ab176526cd0c..67c8f7a78738d45c7721c6dfbb004c4ed3d2c7ad 100644 --- a/content/browser/renderer_host/legacy_render_widget_host_win.cc +++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc @@ -328,12 +328,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, diff --git a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch index 9700e831af6ca..2a41793acedb7 100644 --- a/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch +++ b/patches/chromium/fix_select_the_first_menu_item_when_opened_via_keyboard.patch @@ -6,14 +6,16 @@ Subject: fix: select the first menu item when opened via keyboard This fixes an accessibility issue where the root view is 'focused' to the screen reader instead of the first menu item as with all other native menus. This patch will be upstreamed. diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc -index c5f3700f8f04b11b1a3dcc214dab40622652cd3a..43cbc0f8febd2330f47b5617b3dc9da9beac4962 100644 +index 74dd82258742f9e217966174e3ee438086b946bb..9b8b40fa87f3354f9b5ee7a5c4746aae22f5c8c6 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc -@@ -701,6 +701,14 @@ void MenuController::Run(Widget* parent, +@@ -711,6 +711,16 @@ void MenuController::Run(Widget* parent, SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); } -+ if (source_type == ui::mojom::MenuSourceType::kKeyboard && context_menu && root->HasSubmenu()) { ++ if (source_type == ui::mojom::MenuSourceType::kKeyboard && ++ (menu_type == MenuType::kContextMenu || menu_type == MenuType::kMenuItemContextMenu) && ++ root->HasSubmenu()) { + // For context menus opened via the keyboard we select the first item by default + // to match accessibility expectations + MenuItemView* first_item = FindInitialSelectableMenuItem(root, INCREMENT_SELECTION_DOWN); @@ -24,7 +26,7 @@ index c5f3700f8f04b11b1a3dcc214dab40622652cd3a..43cbc0f8febd2330f47b5617b3dc9da9 if (button_controller) { pressed_lock_ = button_controller->TakeLock( false, ui::LocatedEvent::FromIfValid(event)); -@@ -2407,19 +2415,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { +@@ -2427,19 +2437,15 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { } item->GetSubmenu()->ShowAt(params); diff --git a/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch index 3298782f91e5b..6dc377ddac639 100644 --- a/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch +++ b/patches/chromium/mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch @@ -24,10 +24,10 @@ Reviewed-by: Marijn Kruisselbrink Cr-Commit-Position: refs/heads/main@{#1448935} diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index f2499bc084312a09b2324567d270fc1b899e7617..12ee7e75e437426f28002c7c9f4d5f5b5016ec53 100644 +index 300d90a25cb87019d6d179dac029138fd759b80b..008e1963242bde90d818013494b8d78482d482ce 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -@@ -2415,9 +2415,10 @@ - (void)setMarkedText:(id)string +@@ -2424,9 +2424,10 @@ - (void)setMarkedText:(id)string if ([self isHandlingKeyDown] && !_isReconversionTriggered) { _setMarkedTextReplacementRange = gfx::Range(replacementRange); } else { diff --git a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch index 0fcc4487f1b9d..768aa32004924 100644 --- a/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch +++ b/patches/chromium/mas_avoid_private_macos_api_usage.patch.patch @@ -545,7 +545,7 @@ index dbf334caa3a6d10017b69ad76802e389a011436b..da828823e8195cc9e497866363c9af93 void ForwardKeyboardEvent(const input::NativeWebKeyboardEvent& key_event, diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index 4017ee032569466f5311e5c9612c82c086eab935..f2499bc084312a09b2324567d270fc1b899e7617 100644 +index c125b021061b74b8a7e01b757d08f0b1fa89f14c..300d90a25cb87019d6d179dac029138fd759b80b 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -35,6 +35,7 @@ @@ -556,7 +556,7 @@ index 4017ee032569466f5311e5c9612c82c086eab935..f2499bc084312a09b2324567d270fc1b #include "skia/ext/skia_utils_mac.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/input/input_handler.mojom.h" -@@ -2136,15 +2137,21 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -2145,15 +2146,21 @@ - (NSAccessibilityRole)accessibilityRole { // Since this implementation doesn't have to wait any IPC calls, this doesn't // make any key-typing jank. --hbono 7/23/09 // diff --git a/patches/chromium/render_widget_host_view_mac.patch b/patches/chromium/render_widget_host_view_mac.patch index 3d7f24d1e77fa..ed4b423297ced 100644 --- a/patches/chromium/render_widget_host_view_mac.patch +++ b/patches/chromium/render_widget_host_view_mac.patch @@ -8,7 +8,7 @@ respond to the first mouse click in their window, which is desirable for some kinds of utility windows. Similarly for `disableAutoHideCursor`. diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index 22ee05153569d0db7cbc7ab520944e84b9475c8e..4017ee032569466f5311e5c9612c82c086eab935 100644 +index b0a49de7b4ed6402d0a849c9c971712f8bd2a126..c125b021061b74b8a7e01b757d08f0b1fa89f14c 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -170,6 +170,15 @@ void ExtractUnderlines(NSAttributedString* string, @@ -27,7 +27,7 @@ index 22ee05153569d0db7cbc7ab520944e84b9475c8e..4017ee032569466f5311e5c9612c82c0 // RenderWidgetHostViewCocoa --------------------------------------------------- // Private methods: -@@ -774,6 +783,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { +@@ -783,6 +792,9 @@ - (AcceptMouseEventsOption)acceptsMouseEventsOption { } - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { @@ -37,7 +37,7 @@ index 22ee05153569d0db7cbc7ab520944e84b9475c8e..4017ee032569466f5311e5c9612c82c0 // Enable "click-through" if mouse clicks are accepted in inactive windows return [self acceptsMouseEventsOption] > kAcceptMouseEventsInActiveWindow; } -@@ -919,6 +931,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { +@@ -928,6 +940,10 @@ - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent { // its parent view. BOOL hitSelf = NO; while (view) { @@ -48,7 +48,7 @@ index 22ee05153569d0db7cbc7ab520944e84b9475c8e..4017ee032569466f5311e5c9612c82c0 if (view == self) hitSelf = YES; if ([view isKindOfClass:[self class]] && ![view isEqual:self] && -@@ -1253,6 +1269,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { +@@ -1262,6 +1278,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { eventType == NSEventTypeKeyDown && !(modifierFlags & NSEventModifierFlagCommand); diff --git a/patches/sqlite/fix_rename_sqlite_win32_exports_to_avoid_conflicts_with_node_js.patch b/patches/sqlite/fix_rename_sqlite_win32_exports_to_avoid_conflicts_with_node_js.patch index 0978319495756..77cc762110dd3 100644 --- a/patches/sqlite/fix_rename_sqlite_win32_exports_to_avoid_conflicts_with_node_js.patch +++ b/patches/sqlite/fix_rename_sqlite_win32_exports_to_avoid_conflicts_with_node_js.patch @@ -10,13 +10,13 @@ the exported symbols in SQLite to avoid conflicts with Node.js. This should be upstreamed to SQLite. diff --git a/amalgamation/rename_exports.h b/amalgamation/rename_exports.h -index b1c485159a624ea1bfbec603aabc58074721e72a..8eb71ae67acc3e0ad17bae2e87e85bf12c10b9af 100644 +index 91a69bfdea89ed1845c330e71105cf818b6f0e2f..dcf92ac793f04c061d6db64bf26fcb98884490cf 100644 --- a/amalgamation/rename_exports.h +++ b/amalgamation/rename_exports.h @@ -367,6 +367,15 @@ - #define sqlite3session_patchset chrome_sqlite3session_patchset // Lines 11449-11453 - #define sqlite3session_patchset_strm chrome_sqlite3session_patchset_strm // Lines 12728-12732 - #define sqlite3session_table_filter chrome_sqlite3session_table_filter // Lines 11219-11226 + #define sqlite3session_patchset chrome_sqlite3session_patchset // Lines 11609-11613 + #define sqlite3session_patchset_strm chrome_sqlite3session_patchset_strm // Lines 12888-12892 + #define sqlite3session_table_filter chrome_sqlite3session_table_filter // Lines 11379-11386 +#define sqlite3_win32_write_debug chrome_sqlite3_win32_write_debug +#define sqlite3_win32_sleep chrome_sqlite3_win32_sleep +#define sqlite3_win32_is_nt chrome_sqlite3_win32_is_nt From 07c03573665e1a3ea85a8113f2f8e8cdf3160cfe Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 09:28:05 -0400 Subject: [PATCH 209/339] fix: use-after-move of bus connection in xdg portal detection (#47024) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- shell/browser/ui/file_dialog_linux_portal.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/browser/ui/file_dialog_linux_portal.cc b/shell/browser/ui/file_dialog_linux_portal.cc index 298ee25caffb4..7dd520d92bdfa 100644 --- a/shell/browser/ui/file_dialog_linux_portal.cc +++ b/shell/browser/ui/file_dialog_linux_portal.cc @@ -86,8 +86,9 @@ void CheckPortalAvailabilityOnBusThread() { << (g_portal_available ? "yes" : "no"); flag->Set(); bus->ShutdownAndBlock(); + bus.reset(); }, - std::move(bus), flag)); + bus, flag)); } } // namespace From f5bb2f480db3b0b4ab4bbe0104b6f583a51cd5bc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 12:53:11 -0400 Subject: [PATCH 210/339] build: update hasher return value (#47012) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Keeley Hammond --- script/release/get-url-hash.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/release/get-url-hash.ts b/script/release/get-url-hash.ts index ebbf5bce54c64..dc4bb77672a2a 100644 --- a/script/release/get-url-hash.ts +++ b/script/release/get-url-hash.ts @@ -28,7 +28,9 @@ export async function getUrlHash (targetUrl: string, algorithm = 'sha256', attem } if (!resp.body) throw new Error('Successful lambda call but failed to get valid hash'); - return resp.body.trim(); + // response shape should be { hash: 'xyz', invocationId: "abc"} + const { hash } = JSON.parse(resp.body.trim()); + return hash; } catch (err) { if (attempts > 1) { const { response } = err as any; From 7030816fe16f84ddb5311d425e179d77bba2b099 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 13:10:19 -0400 Subject: [PATCH 211/339] fix: restore previous Windows screenshotting (#47032) Fixes https://github.com/electron/electron/issues/45990 We previously made a change in https://github.com/electron/electron/pull/45868 to fix content protection being lost on hide and re-show. However, this cause a breaking change where protected windows were made opaque black instead of being hidden as before. This overrides relevant methods in ElectronDesktopWindowTreeHostWin to restore the previous behavior. without regressing the original issue. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../ui/win/electron_desktop_window_tree_host_win.cc | 10 ++++++++++ .../ui/win/electron_desktop_window_tree_host_win.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index ac4ffc26e0ddf..c5b0b201071a3 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -126,6 +126,16 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { return views::DesktopWindowTreeHostWin::HandleMouseEvent(event); } +void ElectronDesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) { + if (native_window_view_->widget()) + native_window_view_->widget()->OnNativeWidgetVisibilityChanged(visible); +} + +void ElectronDesktopWindowTreeHostWin::SetAllowScreenshots(bool allow) { + ::SetWindowDisplayAffinity(GetAcceleratedWidget(), + allow ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE); +} + void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated( ui::NativeTheme* observed_theme) { HWND hWnd = GetAcceleratedWidget(); diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h index cc51d42e83534..6abe46d81a836 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -41,6 +41,8 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, HMONITOR monitor) const override; bool HandleMouseEventForCaption(UINT message) const override; bool HandleMouseEvent(ui::MouseEvent* event) override; + void HandleVisibilityChanged(bool visible) override; + void SetAllowScreenshots(bool allow) override; // ui::NativeThemeObserver: void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; From 615b65b2a25557372953ee32fa22e2baca479757 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 17:45:54 -0500 Subject: [PATCH 212/339] docs: unify [!NOTE] structure (#47047) * docs: unify [!NOTE] structure Co-authored-by: Erick Zhao * Update docs/api/command-line.md Co-authored-by: Niklas Wenzel Co-authored-by: Erick Zhao * Update docs/api/browser-window.md Co-authored-by: Niklas Wenzel Co-authored-by: Erick Zhao * Update docs/api/download-item.md Co-authored-by: Niklas Wenzel Co-authored-by: Erick Zhao * Update docs/api/global-shortcut.md Co-authored-by: Niklas Wenzel Co-authored-by: Erick Zhao * revert line break Co-authored-by: Erick Zhao --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao Co-authored-by: Erick Zhao --- docs/api/app.md | 165 +++++++++++------- docs/api/auto-updater.md | 22 ++- docs/api/base-window.md | 69 +++++--- docs/api/browser-view.md | 7 +- docs/api/browser-window.md | 82 +++++---- docs/api/clipboard.md | 10 +- docs/api/command-line-switches.md | 6 +- docs/api/command-line.md | 18 +- docs/api/content-tracing.md | 5 +- docs/api/crash-reporter.md | 48 +++-- docs/api/desktop-capturer.md | 10 +- docs/api/dialog.md | 41 +++-- docs/api/dock.md | 3 +- docs/api/download-item.md | 15 +- docs/api/extensions-api.md | 25 +-- docs/api/extensions.md | 9 +- docs/api/global-shortcut.md | 14 +- docs/api/ipc-renderer.md | 7 +- docs/api/menu-item.md | 9 +- docs/api/menu.md | 17 +- docs/api/native-image.md | 3 +- docs/api/net-log.md | 5 +- docs/api/net.md | 5 +- docs/api/power-save-blocker.md | 9 +- docs/api/process.md | 3 +- docs/api/protocol.md | 10 +- docs/api/screen.md | 8 +- docs/api/session.md | 54 +++--- docs/api/shell.md | 3 +- docs/api/structures/jump-list-category.md | 16 +- docs/api/structures/point.md | 7 +- docs/api/touch-bar-other-items-proxy.md | 5 +- docs/api/touch-bar.md | 14 +- docs/api/tray.md | 3 +- docs/api/utility-process.md | 6 +- docs/api/view.md | 6 +- docs/api/web-contents.md | 22 ++- docs/api/web-frame.md | 13 +- docs/api/webview-tag.md | 12 +- docs/development/build-instructions-gn.md | 3 +- .../development/build-instructions-windows.md | 5 +- docs/development/creating-api.md | 3 +- docs/development/patches.md | 3 +- docs/tutorial/keyboard-shortcuts.md | 7 +- docs/tutorial/multithreading.md | 3 +- docs/tutorial/online-offline-events.md | 3 +- docs/tutorial/process-model.md | 3 +- docs/tutorial/repl.md | 5 +- docs/tutorial/web-embeds.md | 12 +- 49 files changed, 509 insertions(+), 324 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 281f606f64ecf..119c867abef9b 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -41,9 +41,10 @@ that was used to open the application, if it was launched from Notification Cent You can also call `app.isReady()` to check if this event has already fired and `app.whenReady()` to get a Promise that is fulfilled when Electron is initialized. -**Note**: The `ready` event is only fired after the main process has finished running the first -tick of the event loop. If an Electron API needs to be called before the `ready` event, ensure -that it is called synchronously in the top-level context of the main process. +> [!NOTE] +> The `ready` event is only fired after the main process has finished running the first +> tick of the event loop. If an Electron API needs to be called before the `ready` event, ensure +> that it is called synchronously in the top-level context of the main process. ### Event: 'window-all-closed' @@ -66,12 +67,14 @@ Emitted before the application starts closing its windows. Calling `event.preventDefault()` will prevent the default behavior, which is terminating the application. -**Note:** If application quit was initiated by `autoUpdater.quitAndInstall()`, -then `before-quit` is emitted _after_ emitting `close` event on all windows and -closing them. +> [!NOTE] +> If application quit was initiated by `autoUpdater.quitAndInstall()`, +> then `before-quit` is emitted _after_ emitting `close` event on all windows and +> closing them. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'will-quit' @@ -86,8 +89,9 @@ terminating the application. See the description of the `window-all-closed` event for the differences between the `will-quit` and `window-all-closed` events. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'quit' @@ -98,8 +102,9 @@ Returns: Emitted when the application is quitting. -**Note:** On Windows, this event will not be emitted if the app is closed due -to a shutdown/restart of the system or a user logout. +> [!NOTE] +> On Windows, this event will not be emitted if the app is closed due +> to a shutdown/restart of the system or a user logout. ### Event: 'open-file' _macOS_ @@ -470,24 +475,28 @@ and `workingDirectory` is its current working directory. Usually applications respond to this by making their primary window focused and non-minimized. -**Note:** `argv` will not be exactly the same list of arguments as those passed -to the second instance. The order might change and additional arguments might be appended. -If you need to maintain the exact same arguments, it's advised to use `additionalData` instead. +> [!NOTE] +> `argv` will not be exactly the same list of arguments as those passed +> to the second instance. The order might change and additional arguments might be appended. +> If you need to maintain the exact same arguments, it's advised to use `additionalData` instead. -**Note:** If the second instance is started by a different user than the first, the `argv` array will not include the arguments. +> [!NOTE] +> If the second instance is started by a different user than the first, the `argv` array will not include the arguments. This event is guaranteed to be emitted after the `ready` event of `app` gets emitted. -**Note:** Extra command line arguments might be added by Chromium, -such as `--original-process-start-time`. +> [!NOTE] +> Extra command line arguments might be added by Chromium, +> such as `--original-process-start-time`. ## Methods The `app` object has the following methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. ### `app.quit()` @@ -679,7 +688,8 @@ preferred over `name` by Electron. Overrides the current application's name. -**Note:** This function overrides the name used internally by Electron; it does not affect the name that the OS uses. +> [!NOTE] +> This function overrides the name used internally by Electron; it does not affect the name that the OS uses. ### `app.getLocale()` @@ -688,18 +698,22 @@ Possible return values are documented [here](https://source.chromium.org/chromiu To set the locale, you'll want to use a command line switch at app startup, which may be found [here](command-line-switches.md). -**Note:** When distributing your packaged app, you have to also ship the -`locales` folder. +> [!NOTE] +> When distributing your packaged app, you have to also ship the +> `locales` folder. -**Note:** This API must be called after the `ready` event is emitted. +> [!NOTE] +> This API must be called after the `ready` event is emitted. -**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). +> [!NOTE] +> To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). ### `app.getLocaleCountryCode()` Returns `string` - User operating system's locale two-letter [ISO 3166](https://www.iso.org/iso-3166-country-codes.html) country code. The value is taken from native OS APIs. -**Note:** When unable to detect locale country code, it returns empty string. +> [!NOTE] +> When unable to detect locale country code, it returns empty string. ### `app.getSystemLocale()` @@ -712,9 +726,11 @@ Different operating systems also use the regional data differently: Therefore, this API can be used for purposes such as choosing a format for rendering dates and times in a calendar app, especially when the developer wants the format to be consistent with the OS. -**Note:** This API must be called after the `ready` event is emitted. +> [!NOTE] +> This API must be called after the `ready` event is emitted. -**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). +> [!NOTE] +> To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages). ### `app.getPreferredSystemLanguages()` @@ -777,16 +793,18 @@ Once registered, all links with `your-protocol://` will be opened with the current executable. The whole link, including protocol, will be passed to your application as a parameter. -**Note:** On macOS, you can only register protocols that have been added to -your app's `info.plist`, which cannot be modified at runtime. However, you can -change the file during build time via [Electron Forge][electron-forge], -[Electron Packager][electron-packager], or by editing `info.plist` with a text -editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details. +> [!NOTE] +> On macOS, you can only register protocols that have been added to +> your app's `info.plist`, which cannot be modified at runtime. However, you can +> change the file during build time via [Electron Forge][electron-forge], +> [Electron Packager][electron-packager], or by editing `info.plist` with a text +> editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details. -**Note:** In a Windows Store environment (when packaged as an `appx`) this API -will return `true` for all calls but the registry key it sets won't be accessible -by other applications. In order to register your Windows Store application -as a default protocol handler you must [declare the protocol in your manifest](https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). +> [!NOTE] +> In a Windows Store environment (when packaged as an `appx`) this API +> will return `true` for all calls but the registry key it sets won't be accessible +> by other applications. In order to register your Windows Store application +> as a default protocol handler you must [declare the protocol in your manifest](https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally. @@ -810,11 +828,12 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler. Returns `boolean` - Whether the current executable is the default handler for a protocol (aka URI scheme). -**Note:** On macOS, you can use this method to check if the app has been -registered as the default protocol handler for a protocol. You can also verify -this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the -macOS machine. Please refer to -[Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. +> [!NOTE] +> On macOS, you can use this method to check if the app has been +> registered as the default protocol handler for a protocol. You can also verify +> this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the +> macOS machine. Please refer to +> [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally. @@ -858,8 +877,9 @@ Adds `tasks` to the [Tasks][tasks] category of the Jump List on Windows. Returns `boolean` - Whether the call succeeded. -**Note:** If you'd like to customize the Jump List even more use -`app.setJumpList(categories)` instead. +> [!NOTE] +> If you'd like to customize the Jump List even more use +> `app.setJumpList(categories)` instead. ### `app.getJumpListSettings()` _Windows_ @@ -897,21 +917,24 @@ following strings: If `categories` is `null` the previously set custom Jump List (if any) will be replaced by the standard Jump List for the app (managed by Windows). -**Note:** If a `JumpListCategory` object has neither the `type` nor the `name` -property set then its `type` is assumed to be `tasks`. If the `name` property +> [!NOTE] +> If a `JumpListCategory` object has neither the `type` nor the `name` +> property set then its `type` is assumed to be `tasks`. If the `name` property is set but the `type` property is omitted then the `type` is assumed to be `custom`. -**Note:** Users can remove items from custom categories, and Windows will not -allow a removed item to be added back into a custom category until **after** -the next successful call to `app.setJumpList(categories)`. Any attempt to -re-add a removed item to a custom category earlier than that will result in the -entire custom category being omitted from the Jump List. The list of removed -items can be obtained using `app.getJumpListSettings()`. +> [!NOTE] +> Users can remove items from custom categories, and Windows will not +> allow a removed item to be added back into a custom category until **after** +> the next successful call to `app.setJumpList(categories)`. Any attempt to +> re-add a removed item to a custom category earlier than that will result in the +> entire custom category being omitted from the Jump List. The list of removed +> items can be obtained using `app.getJumpListSettings()`. -**Note:** The maximum length of a Jump List item's `description` property is -260 characters. Beyond this limit, the item will not be added to the Jump -List, nor will it be displayed. +> [!NOTE] +> The maximum length of a Jump List item's `description` property is +> 260 characters. Beyond this limit, the item will not be added to the Jump +> List, nor will it be displayed. Here's a very simple example of creating a custom Jump List: @@ -1188,7 +1211,8 @@ Returns [`ProcessMetric[]`](structures/process-metric.md): Array of `ProcessMetr Returns [`GPUFeatureStatus`](structures/gpu-feature-status.md) - The Graphics Feature Status from `chrome://gpu/`. -**Note:** This information is only usable after the `gpu-info-update` event is emitted. +> [!NOTE] +> This information is only usable after the `gpu-info-update` event is emitted. ### `app.getGPUInfo(infoType)` @@ -1242,11 +1266,13 @@ badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity launcher. -**Note:** Unity launcher requires a `.desktop` file to work. For more information, -please read the [Unity integration documentation][unity-requirement]. +> [!NOTE] +> Unity launcher requires a `.desktop` file to work. For more information, +> please read the [Unity integration documentation][unity-requirement]. -**Note:** On macOS, you need to ensure that your application has the permission -to display notifications for this method to work. +> [!NOTE] +> On macOS, you need to ensure that your application has the permission +> to display notifications for this method to work. ### `app.getBadgeCount()` _Linux_ _macOS_ @@ -1348,7 +1374,8 @@ details. Disabled by default. This API must be called after the `ready` event is emitted. -**Note:** Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. +> [!NOTE] +> Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. ### `app.showAboutPanel()` @@ -1476,7 +1503,8 @@ By using this API, important information such as password and other sensitive in See [Apple's documentation](https://developer.apple.com/library/archive/technotes/tn2150/_index.html) for more details. -**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed. +> [!NOTE] +> Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed. ### `app.setProxy(config)` @@ -1538,7 +1566,8 @@ See [Chromium's accessibility docs](https://www.chromium.org/developers/design-d This API must be called after the `ready` event is emitted. -**Note:** Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. +> [!NOTE] +> Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default. ### `app.applicationMenu` @@ -1551,11 +1580,13 @@ An `Integer` property that returns the badge count for current app. Setting the On macOS, setting this with any nonzero integer shows on the dock icon. On Linux, this property only works for Unity launcher. -**Note:** Unity launcher requires a `.desktop` file to work. For more information, -please read the [Unity integration documentation][unity-requirement]. +> [!NOTE] +> Unity launcher requires a `.desktop` file to work. For more information, +> please read the [Unity integration documentation][unity-requirement]. -**Note:** On macOS, you need to ensure that your application has the permission -to display notifications for this property to take effect. +> [!NOTE] +> On macOS, you need to ensure that your application has the permission +> to display notifications for this property to take effect. ### `app.commandLine` _Readonly_ diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index bf419f33ad70a..f2e4a37945037 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -26,8 +26,9 @@ requirements, you can read [Server Support][server-support]. Note that update process. Apps that need to disable ATS can add the `NSAllowsArbitraryLoads` key to their app's plist. -**Note:** Your application must be signed for automatic updates on macOS. -This is a requirement of `Squirrel.Mac`. +> [!IMPORTANT] +> Your application must be signed for automatic updates on macOS. +> This is a requirement of `Squirrel.Mac`. ### Windows @@ -93,8 +94,9 @@ Emitted when an update has been downloaded. On Windows only `releaseName` is available. -**Note:** It is not strictly necessary to handle this event. A successfully -downloaded update will still be applied the next time the application starts. +> [!NOTE] +> It is not strictly necessary to handle this event. A successfully +> downloaded update will still be applied the next time the application starts. ### Event: 'before-quit-for-update' @@ -125,8 +127,9 @@ Returns `string` - The current update feed URL. Asks the server whether there is an update. You must call `setFeedURL` before using this API. -**Note:** If an update is available it will be downloaded automatically. -Calling `autoUpdater.checkForUpdates()` twice will download the update two times. +> [!NOTE] +> If an update is available it will be downloaded automatically. +> Calling `autoUpdater.checkForUpdates()` twice will download the update two times. ### `autoUpdater.quitAndInstall()` @@ -137,9 +140,10 @@ Under the hood calling `autoUpdater.quitAndInstall()` will close all application windows first, and automatically call `app.quit()` after all windows have been closed. -**Note:** It is not strictly necessary to call this function to apply an update, -as a successfully downloaded update will always be applied the next time the -application starts. +> [!NOTE] +> It is not strictly necessary to call this function to apply an update, +> as a successfully downloaded update will always be applied the next time the +> application starts. [squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac [server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 7ad17d045c909..d59cdb286731d 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -4,7 +4,7 @@ Process: [Main](../glossary.md#main-process) -> **Note** +> [!NOTE] > `BaseWindow` provides a flexible way to compose multiple web views in a > single window. For windows with only a single, full-size web view, the > [`BrowserWindow`](browser-window.md) class may be a simpler option. @@ -107,8 +107,9 @@ It creates a new `BaseWindow` with native properties as set by the `options`. Objects created with `new BaseWindow` emit the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some events are only available on specific operating systems and are +> labeled as such. #### Event: 'close' @@ -137,7 +138,11 @@ window.onbeforeunload = (e) => { } ``` -_**Note**: There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and `window.addEventListener('beforeunload', handler)`. It is recommended to always set the `event.returnValue` explicitly, instead of only returning a value, as the former works more consistently within Electron._ +> [!NOTE] +> There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and +> `window.addEventListener('beforeunload', handler)`. It is recommended to always set the +> `event.returnValue` explicitly, instead of only returning a value, as the former works more +> consistently within Electron. #### Event: 'closed' @@ -252,7 +257,8 @@ Emitted when the window is being moved to a new position. Emitted once when the window is moved to a new position. -**Note**: On macOS this event is an alias of `move`. +> [!NOTE] +> On macOS, this event is an alias of `move`. #### Event: 'enter-full-screen' @@ -421,7 +427,8 @@ A `boolean` property that determines whether the window is focusable. A `boolean` property that determines whether the window is visible on all workspaces. -**Note:** Always returns false on Windows. +> [!NOTE] +> Always returns false on Windows. #### `win.shadow` @@ -431,7 +438,8 @@ A `boolean` property that determines whether the window has a shadow. A `boolean` property that determines whether the menu bar should be visible. -**Note:** If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. +> [!NOTE] +> If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. #### `win.kiosk` @@ -452,7 +460,8 @@ and the icon of the file will show in window's title bar. A `string` property that determines the title of the native window. -**Note:** The title of the web page can be different from the title of the native window. +> [!NOTE] +> The title of the web page can be different from the title of the native window. #### `win.minimizable` _macOS_ _Windows_ @@ -521,8 +530,9 @@ A `boolean` property that indicates whether the window is arranged via [Snap.](h Objects created with `new BaseWindow` have the following instance methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. #### `win.setContentView(view)` @@ -614,7 +624,8 @@ Returns `boolean` - Whether the window is minimized. Sets whether the window should be in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](base-window.md#event-enter-full-screen) or ['leave-full-screen'](base-window.md#event-leave-full-screen) events. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](base-window.md#event-enter-full-screen) or > ['leave-full-screen'](base-window.md#event-leave-full-screen) events. #### `win.isFullScreen()` @@ -728,13 +739,15 @@ win.setBounds({ width: 100 }) console.log(win.getBounds()) ``` -**Note:** On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. +> [!NOTE] +> On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. #### `win.getBounds()` Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window as `Object`. -**Note:** On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. +> [!NOTE] +> On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. #### `win.getBackgroundColor()` @@ -742,7 +755,8 @@ Returns `string` - Gets the background color of the window in Hex (`#RRGGBB`) fo See [Setting `backgroundColor`](browser-window.md#setting-the-backgroundcolor-property). -**Note:** The alpha value is _not_ returned alongside the red, green, and blue values. +> [!NOTE] +> The alpha value is _not_ returned alongside the red, green, and blue values. #### `win.setContentBounds(bounds[, animate])` @@ -760,7 +774,8 @@ Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window's cl Returns [`Rectangle`](structures/rectangle.md) - Contains the window bounds of the normal state -**Note:** whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). +> [!NOTE] +> Whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). #### `win.setEnabled(enable)` @@ -957,8 +972,9 @@ Changes the title of native window to `title`. Returns `string` - The title of the native window. -**Note:** The title of the web page can be different from the title of the native -window. +> [!NOTE] +> The title of the web page can be different from the title of the native +> window. #### `win.setSheetOffset(offsetY[, offsetX])` _macOS_ @@ -1232,8 +1248,9 @@ in the taskbar. Sets the properties for the window's taskbar button. -**Note:** `relaunchCommand` and `relaunchDisplayName` must always be set -together. If one of those properties is not set, then neither will be used. +> [!NOTE] +> `relaunchCommand` and `relaunchDisplayName` must always be set +> together. If one of those properties is not set, then neither will be used. #### `win.setIcon(icon)` _Windows_ _Linux_ @@ -1293,13 +1310,15 @@ maximize button, or by dragging it to the edges of the screen. Sets whether the window should be visible on all workspaces. -**Note:** This API does nothing on Windows. +> [!NOTE] +> This API does nothing on Windows. #### `win.isVisibleOnAllWorkspaces()` _macOS_ _Linux_ Returns `boolean` - Whether the window is visible on all workspaces. -**Note:** This API always returns false on Windows. +> [!NOTE] +> This API always returns false on Windows. #### `win.setIgnoreMouseEvents(ignore[, options])` @@ -1416,7 +1435,8 @@ This method sets the browser window's system-drawn background material, includin See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type) for more details. -**Note:** This method is only supported on Windows 11 22H2 and up. +> [!NOTE] +> This method is only supported on Windows 11 22H2 and up. #### `win.setWindowButtonPosition(position)` _macOS_ @@ -1438,8 +1458,9 @@ Sets the touchBar layout for the current window. Specifying `null` or `undefined` clears the touch bar. This method only has an effect if the machine has a touch bar. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. #### `win.setTitleBarOverlay(options)` _Windows_ _Linux_ diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index f3873c538a69b..693363ec4b2eb 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -8,7 +8,7 @@ deprecated: ``` --> -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -29,7 +29,7 @@ deprecated: > Create and control views. -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -176,4 +176,5 @@ Examples of valid `color` values: * Similar to CSS Color Module Level 3 keywords, but case-sensitive. * e.g. `blueviolet` or `red` -**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. +> [!NOTE] +> Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 95a1baa784450..bb45135c16009 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -158,7 +158,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. Objects created with `new BrowserWindow` emit the following events: -**Note:** Some events are only available on specific operating systems and are +> [!NOTE] +> Some events are only available on specific operating systems and are labeled as such. #### Event: 'page-title-updated' @@ -200,7 +201,11 @@ window.onbeforeunload = (e) => { } ``` -_**Note**: There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and `window.addEventListener('beforeunload', handler)`. It is recommended to always set the `event.returnValue` explicitly, instead of only returning a value, as the former works more consistently within Electron._ +> [!NOTE] +> There is a subtle difference between the behaviors of `window.onbeforeunload = handler` and +> `window.addEventListener('beforeunload', handler)`. It is recommended to always set the +> `event.returnValue` explicitly, instead of only returning a value, as the former works more +> consistently within Electron. #### Event: 'closed' @@ -323,7 +328,8 @@ Emitted when the window is being moved to a new position. Emitted once when the window is moved to a new position. -**Note**: On macOS this event is an alias of `move`. +> [!NOTE] +> On macOS, this event is an alias of `move`. #### Event: 'enter-full-screen' @@ -460,7 +466,7 @@ or `null` if the contents are not owned by a window. * `browserView` [BrowserView](browser-view.md) -> **Note** +> [!NOTE] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -522,7 +528,8 @@ A `boolean` property that determines whether the window is focusable. A `boolean` property that determines whether the window is visible on all workspaces. -**Note:** Always returns false on Windows. +> [!NOTE] +> Always returns false on Windows. #### `win.shadow` @@ -532,7 +539,8 @@ A `boolean` property that determines whether the window has a shadow. A `boolean` property that determines whether the menu bar should be visible. -**Note:** If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. +> [!NOTE] +> If the menu bar is auto-hide, users can still bring up the menu bar by pressing the single `Alt` key. #### `win.kiosk` @@ -553,7 +561,8 @@ and the icon of the file will show in window's title bar. A `string` property that determines the title of the native window. -**Note:** The title of the web page can be different from the title of the native window. +> [!NOTE] +> The title of the web page can be different from the title of the native window. #### `win.minimizable` _macOS_ _Windows_ @@ -621,8 +630,9 @@ A `boolean` property that indicates whether the window is arranged via [Snap.](h Objects created with `new BrowserWindow` have the following instance methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some methods are only available on specific operating systems and are +> labeled as such. #### `win.destroy()` @@ -704,13 +714,15 @@ Returns `boolean` - Whether the window is minimized. Sets whether the window should be in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. If further actions depend on the fullscreen state, use the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events. #### `win.isFullScreen()` Returns `boolean` - Whether the window is in fullscreen mode. -**Note:** On macOS, fullscreen transitions take place asynchronously. When querying for a BrowserWindow's fullscreen status, you should ensure that either the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events have been emitted. +> [!NOTE] +> On macOS, fullscreen transitions take place asynchronously. When querying for a BrowserWindow's fullscreen status, you should ensure that either the ['enter-full-screen'](browser-window.md#event-enter-full-screen) or ['leave-full-screen'](browser-window.md#event-leave-full-screen) events have been emitted. #### `win.setSimpleFullScreen(flag)` _macOS_ @@ -820,13 +832,15 @@ win.setBounds({ width: 100 }) console.log(win.getBounds()) ``` -**Note:** On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. +> [!NOTE] +> On macOS, the y-coordinate value cannot be smaller than the [Tray](tray.md) height. The tray height has changed over time and depends on the operating system, but is between 20-40px. Passing a value lower than the tray height will result in a window that is flush to the tray. #### `win.getBounds()` Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window as `Object`. -**Note:** On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. +> [!NOTE] +> On macOS, the y-coordinate value returned will be at minimum the [Tray](tray.md) height. For example, calling `win.setBounds({ x: 25, y: 20, width: 800, height: 600 })` with a tray height of 38 means that `win.getBounds()` will return `{ x: 25, y: 38, width: 800, height: 600 }`. #### `win.getBackgroundColor()` @@ -834,7 +848,8 @@ Returns `string` - Gets the background color of the window in Hex (`#RRGGBB`) fo See [Setting `backgroundColor`](#setting-the-backgroundcolor-property). -**Note:** The alpha value is _not_ returned alongside the red, green, and blue values. +> [!NOTE] +> The alpha value is _not_ returned alongside the red, green, and blue values. #### `win.setContentBounds(bounds[, animate])` @@ -852,7 +867,8 @@ Returns [`Rectangle`](structures/rectangle.md) - The `bounds` of the window's cl Returns [`Rectangle`](structures/rectangle.md) - Contains the window bounds of the normal state -**Note:** whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). +> [!NOTE] +> Whatever the current state of the window (maximized, minimized or in fullscreen), this function always returns the position and size of the window in normal state. In normal state, `getBounds` and `getNormalBounds` return the same [`Rectangle`](structures/rectangle.md). #### `win.setEnabled(enable)` @@ -1049,8 +1065,9 @@ Changes the title of native window to `title`. Returns `string` - The title of the native window. -**Note:** The title of the web page can be different from the title of the native -window. +> [!NOTE] +> The title of the web page can be different from the title of the native +> window. #### `win.setSheetOffset(offsetY[, offsetX])` _macOS_ @@ -1409,8 +1426,9 @@ in the taskbar. Sets the properties for the window's taskbar button. -**Note:** `relaunchCommand` and `relaunchDisplayName` must always be set -together. If one of those properties is not set, then neither will be used. +> [!NOTE] +> `relaunchCommand` and `relaunchDisplayName` must always be set +> together. If one of those properties is not set, then neither will be used. #### `win.showDefinitionForSelection()` _macOS_ @@ -1474,13 +1492,15 @@ maximize button, or by dragging it to the edges of the screen. Sets whether the window should be visible on all workspaces. -**Note:** This API does nothing on Windows. +> [!NOTE] +> This API does nothing on Windows. #### `win.isVisibleOnAllWorkspaces()` _macOS_ _Linux_ Returns `boolean` - Whether the window is visible on all workspaces. -**Note:** This API always returns false on Windows. +> [!NOTE] +> This API always returns false on Windows. #### `win.setIgnoreMouseEvents(ignore[, options])` @@ -1601,7 +1621,8 @@ This method sets the browser window's system-drawn background material, includin See the [Windows documentation](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_systembackdrop_type) for more details. -**Note:** This method is only supported on Windows 11 22H2 and up. +> [!NOTE] +> This method is only supported on Windows 11 22H2 and up. #### `win.setWindowButtonPosition(position)` _macOS_ @@ -1623,8 +1644,9 @@ Sets the touchBar layout for the current window. Specifying `null` or `undefined` clears the touch bar. This method only has an effect if the machine has a touch bar. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. #### `win.setBrowserView(browserView)` _Experimental_ _Deprecated_ @@ -1632,7 +1654,7 @@ removed in future Electron releases. If there are other `BrowserView`s attached, they will be removed from this window. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1641,7 +1663,7 @@ this window. Returns `BrowserView | null` - The `BrowserView` attached to `win`. Returns `null` if one is not attached. Throws an error if multiple `BrowserView`s are attached. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1651,7 +1673,7 @@ if one is not attached. Throws an error if multiple `BrowserView`s are attached. Replacement API for setBrowserView supporting work with multi browser views. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1659,7 +1681,7 @@ Replacement API for setBrowserView supporting work with multi browser views. * `browserView` [BrowserView](browser-view.md) -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1670,7 +1692,7 @@ Replacement API for setBrowserView supporting work with multi browser views. Raises `browserView` above other `BrowserView`s attached to `win`. Throws an error if `browserView` is not attached to `win`. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. @@ -1679,7 +1701,7 @@ Throws an error if `browserView` is not attached to `win`. Returns `BrowserView[]` - a sorted by z-index array of all BrowserViews that have been attached with `addBrowserView` or `setBrowserView`. The top-most BrowserView is the last element of the array. -> **Note** +> [!WARNING] > The `BrowserView` class is deprecated, and replaced by the new > [`WebContentsView`](web-contents-view.md) class. diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index c4328e2871720..275a7afe62108 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -18,7 +18,8 @@ console.log(clipboard.readText('selection')) The `clipboard` module has the following methods: -**Note:** Experimental APIs are marked as such and could be removed in future. +> [!NOTE] +> Experimental APIs are marked as such and could be removed in future. ### `clipboard.readText([type])` @@ -141,9 +142,10 @@ bookmark is unavailable. The `title` value will always be empty on Windows. Writes the `title` (macOS only) and `url` into the clipboard as a bookmark. -**Note:** Most apps on Windows don't support pasting bookmarks into them so -you can use `clipboard.write` to write both a bookmark and fallback text to the -clipboard. +> [!NOTE] +> Most apps on Windows don't support pasting bookmarks into them so +> you can use `clipboard.write` to write both a bookmark and fallback text to the +> clipboard. ```js const { clipboard } = require('electron') diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index a07a3eb9523df..dd39f076777c4 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -73,7 +73,8 @@ Passing `--enable-logging=file` will result in logs being saved to the file specified by `--log-file=...`, or to `electron_debug.log` in the user-data directory if `--log-file` is not specified. -> **Note:** On Windows, logs from child processes cannot be sent to stderr. +> [!NOTE] +> On Windows, logs from child processes cannot be sent to stderr. > Logging to a file is the most reliable way to collect logs on Windows. See also `--log-file`, `--log-level`, `--v`, and `--vmodule`. @@ -252,7 +253,8 @@ the required version is unavailable. Current default is set to `3`. Electron supports some of the [CLI flags][node-cli] supported by Node.js. -**Note:** Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect. +> [!NOTE] +> Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect. ### `--inspect-brk\[=\[host:]port]` diff --git a/docs/api/command-line.md b/docs/api/command-line.md index 424c4e9d5921d..373bee8417423 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -25,8 +25,9 @@ document. Append a switch (with optional `value`) to Chromium's command line. -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. ```js const { app } = require('electron') @@ -49,8 +50,9 @@ const { app } = require('electron') app.commandLine.appendArgument('--enable-experimental-web-platform-features') ``` -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. #### `commandLine.hasSwitch(switch)` @@ -84,7 +86,8 @@ const portValue = app.commandLine.getSwitchValue('remote-debugging-port') console.log(portValue) // '8315' ``` -**Note:** When the switch is not present or has no value, it returns empty string. +> [!NOTE] +> When the switch is not present or has no value, it returns empty string. #### `commandLine.removeSwitch(switch)` @@ -102,5 +105,6 @@ app.commandLine.removeSwitch('remote-debugging-port') console.log(app.commandLine.hasSwitch('remote-debugging-port')) // false ``` -**Note:** This will not affect `process.argv`. The intended usage of this function is to -control Chromium's behavior. +> [!NOTE] +> This will not affect `process.argv`. The intended usage of this function is to +> control Chromium's behavior. diff --git a/docs/api/content-tracing.md b/docs/api/content-tracing.md index f4646f2ad3e67..9ccb2c1cbdd0d 100644 --- a/docs/api/content-tracing.md +++ b/docs/api/content-tracing.md @@ -7,8 +7,9 @@ Process: [Main](../glossary.md#main-process) This module does not include a web interface. To view recorded traces, use [trace viewer][], available at `chrome://tracing` in Chrome. -**Note:** You should not use this module until the `ready` event of the app -module is emitted. +> [!NOTE] +> You should not use this module until the `ready` event of the app +> module is emitted. ```js const { app, contentTracing } = require('electron') diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index 26c685f8a48a3..2af51c383617c 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -19,7 +19,8 @@ following projects: * [socorro](https://github.com/mozilla-services/socorro) * [mini-breakpad-server](https://github.com/electron/mini-breakpad-server) -> **Note:** Electron uses Crashpad, not Breakpad, to collect and upload +> [!NOTE] +> Electron uses Crashpad, not Breakpad, to collect and upload > crashes, but for the time being, the [upload protocol is the same](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#Upload-to-collection-server). Or use a 3rd party hosted solution: @@ -84,19 +85,23 @@ before `app.on('ready')`. If the crash reporter is not initialized at the time a renderer process is created, then that renderer process will not be monitored by the crash reporter. -**Note:** You can test out the crash reporter by generating a crash using -`process.crash()`. +> [!NOTE] +> You can test out the crash reporter by generating a crash using +> `process.crash()`. -**Note:** If you need to send additional/updated `extra` parameters after your -first call `start` you can call `addExtraParameter`. +> [!NOTE] +> If you need to send additional/updated `extra` parameters after your +> first call `start` you can call `addExtraParameter`. -**Note:** Parameters passed in `extra`, `globalExtra` or set with -`addExtraParameter` have limits on the length of the keys and values. Key names -must be at most 39 bytes long, and values must be no longer than 127 bytes. -Keys with names longer than the maximum will be silently ignored. Key values -longer than the maximum length will be truncated. +> [!NOTE] +> Parameters passed in `extra`, `globalExtra` or set with +> `addExtraParameter` have limits on the length of the keys and values. Key names +> must be at most 39 bytes long, and values must be no longer than 127 bytes. +> Keys with names longer than the maximum will be silently ignored. Key values +> longer than the maximum length will be truncated. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getLastCrashReport()` @@ -105,7 +110,8 @@ last crash report. Only crash reports that have been uploaded will be returned; even if a crash report is present on disk it will not be returned until it is uploaded. In the case that there are no uploaded reports, `null` is returned. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getUploadedReports()` @@ -114,14 +120,16 @@ Returns [`CrashReport[]`](structures/crash-report.md): Returns all uploaded crash reports. Each report contains the date and uploaded ID. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.getUploadToServer()` Returns `boolean` - Whether reports should be submitted to the server. Set through the `start` method or `setUploadToServer`. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.setUploadToServer(uploadToServer)` @@ -130,7 +138,8 @@ the `start` method or `setUploadToServer`. This would normally be controlled by user preferences. This has no effect if called before `start` is called. -**Note:** This method is only available in the main process. +> [!NOTE] +> This method is only available in the main process. ### `crashReporter.addExtraParameter(key, value)` @@ -148,10 +157,11 @@ with crashes from renderer or other child processes. Similarly, adding extra parameters in a renderer process will not result in those parameters being sent with crashes that occur in other renderer processes or in the main process. -**Note:** Parameters have limits on the length of the keys and values. Key -names must be no longer than 39 bytes, and values must be no longer than 20320 -bytes. Keys with names longer than the maximum will be silently ignored. Key -values longer than the maximum length will be truncated. +> [!NOTE] +> Parameters have limits on the length of the keys and values. Key +> names must be no longer than 39 bytes, and values must be no longer than 20320 +> bytes. Keys with names longer than the maximum will be silently ignored. Key +> values longer than the maximum length will be truncated. ### `crashReporter.removeExtraParameter(key)` diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index 89f6f120062f7..822eabeac8751 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -70,8 +70,9 @@ stopButton.addEventListener('click', () => { See [`navigator.mediaDevices.getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) for more information. -**Note:** `navigator.mediaDevices.getDisplayMedia` does not permit the use of `deviceId` for -selection of a source - see [specification](https://w3c.github.io/mediacapture-screen-share/#constraints). +> [!NOTE] +> `navigator.mediaDevices.getDisplayMedia` does not permit the use of `deviceId` for +> selection of a source - see [specification](https://w3c.github.io/mediacapture-screen-share/#constraints). ## Methods @@ -92,8 +93,9 @@ The `desktopCapturer` module has the following methods: Returns `Promise` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. -**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, -which can detected by [`systemPreferences.getMediaAccessStatus`][]. +> [!NOTE] +> Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, +> which can detected by [`systemPreferences.getMediaAccessStatus`][]. [`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia [`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-windows-macos diff --git a/docs/api/dialog.md b/docs/api/dialog.md index a3a18584e648d..e1c641c724731 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -67,10 +67,11 @@ The `extensions` array should contain extensions without wildcards or dots (e.g. `'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the `'*'` wildcard (no other wildcard is supported). -**Note:** On Windows and Linux an open dialog can not be both a file selector -and a directory selector, so if you set `properties` to -`['openFile', 'openDirectory']` on these platforms, a directory selector will be -shown. +> [!NOTE] +> On Windows and Linux an open dialog can not be both a file selector +> and a directory selector, so if you set `properties` to +> `['openFile', 'openDirectory']` on these platforms, a directory selector will be +> shown. ```js @ts-type={mainWindow:Electron.BaseWindow} dialog.showOpenDialogSync(mainWindow, { @@ -78,10 +79,11 @@ dialog.showOpenDialogSync(mainWindow, { }) ``` -**Note:** On Linux `defaultPath` is not supported when using portal file chooser -dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` -[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) -to force gtk or kde dialogs. +> [!NOTE] +> On Linux `defaultPath` is not supported when using portal file chooser +> dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +> [command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +> to force gtk or kde dialogs. ### `dialog.showOpenDialog([window, ]options)` @@ -139,10 +141,11 @@ The `extensions` array should contain extensions without wildcards or dots (e.g. `'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the `'*'` wildcard (no other wildcard is supported). -**Note:** On Windows and Linux an open dialog can not be both a file selector -and a directory selector, so if you set `properties` to -`['openFile', 'openDirectory']` on these platforms, a directory selector will be -shown. +> [!NOTE] +> On Windows and Linux an open dialog can not be both a file selector +> and a directory selector, so if you set `properties` to +> `['openFile', 'openDirectory']` on these platforms, a directory selector will be +> shown. ```js @ts-type={mainWindow:Electron.BaseWindow} dialog.showOpenDialog(mainWindow, { @@ -155,10 +158,11 @@ dialog.showOpenDialog(mainWindow, { }) ``` -**Note:** On Linux `defaultPath` is not supported when using portal file chooser -dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` -[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) -to force gtk or kde dialogs. +> [!NOTE] +> On Linux `defaultPath` is not supported when using portal file chooser +> dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +> [command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +> to force gtk or kde dialogs. ### `dialog.showSaveDialogSync([window, ]options)` @@ -225,8 +229,9 @@ The `window` argument allows the dialog to attach itself to a parent window, mak The `filters` specifies an array of file types that can be displayed, see `dialog.showOpenDialog` for an example. -**Note:** On macOS, using the asynchronous version is recommended to avoid issues when -expanding and collapsing the dialog. +> [!NOTE] +> On macOS, using the asynchronous version is recommended to avoid issues when +> expanding and collapsing the dialog. ### `dialog.showMessageBoxSync([window, ]options)` diff --git a/docs/api/dock.md b/docs/api/dock.md index 4224694728da9..6bac22e9b9f47 100644 --- a/docs/api/dock.md +++ b/docs/api/dock.md @@ -28,7 +28,8 @@ When `informational` is passed, the dock icon will bounce for one second. However, the request remains active until either the application becomes active or the request is canceled. -**Note:** This method can only be used while the app is not focused; when the app is focused it will return -1. +> [!NOTE] +> This method can only be used while the app is not focused; when the app is focused it will return -1. #### `dock.cancelBounce(id)` _macOS_ diff --git a/docs/api/download-item.md b/docs/api/download-item.md index dc2c0d1a893e4..06d52a44ea803 100644 --- a/docs/api/download-item.md +++ b/docs/api/download-item.md @@ -115,7 +115,8 @@ Returns `boolean` - Whether the download is paused. Resumes the download that has been paused. -**Note:** To enable resumable downloads the server you are downloading from must support range requests and provide both `Last-Modified` and `ETag` header values. Otherwise `resume()` will dismiss previously received bytes and restart the download from the beginning. +> [!NOTE] +> To enable resumable downloads the server you are downloading from must support range requests and provide both `Last-Modified` and `ETag` header values. Otherwise `resume()` will dismiss previously received bytes and restart the download from the beginning. #### `downloadItem.canResume()` @@ -141,9 +142,10 @@ Returns `boolean` - Whether the download has user gesture. Returns `string` - The file name of the download item. -**Note:** The file name is not always the same as the actual one saved in local -disk. If user changes the file name in a prompted download saving dialog, the -actual name of saved file will be different. +> [!NOTE] +> The file name is not always the same as the actual one saved in local +> disk. If user changes the file name in a prompted download saving dialog, the +> actual name of saved file will be different. #### `downloadItem.getCurrentBytesPerSecond()` @@ -172,8 +174,9 @@ header. Returns `string` - The current state. Can be `progressing`, `completed`, `cancelled` or `interrupted`. -**Note:** The following methods are useful specifically to resume a -`cancelled` item when session is restarted. +> [!NOTE] +> The following methods are useful specifically to resume a +> `cancelled` item when session is restarted. #### `downloadItem.getURLChain()` diff --git a/docs/api/extensions-api.md b/docs/api/extensions-api.md index 30add8f9bcd9d..afbb40574e17b 100644 --- a/docs/api/extensions-api.md +++ b/docs/api/extensions-api.md @@ -92,11 +92,13 @@ app.whenReady().then(async () => { This API does not support loading packed (.crx) extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. -**Note:** Loading extensions into in-memory (non-persistent) sessions is not -supported and will throw an error. +> [!NOTE] +> Loading extensions into in-memory (non-persistent) sessions is not +> supported and will throw an error. #### `extensions.removeExtension(extensionId)` @@ -104,8 +106,9 @@ supported and will throw an error. Unloads an extension. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. #### `extensions.getExtension(extensionId)` @@ -113,12 +116,14 @@ is emitted. Returns `Extension | null` - The loaded extension with the given ID. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. #### `extensions.getAllExtensions()` Returns `Extension[]` - A list of all loaded extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. diff --git a/docs/api/extensions.md b/docs/api/extensions.md index 2bc93d499a2d2..43f7ccfb40d8a 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -6,7 +6,8 @@ but it also happens to support some other extension capabilities. [chrome-extensions-api-index]: https://developer.chrome.com/extensions/api_index -> **Note:** Electron does not support arbitrary Chrome extensions from the +> [!NOTE] +> Electron does not support arbitrary Chrome extensions from the > store, and it is a **non-goal** of the Electron project to be perfectly > compatible with Chrome's implementation of Extensions. @@ -160,7 +161,8 @@ The following methods of `chrome.tabs` are supported: - `chrome.tabs.update` (partial support) - supported properties: `url`, `muted`. -> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active +> [!NOTE] +> In Chrome, passing `-1` as a tab ID signifies the "currently active > tab". Since Electron has no such concept, passing `-1` as a tab ID is not > supported and will raise an error. @@ -170,6 +172,7 @@ See [official documentation](https://developer.chrome.com/docs/extensions/refere All features of this API are supported. -> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. +> [!NOTE] +> Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. See [official documentation](https://developer.chrome.com/docs/extensions/reference/webRequest) for more information. diff --git a/docs/api/global-shortcut.md b/docs/api/global-shortcut.md index 142bdbaba1adf..50a5dacdfd246 100644 --- a/docs/api/global-shortcut.md +++ b/docs/api/global-shortcut.md @@ -8,13 +8,13 @@ The `globalShortcut` module can register/unregister a global keyboard shortcut with the operating system so that you can customize the operations for various shortcuts. -**Note:** The shortcut is global; it will work even if the app does -not have the keyboard focus. This module cannot be used before the `ready` -event of the app module is emitted. - -Please also note that it is also possible to use Chromium's -`GlobalShortcutsPortal` implementation, which allows apps to bind global -shortcuts when running within a Wayland session. +> [!NOTE] +> The shortcut is global; it will work even if the app does +> not have the keyboard focus. This module cannot be used before the `ready` +> event of the app module is emitted. +> Please also note that it is also possible to use Chromium's +> `GlobalShortcutsPortal` implementation, which allows apps to bind global +> shortcuts when running within a Wayland session. ```js const { app, globalShortcut } = require('electron') diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index 7f2afc41e7c10..53722a414bbea 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -156,7 +156,7 @@ If you need to transfer a [`MessagePort`][] to the main process, use [`ipcRender If you do not need a response to the message, consider using [`ipcRenderer.send`](#ipcrenderersendchannel-args). -> **Note** +> [!NOTE] > Sending non-standard JavaScript types such as DOM objects or > special Electron objects will throw an exception. > @@ -165,7 +165,7 @@ If you do not need a response to the message, consider using [`ipcRenderer.send` > Electron's IPC to the main process, as the main process would have no way to decode > them. Attempting to send such objects over IPC will result in an error. -> **Note** +> [!NOTE] > If the handler in the main process throws an error, > the promise returned by `invoke` will reject. > However, the `Error` object in the renderer process @@ -195,7 +195,8 @@ throw an exception. The main process handles it by listening for `channel` with [`ipcMain`](./ipc-main.md) module, and replies by setting `event.returnValue`. -> :warning: **WARNING**: Sending a synchronous message will block the whole +> [!WARNING] +> Sending a synchronous message will block the whole > renderer process until the reply is received, so use this method only as a > last resort. It's much better to use the asynchronous version, > [`invoke()`](./ipc-renderer.md#ipcrendererinvokechannel-args). diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 0bc80b2bd37c1..0bf0753012170 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -51,7 +51,8 @@ See [`Menu`](menu.md) for examples. the placement of their containing group after the containing group of the item with the specified id. -**Note:** `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development. +> [!NOTE] +> `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development. ### Roles @@ -125,7 +126,8 @@ When specifying a `role` on macOS, `label` and `accelerator` are the only options that will affect the menu item. All other options will be ignored. Lowercase `role`, e.g. `toggledevtools`, is still supported. -**Note:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS. +> [!NOTE] +> The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS. ### Instance Properties @@ -170,7 +172,8 @@ An `Accelerator` (optional) indicating the item's accelerator, if set. An `Accelerator | null` indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item. -**Note:** This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`. +> [!NOTE] +> This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`. #### `menuItem.icon` diff --git a/docs/api/menu.md b/docs/api/menu.md index fef600f68dd62..477729cede80a 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -32,16 +32,18 @@ In order to escape the `&` character in an item name, add a proceeding `&`. For Passing `null` will suppress the default menu. On Windows and Linux, 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`. +> [!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`. #### `Menu.getApplicationMenu()` Returns `Menu | null` - The application menu, if set, or `null`, if not set. -**Note:** The returned `Menu` instance doesn't support dynamic addition or -removal of menu items. [Instance properties](#instance-properties) can still -be dynamically modified. +> [!NOTE] +> The returned `Menu` instance doesn't support dynamic addition or +> removal of menu items. [Instance properties](#instance-properties) can still +> be dynamically modified. #### `Menu.sendActionToFirstResponder(action)` _macOS_ @@ -119,8 +121,9 @@ Inserts the `menuItem` to the `pos` position of the menu. Objects created with `new Menu` or returned by `Menu.buildFromTemplate` emit the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. +> [!NOTE] +> Some events are only available on specific operating systems and are +> labeled as such. #### Event: 'menu-will-show' diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 58c9dadfd3c01..a867cfca5e2f1 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -134,7 +134,8 @@ Creates an empty `NativeImage` instance. Returns `Promise` - fulfilled with the file's thumbnail preview image, which is a [NativeImage](native-image.md). -Note: The Windows implementation will ignore `size.height` and scale the height according to `size.width`. +> [!NOTE] +> Windows implementation will ignore `size.height` and scale the height according to `size.width`. ### `nativeImage.createFromPath(path)` diff --git a/docs/api/net-log.md b/docs/api/net-log.md index f9b04212c043f..2ae5beabfc9ee 100644 --- a/docs/api/net-log.md +++ b/docs/api/net-log.md @@ -17,8 +17,9 @@ app.whenReady().then(async () => { See [`--log-net-log`](command-line-switches.md#--log-net-logpath) to log network events throughout the app's lifecycle. -**Note:** All methods unless specified can only be used after the `ready` event -of the `app` module gets emitted. +> [!NOTE] +> All methods unless specified can only be used after the `ready` event +> of the `app` module gets emitted. ## Methods diff --git a/docs/api/net.md b/docs/api/net.md index 464c078a54258..2b42e445ff19c 100644 --- a/docs/api/net.md +++ b/docs/api/net.md @@ -117,8 +117,9 @@ protocol.handle('https', (req) => { }) ``` -Note: in the [utility process](../glossary.md#utility-process) custom protocols -are not supported. +> [!NOTE] +> In the [utility process](../glossary.md#utility-process), custom protocols +> are not supported. ### `net.isOnline()` diff --git a/docs/api/power-save-blocker.md b/docs/api/power-save-blocker.md index ee57287258df0..348a6b78907a6 100644 --- a/docs/api/power-save-blocker.md +++ b/docs/api/power-save-blocker.md @@ -33,10 +33,11 @@ Returns `Integer` - The blocker ID that is assigned to this power blocker. Starts preventing the system from entering lower-power mode. Returns an integer identifying the power save blocker. -**Note:** `prevent-display-sleep` has higher precedence over -`prevent-app-suspension`. Only the highest precedence type takes effect. In -other words, `prevent-display-sleep` always takes precedence over -`prevent-app-suspension`. +> [!NOTE] +> `prevent-display-sleep` has higher precedence over +> `prevent-app-suspension`. Only the highest precedence type takes effect. In +> other words, `prevent-display-sleep` always takes precedence over +> `prevent-app-suspension`. For example, an API calling A requests for `prevent-app-suspension`, and another calling B requests for `prevent-display-sleep`. `prevent-display-sleep` diff --git a/docs/api/process.md b/docs/api/process.md index 03a606de9ac19..3c24d5db57985 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -233,7 +233,8 @@ console.log(version) // On Linux -> '4.15.0-45-generic' ``` -**Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. +> [!NOTE] +> It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. ### `process.takeHeapSnapshot(filePath)` diff --git a/docs/api/protocol.md b/docs/api/protocol.md index 1af780827c80b..a14cd01b6250c 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -20,8 +20,9 @@ app.whenReady().then(() => { }) ``` -**Note:** All methods unless specified can only be used after the `ready` event -of the `app` module gets emitted. +> [!NOTE] +> All methods unless specified can only be used after the `ready` event +> of the `app` module gets emitted. ## Using `protocol` with a custom `partition` or `session` @@ -61,8 +62,9 @@ The `protocol` module has the following methods: * `customSchemes` [CustomScheme[]](structures/custom-scheme.md) -**Note:** This method can only be used before the `ready` event of the `app` -module gets emitted and can be called only once. +> [!NOTE] +> This method can only be used before the `ready` event of the `app` +> module gets emitted and can be called only once. Registers the `scheme` as standard, secure, bypasses content security policy for resources, allows registering ServiceWorker, supports fetch API, streaming diff --git a/docs/api/screen.md b/docs/api/screen.md index 4f68a2018b062..ab51ff27861a0 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -9,8 +9,9 @@ module is emitted. `screen` is an [EventEmitter][event-emitter]. -**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM -property, so writing `let { screen } = require('electron')` will not work. +> [!NOTE] +> In the renderer / DevTools, `window.screen` is a reserved DOM +> property, so writing `let { screen } = require('electron')` will not work. An example of creating a window that fills the whole screen: @@ -101,7 +102,8 @@ Returns [`Point`](structures/point.md) The current absolute position of the mouse pointer. -**Note:** The return value is a DIP point, not a screen physical point. +> [!NOTE] +> The return value is a DIP point, not a screen physical point. ### `screen.getPrimaryDisplay()` diff --git a/docs/api/session.md b/docs/api/session.md index 16c382d0c3a96..105dfb5f7673c 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -762,7 +762,8 @@ Preconnects the given number of sockets to an origin. Returns `Promise` - Resolves when all connections are closed. -**Note:** It will terminate / fail all requests currently in flight. +> [!NOTE] +> It will terminate / fail all requests currently in flight. #### `ses.fetch(input[, init])` @@ -1305,8 +1306,9 @@ Initiates a download of the resource at `url`. The API will generate a [DownloadItem](download-item.md) that can be accessed with the [will-download](#event-will-download) event. -**Note:** This does not perform any security checks that relate to a page's origin, -unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl-options). +> [!NOTE] +> This does not perform any security checks that relate to a page's origin, +> unlike [`webContents.downloadURL`](web-contents.md#contentsdownloadurlurl-options). #### `ses.createInterruptedDownload(options)` @@ -1434,7 +1436,8 @@ The built in spellchecker does not automatically detect what language a user is spell checker to correctly check their words you must call this API with an array of language codes. You can get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property. -**Note:** On macOS the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS. +> [!NOTE] +> On macOS, the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS. #### `ses.getSpellCheckerLanguages()` @@ -1442,7 +1445,8 @@ Returns `string[]` - An array of language codes the spellchecker is enabled for. will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this setting with the current OS locale. This setting is persisted across restarts. -**Note:** On macOS the OS spellchecker is used and has its own list of languages. On macOS, this API will return whichever languages have been configured by the OS. +> [!NOTE] +> On macOS, the OS spellchecker is used and has its own list of languages. On macOS, this API will return whichever languages have been configured by the OS. #### `ses.setSpellCheckerDictionaryDownloadURL(url)` @@ -1460,7 +1464,8 @@ If the files present in `hunspell_dictionaries.zip` are available at `https://ex then you should call this api with `ses.setSpellCheckerDictionaryDownloadURL('https://example.com/dictionaries/')`. Please note the trailing slash. The URL to the dictionaries is formed as `${url}${filename}`. -**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS. +> [!NOTE] +> On macOS, the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS. #### `ses.listWordsInSpellCheckerDictionary()` @@ -1474,7 +1479,8 @@ Resolves when the full dictionary is loaded from disk. Returns `boolean` - Whether the word was successfully written to the custom dictionary. This API will not work on non-persistent (in-memory) sessions. -**Note:** On macOS and Windows 10 this word will be written to the OS custom dictionary as well +> [!NOTE] +> On macOS and Windows, this word will be written to the OS custom dictionary as well. #### `ses.removeWordFromSpellCheckerDictionary(word)` @@ -1483,7 +1489,8 @@ will not work on non-persistent (in-memory) sessions. Returns `boolean` - Whether the word was successfully removed from the custom dictionary. This API will not work on non-persistent (in-memory) sessions. -**Note:** On macOS and Windows 10 this word will be removed from the OS custom dictionary as well +> [!NOTE] +> On macOS and Windows, this word will be removed from the OS custom dictionary as well. #### `ses.loadExtension(path[, options])` _Deprecated_ @@ -1526,11 +1533,13 @@ app.whenReady().then(async () => { This API does not support loading packed (.crx) extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. -**Note:** Loading extensions into in-memory (non-persistent) sessions is not -supported and will throw an error. +> [!NOTE] +> Loading extensions into in-memory (non-persistent) sessions is not +> supported and will throw an error. **Deprecated:** Use the new `ses.extensions.loadExtension` API. @@ -1540,8 +1549,9 @@ supported and will throw an error. Unloads an extension. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.removeExtension` API. @@ -1551,8 +1561,9 @@ is emitted. Returns `Extension | null` - The loaded extension with the given ID. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.getExtension` API. @@ -1560,8 +1571,9 @@ is emitted. Returns `Extension[]` - A list of all loaded extensions. -**Note:** This API cannot be called before the `ready` event of the `app` module -is emitted. +> [!NOTE] +> This API cannot be called before the `ready` event of the `app` module +> is emitted. **Deprecated:** Use the new `ses.extensions.getAllExtensions` API. @@ -1599,9 +1611,11 @@ Clears various different types of data. This method clears more types of data and is more thorough than the `clearStorageData` method. -**Note:** Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`. +> [!NOTE] +> Cookies are stored at a broader scope than origins. When removing cookies and filtering by `origins` (or `excludeOrigins`), the cookies will be removed at the [registrable domain](https://url.spec.whatwg.org/#host-registrable-domain) level. For example, clearing cookies for the origin `https://really.specific.origin.example.com/` will end up clearing all cookies for `example.com`. Clearing cookies for the origin `https://my.website.example.co.uk/` will end up clearing all cookies for `example.co.uk`. -**Note:** Clearing cache data will also clear the shared dictionary cache. This means that any dictionaries used for compression may be reloaded after clearing the cache. If you wish to clear the shared dictionary cache but leave other cached data intact, you may want to use the `clearSharedDictionaryCache` method. +> [!NOTE] +> Clearing cache data will also clear the shared dictionary cache. This means that any dictionaries used for compression may be reloaded after clearing the cache. If you wish to clear the shared dictionary cache but leave other cached data intact, you may want to use the `clearSharedDictionaryCache` method. For more information, refer to Chromium's [`BrowsingDataRemover` interface][browsing-data-remover]. diff --git a/docs/api/shell.md b/docs/api/shell.md index 1008b73c164d2..80e720b8f419a 100644 --- a/docs/api/shell.md +++ b/docs/api/shell.md @@ -14,7 +14,8 @@ const { shell } = require('electron') shell.openExternal('https://github.com') ``` -**Note:** While the `shell` module can be used in the renderer process, it will not function in a sandboxed renderer. +> [!WARNING] +> While the `shell` module can be used in the renderer process, it will not function in a sandboxed renderer. ## Methods diff --git a/docs/api/structures/jump-list-category.md b/docs/api/structures/jump-list-category.md index 117483f1a8e5e..ad5747ceed62f 100644 --- a/docs/api/structures/jump-list-category.md +++ b/docs/api/structures/jump-list-category.md @@ -15,11 +15,13 @@ * `items` JumpListItem[] (optional) - Array of [`JumpListItem`](jump-list-item.md) objects if `type` is `tasks` or `custom`, otherwise it should be omitted. -**Note:** If a `JumpListCategory` object has neither the `type` nor the `name` -property set then its `type` is assumed to be `tasks`. If the `name` property -is set but the `type` property is omitted then the `type` is assumed to be -`custom`. +> [!NOTE] +> If a `JumpListCategory` object has neither the `type` nor the `name` +> property set then its `type` is assumed to be `tasks`. If the `name` property +> is set but the `type` property is omitted then the `type` is assumed to be +> `custom`. -**Note:** The maximum length of a Jump List item's `description` property is -260 characters. Beyond this limit, the item will not be added to the Jump -List, nor will it be displayed. +> [!NOTE] +> The maximum length of a Jump List item's `description` property is +> 260 characters. Beyond this limit, the item will not be added to the Jump +> List, nor will it be displayed. diff --git a/docs/api/structures/point.md b/docs/api/structures/point.md index 5b792cea0f9c7..9294dc7db54ba 100644 --- a/docs/api/structures/point.md +++ b/docs/api/structures/point.md @@ -3,6 +3,7 @@ * `x` number * `y` number -**Note:** Both `x` and `y` must be whole integers, when providing a point object -as input to an Electron API we will automatically round your `x` and `y` values -to the nearest whole integer. +> [!NOTE] +> Both `x` and `y` must be whole integers, when providing a point object +> as input to an Electron API we will automatically round your `x` and `y` values +> to the nearest whole integer. diff --git a/docs/api/touch-bar-other-items-proxy.md b/docs/api/touch-bar-other-items-proxy.md index a00cd4b0b1588..efad02d070c70 100644 --- a/docs/api/touch-bar-other-items-proxy.md +++ b/docs/api/touch-bar-other-items-proxy.md @@ -4,8 +4,9 @@ > from Chromium at the space indicated by the proxy. By default, this proxy is added > to each TouchBar at the end of the input. For more information, see the AppKit docs on > [NSTouchBarItemIdentifierOtherItemsProxy](https://developer.apple.com/documentation/appkit/nstouchbaritemidentifierotheritemsproxy) -> -> Note: Only one instance of this class can be added per TouchBar. + +> [!NOTE] +> Only one instance of this class can be added per TouchBar. Process: [Main](../glossary.md#main-process)
_This class is not exported from the `'electron'` module. It is only available as a return value of other methods in the Electron API._ diff --git a/docs/api/touch-bar.md b/docs/api/touch-bar.md index c229430326437..c304f57430832 100644 --- a/docs/api/touch-bar.md +++ b/docs/api/touch-bar.md @@ -15,12 +15,14 @@ Process: [Main](../glossary.md#main-process) Creates a new touch bar with the specified items. Use `BrowserWindow.setTouchBar` to add the `TouchBar` to a window. -**Note:** The TouchBar API is currently experimental and may change or be -removed in future Electron releases. - -**Tip:** If you don't have a MacBook with Touch Bar, you can use -[Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator) -to test Touch Bar usage in your app. +> [!NOTE] +> The TouchBar API is currently experimental and may change or be +> removed in future Electron releases. + +> [!TIP] +> If you don't have a MacBook with Touch Bar, you can use +> [Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator) +> to test Touch Bar usage in your app. ### Static Properties diff --git a/docs/api/tray.md b/docs/api/tray.md index 27c1d948a1246..6b112def35918 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -176,7 +176,8 @@ Returns: Emitted when the mouse is released from clicking the tray icon. -Note: This will not be emitted if you have set a context menu for your Tray using `tray.setContextMenu`, as a result of macOS-level constraints. +> [!NOTE] +> This will not be emitted if you have set a context menu for your Tray using `tray.setContextMenu`, as a result of macOS-level constraints. #### Event: 'mouse-down' _macOS_ diff --git a/docs/api/utility-process.md b/docs/api/utility-process.md index 658c0c8804fba..e6cf1b79bab87 100644 --- a/docs/api/utility-process.md +++ b/docs/api/utility-process.md @@ -44,7 +44,8 @@ Process: [Main](../glossary.md#main-process)
Returns [`UtilityProcess`](utility-process.md#class-utilityprocess) -**Note:** `utilityProcess.fork` can only be called after the `ready` event has been emitted on `App`. +> [!NOTE] +> `utilityProcess.fork` can only be called after the `ready` event has been emitted on `App`. ## Class: UtilityProcess @@ -108,7 +109,8 @@ child.on('exit', () => { }) ``` -**Note:** You can use the `pid` to determine if the process is currently running. +> [!NOTE] +> You can use the `pid` to determine if the process is currently running. #### `child.stdout` diff --git a/docs/api/view.md b/docs/api/view.md index 32ac190d618e6..8e0a23a5d8def 100644 --- a/docs/api/view.md +++ b/docs/api/view.md @@ -94,13 +94,15 @@ Examples of valid `color` values: * Similar to CSS Color Module Level 3 keywords, but case-sensitive. * e.g. `blueviolet` or `red` -**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. +> [!NOTE] +> Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`. #### `view.setBorderRadius(radius)` * `radius` Integer - Border radius size in pixels. -**Note:** The area cutout of the view's border still captures clicks. +> [!NOTE] +> The area cutout of the view's border still captures clicks. #### `view.setVisible(visible)` diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 0dc3cd9dfc7cb..3e89f5e6f7abc 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -463,7 +463,8 @@ win.webContents.on('will-prevent-unload', (event) => { }) ``` -**Note:** This will be emitted for `BrowserViews` but will _not_ be respected - this is because we have chosen not to tie the `BrowserView` lifecycle to its owning BrowserWindow should one exist per the [specification](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event). +> [!NOTE] +> This will be emitted for `BrowserViews` but will _not_ be respected - this is because we have chosen not to tie the `BrowserView` lifecycle to its owning BrowserWindow should one exist per the [specification](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event). #### Event: 'render-process-gone' @@ -1491,7 +1492,8 @@ increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. The formula for this is `scale := 1.2 ^ level`. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. @@ -1508,7 +1510,8 @@ Returns `Promise` Sets the maximum and minimum pinch-to-zoom level. -> **NOTE**: Visual zoom is disabled by default in Electron. To re-enable it, call: +> [!NOTE] +> Visual zoom is disabled by default in Electron. To re-enable it, call: > > ```js > const win = new BrowserWindow() @@ -2076,7 +2079,9 @@ Disable device emulation enabled by `webContents.enableDeviceEmulation`. * `inputEvent` [MouseInputEvent](structures/mouse-input-event.md) | [MouseWheelInputEvent](structures/mouse-wheel-input-event.md) | [KeyboardInputEvent](structures/keyboard-input-event.md) Sends an input `event` to the page. -**Note:** The [`BrowserWindow`](browser-window.md) containing the contents needs to be focused for + +> [!NOTE] +> The [`BrowserWindow`](browser-window.md) containing the contents needs to be focused for `sendInputEvent()` to work. #### `contents.beginFrameSubscription([onlyDirty ,]callback)` @@ -2218,7 +2223,9 @@ By default this value is `{ min: 0, max: 0 }` , which would apply no restriction * `max` Integer - The maximum UDP port number that WebRTC should use. Setting the WebRTC UDP Port Range allows you to restrict the udp port range used by WebRTC. By default the port range is unrestricted. -**Note:** To reset to an unrestricted port range this value should be set to `{ min: 0, max: 0 }`. + +> [!NOTE] +> To reset to an unrestricted port range this value should be set to `{ min: 0, max: 0 }`. #### `contents.getMediaSourceId(requestWebContents)` @@ -2364,8 +2371,9 @@ A [`WebContents`](web-contents.md) instance that might own this `WebContents`. A `WebContents | null` property that represents the of DevTools `WebContents` associated with a given `WebContents`. -**Note:** Users should never store this object because it may become `null` -when the DevTools has been closed. +> [!NOTE] +> Users should never store this object because it may become `null` +> when the DevTools has been closed. #### `contents.debugger` _Readonly_ diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index b0148f4bfe043..301c002bd1401 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -41,7 +41,8 @@ Changes the zoom level to the specified level. The original size is 0 and each increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. @@ -56,13 +57,15 @@ Returns `number` - The current zoom level. Sets the maximum and minimum pinch-to-zoom level. -> **NOTE**: Visual zoom is disabled by default in Electron. To re-enable it, call: +> [!NOTE] +> Visual zoom is disabled by default in Electron. To re-enable it, call: > > ```js > webFrame.setVisualZoomLevelLimits(1, 3) > ``` -> **NOTE**: Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are +> [!NOTE] +> Visual zoom only applies to pinch-to-zoom behavior. Cmd+/-/0 zoom shortcuts are > controlled by the 'zoomIn', 'zoomOut', and 'resetZoom' MenuItem roles in the application > Menu. To disable shortcuts, manually [define the Menu](./menu.md#examples) and omit zoom roles > from the definition. @@ -189,7 +192,9 @@ dispatch errors of isolated worlds to foreign worlds. * `name` string (optional) - Name for isolated world. Useful in devtools. Set the security origin, content security policy and name of the isolated world. -Note: If the `csp` is specified, then the `securityOrigin` also has to be specified. + +> [!NOTE] +> If the `csp` is specified, then the `securityOrigin` also has to be specified. ### `webFrame.getResourceUsage()` diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 14103680f7f4e..385358728a2ed 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -30,8 +30,10 @@ rendered. Unlike an `iframe`, the `webview` runs in a separate process than your app. It doesn't have the same permissions as your web page and all interactions between your app and embedded content will be asynchronous. This keeps your app -safe from the embedded content. **Note:** Most methods called on the -webview from the host page require a synchronous call to the main process. +safe from the embedded content. + +> [!NOTE] +> Most methods called on the webview from the host page require a synchronous call to the main process. ## Example @@ -252,7 +254,8 @@ The full list of supported feature strings can be found in the The `webview` tag has the following methods: -**Note:** The webview element must be loaded before using the methods. +> [!NOTE] +> The webview element must be loaded before using the methods. **Example** @@ -679,7 +682,8 @@ increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively. The formula for this is `scale := 1.2 ^ level`. -> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the +> [!NOTE] +> The zoom policy at the Chromium level is same-origin, meaning that the > zoom level for a specific domain propagates across all instances of windows with > the same domain. Differentiating the window URLs will make zoom work per-window. diff --git a/docs/development/build-instructions-gn.md b/docs/development/build-instructions-gn.md index 64c897b5ea59f..2efd0ba8d9dc8 100644 --- a/docs/development/build-instructions-gn.md +++ b/docs/development/build-instructions-gn.md @@ -155,7 +155,8 @@ $ gn gen out/Release --args="import(\"//electron/build/args/release.gn\")" $ gn gen out/Release --args="import(\`"//electron/build/args/release.gn\`")" ``` -**Note:** This will generate a `out/Testing` or `out/Release` build directory under `src/` with the testing or release build depending upon the configuration passed above. You can replace `Testing|Release` with another names, but it should be a subdirectory of `out`. +> [!NOTE] +> This will generate a `out/Testing` or `out/Release` build directory under `src/` with the testing or release build depending upon the configuration passed above. You can replace `Testing|Release` with another names, but it should be a subdirectory of `out`. Also you shouldn't have to run `gn gen` again—if you want to change the build arguments, you can run `gn args out/Testing` to bring up an editor. To see the list of available build configuration options, run `gn args out/Testing --list`. diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 8d7e7943308a4..0d1a079c5d705 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -39,8 +39,9 @@ Building Electron is done entirely with command-line scripts and cannot be done with Visual Studio. You can develop Electron with any editor but support for building with Visual Studio will come in the future. -**Note:** Even though Visual Studio is not used for building, it's still -**required** because we need the build toolchains it provides. +> [!NOTE] +> Even though Visual Studio is not used for building, it's still +> **required** because we need the build toolchains it provides. ## Exclude source tree from Windows Security diff --git a/docs/development/creating-api.md b/docs/development/creating-api.md index 62af7250c15b0..49f383f33f923 100644 --- a/docs/development/creating-api.md +++ b/docs/development/creating-api.md @@ -148,7 +148,8 @@ In your [`shell/common/node_bindings.cc`](https://github.com/electron/electron/b V(electron_browser_{api_name}) ``` -> Note: More technical details on how Node links with Electron can be found on [our blog](https://www.electronjs.org/blog/electron-internals-using-node-as-a-library#link-node-with-electron). +> [!NOTE] +> More technical details on how Node links with Electron can be found on [our blog](https://www.electronjs.org/blog/electron-internals-using-node-as-a-library#link-node-with-electron). ## Expose your API to TypeScript diff --git a/docs/development/patches.md b/docs/development/patches.md index e7c744f34a551..e32549e1fe7a7 100644 --- a/docs/development/patches.md +++ b/docs/development/patches.md @@ -49,7 +49,8 @@ $ git commit $ ../../electron/script/git-export-patches -o ../../electron/patches/node ``` -> **NOTE**: `git-export-patches` ignores any uncommitted files, so you must create a commit if you want your changes to be exported. The subject line of the commit message will be used to derive the patch file name, and the body of the commit message should include the reason for the patch's existence. +> [!NOTE] +> `git-export-patches` ignores any uncommitted files, so you must create a commit if you want your changes to be exported. The subject line of the commit message will be used to derive the patch file name, and the body of the commit message should include the reason for the patch's existence. Re-exporting patches will sometimes cause shasums in unrelated patches to change. This is generally harmless and can be ignored (but go ahead and add those changes to your PR, it'll stop them from showing up for other people). diff --git a/docs/tutorial/keyboard-shortcuts.md b/docs/tutorial/keyboard-shortcuts.md index 8caa93f65c040..687454c7ae75c 100644 --- a/docs/tutorial/keyboard-shortcuts.md +++ b/docs/tutorial/keyboard-shortcuts.md @@ -133,9 +133,10 @@ function handleKeyPress (event) { window.addEventListener('keyup', handleKeyPress, true) ``` -> Note: the third parameter `true` indicates that the listener will always receive -key presses before other listeners so they can't have `stopPropagation()` -called on them. +> [!NOTE] +> The third parameter `true` indicates that the listener will always receive +> key presses before other listeners so they can't have `stopPropagation()` +> called on them. #### Intercepting events in the main process diff --git a/docs/tutorial/multithreading.md b/docs/tutorial/multithreading.md index ab70b88a502c9..fd0a52205f48b 100644 --- a/docs/tutorial/multithreading.md +++ b/docs/tutorial/multithreading.md @@ -20,7 +20,8 @@ const win = new BrowserWindow({ The `nodeIntegrationInWorker` can be used independent of `nodeIntegration`, but `sandbox` must not be set to `true`. -**Note:** This option is not available in [`SharedWorker`s](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) or [`Service Worker`s](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker) owing to incompatibilities in sandboxing policies. +> [!NOTE] +> This option is not available in [`SharedWorker`s](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) or [`Service Worker`s](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker) owing to incompatibilities in sandboxing policies. ## Available APIs diff --git a/docs/tutorial/online-offline-events.md b/docs/tutorial/online-offline-events.md index 8cb40ebe8389d..eb0f6dac25714 100644 --- a/docs/tutorial/online-offline-events.md +++ b/docs/tutorial/online-offline-events.md @@ -83,4 +83,5 @@ After launching the Electron application, you should see the notification: ![Connection status](../images/connection-status.png) -> Note: If you need to communicate the connection status to the main process, use the [IPC renderer](../api/ipc-renderer.md) API. +> [!NOTE] +> If you need to communicate the connection status to the main process, use the [IPC renderer](../api/ipc-renderer.md) API. diff --git a/docs/tutorial/process-model.md b/docs/tutorial/process-model.md index f9a6348e88971..e8dd209ccec32 100644 --- a/docs/tutorial/process-model.md +++ b/docs/tutorial/process-model.md @@ -64,7 +64,8 @@ const contents = win.webContents console.log(contents) ``` -> Note: A renderer process is also created for [web embeds][web-embed] such as the +> [!NOTE] +> A renderer process is also created for [web embeds][web-embed] such as the > `BrowserView` module. The `webContents` object is also accessible for embedded > web content. diff --git a/docs/tutorial/repl.md b/docs/tutorial/repl.md index 82d9c035e1b3d..fda0c53f077dc 100644 --- a/docs/tutorial/repl.md +++ b/docs/tutorial/repl.md @@ -14,8 +14,9 @@ dependency, you should be able to access the REPL with the following command: ./node_modules/.bin/electron --interactive ``` -**Note:** `electron --interactive` is not available on Windows -(see [electron/electron#5776](https://github.com/electron/electron/pull/5776) for more details). +> [!NOTE] +> `electron --interactive` is not available on Windows +> (see [electron/electron#5776](https://github.com/electron/electron/pull/5776) for more details). ## Renderer process diff --git a/docs/tutorial/web-embeds.md b/docs/tutorial/web-embeds.md index 462b029c03a55..0f9d5b735b8df 100644 --- a/docs/tutorial/web-embeds.md +++ b/docs/tutorial/web-embeds.md @@ -19,12 +19,12 @@ and only allow the capabilities you want to support. ### WebViews -> Important Note: -[we do not recommend you to use WebViews](../api/webview-tag.md#warning), -as this tag undergoes dramatic architectural changes that may affect stability -of your application. Consider switching to alternatives, like `iframe` and -Electron's [`WebContentsView`](../api/web-contents-view.md), or an architecture -that avoids embedded content by design. +> [!IMPORTANT] +> [We do not recommend you to use WebViews](../api/webview-tag.md#warning), +> as this tag undergoes dramatic architectural changes that may affect stability +> of your application. Consider switching to alternatives, like `iframe` and +> Electron's [`WebContentsView`](../api/web-contents-view.md), or an architecture +> that avoids embedded content by design. [WebViews](../api/webview-tag.md) are based on Chromium's WebViews and are not explicitly supported by Electron. We do not guarantee that the WebView API will From bc553af82658fe4215a9126af8f61702673012d8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 15:00:07 -0500 Subject: [PATCH 213/339] fix: white window flicker on window creation (#47053) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window_views.cc | 2 ++ .../win/electron_desktop_window_tree_host_win.cc | 14 ++++++++++++++ .../ui/win/electron_desktop_window_tree_host_win.h | 3 +++ 3 files changed, 19 insertions(+) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 9be657a501a03..9898a4822c81b 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -38,6 +38,7 @@ #include "shell/common/options_switches.h" #include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" +#include "ui/compositor/compositor.h" #include "ui/display/screen.h" #include "ui/gfx/image/image.h" #include "ui/gfx/native_widget_types.h" @@ -1243,6 +1244,7 @@ void NativeWindowViews::SetBackgroundColor(SkColor background_color) { DeleteObject((HBRUSH)previous_brush); InvalidateRect(GetAcceleratedWidget(), nullptr, 1); #endif + GetWidget()->GetCompositor()->SetBackgroundColor(background_color); } void NativeWindowViews::SetHasShadow(bool has_shadow) { diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index c5b0b201071a3..26c0f7a3c9425 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -23,6 +23,20 @@ ElectronDesktopWindowTreeHostWin::ElectronDesktopWindowTreeHostWin( ElectronDesktopWindowTreeHostWin::~ElectronDesktopWindowTreeHostWin() = default; +bool ElectronDesktopWindowTreeHostWin::ShouldUpdateWindowTransparency() const { + // If transparency is updated for an opaque window before widget init is + // completed, the window flickers white before the background color is applied + // and we don't want that. We do, however, want translucent windows to be + // properly transparent, so ensure it gets updated in that case. + if (!widget_init_done_ && !native_window_view_->IsTranslucent()) + return false; + return views::DesktopWindowTreeHostWin::ShouldUpdateWindowTransparency(); +} + +void ElectronDesktopWindowTreeHostWin::OnWidgetInitDone() { + widget_init_done_ = true; +} + bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h index 6abe46d81a836..3afa5ae84f51f 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -31,6 +31,8 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, protected: // views::DesktopWindowTreeHostWin: + void OnWidgetInitDone() override; + bool ShouldUpdateWindowTransparency() const override; bool PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, @@ -51,6 +53,7 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, private: raw_ptr native_window_view_; // weak ref std::optional force_should_paint_as_active_; + bool widget_init_done_ = false; }; } // namespace electron From 748977b1d8e2cd600ca151cd6ca617346960a4d4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 21:18:43 -0500 Subject: [PATCH 214/339] fix: webview crash on focus (#47037) * fix: webview crash on focus Co-authored-by: Shelley Vohr * fixup! fix: webview crash on focus chore: fix .patches shear --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr Co-authored-by: Charles Kerr --- patches/chromium/.patches | 1 + ...ontentsviewchildframe_notimplemented.patch | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 542f273eea961..afe3ceed903a3 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -148,3 +148,4 @@ revert_enable_crel_for_arm32_targets.patch mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch fix_osr_stutter_fix_backport_for_electron.patch do_not_check_the_order_of_display_id_order_on_windows.patch +make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch diff --git a/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch b/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch new file mode 100644 index 0000000000000..8e53a214b87ae --- /dev/null +++ b/patches/chromium/make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Wed, 7 May 2025 05:08:18 -0700 +Subject: Make focus methods in WebContentsViewChildFrame NOTIMPLEMENTED + +Change focus methods in WebContentsViewChildFrame to NOTIMPLEMENTED. +It's possible to for focus to be called on the child frame, e.g. in the +context of chrome.webviewTag, and shouldn't necessarily crash. + +This also fixes an associated crash in Electron, where the NOTREACHED is +hit when PointerLockController::LockPointer calls web_contents->Focus(). + +Change-Id: Ide58aae2187fbdd807be4ec176d13c76e459ba9c +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6508949 +Commit-Queue: Bo Liu +Reviewed-by: Bo Liu +Reviewed-by: Rakina Zata Amni +Cr-Commit-Position: refs/heads/main@{#1456886} + +diff --git a/content/browser/web_contents/web_contents_view_child_frame.cc b/content/browser/web_contents/web_contents_view_child_frame.cc +index b89d4621dc2acc84f7d8c749f34f7f5563543c72..9c206f6ee424fc423d5f772c7559e60ec0df6eef 100644 +--- a/content/browser/web_contents/web_contents_view_child_frame.cc ++++ b/content/browser/web_contents/web_contents_view_child_frame.cc +@@ -6,6 +6,7 @@ + + #include + ++#include "base/notimplemented.h" + #include "build/build_config.h" + #include "content/browser/renderer_host/render_frame_proxy_host.h" + #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" +@@ -160,15 +161,15 @@ void WebContentsViewChildFrame::DestroyBackForwardTransitionAnimationManager() { + } + + void WebContentsViewChildFrame::RestoreFocus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::Focus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::StoreFocus() { +- NOTREACHED(); ++ NOTIMPLEMENTED(); + } + + void WebContentsViewChildFrame::FocusThroughTabTraversal(bool reverse) { From de81d8a4a6bb64e12d07ee7bee99886238331521 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 12:18:10 -0500 Subject: [PATCH 215/339] refactor: use kKeyModifiers in IsAltModifier() (#47090) We probably didn't use this before because IsAltModifier() was written two years before the KeyModifiers mask was added upstream in 98ec378a. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/ui/views/root_view.cc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/shell/browser/ui/views/root_view.cc b/shell/browser/ui/views/root_view.cc index c5fb92868d53c..c38d47ef59ba4 100644 --- a/shell/browser/ui/views/root_view.cc +++ b/shell/browser/ui/views/root_view.cc @@ -20,13 +20,8 @@ bool IsAltKey(const input::NativeWebKeyboardEvent& event) { } bool IsAltModifier(const input::NativeWebKeyboardEvent& event) { - using Modifiers = input::NativeWebKeyboardEvent::Modifiers; - int modifiers = event.GetModifiers(); - modifiers &= ~Modifiers::kNumLockOn; - modifiers &= ~Modifiers::kCapsLockOn; - return (modifiers == Modifiers::kAltKey) || - (modifiers == (Modifiers::kAltKey | Modifiers::kIsLeft)) || - (modifiers == (Modifiers::kAltKey | Modifiers::kIsRight)); + using Mods = input::NativeWebKeyboardEvent::Modifiers; + return (event.GetModifiers() & Mods::kKeyModifiers) == Mods::kAltKey; } } // namespace From 8b2b155c5da3200eab69a339ff56f23d12fa5120 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 12:22:41 -0700 Subject: [PATCH 216/339] feat: enable secondary label for macOS menu (#47040) * feat: enable secondary label for macOS menu Co-authored-by: Michaela Laurencin * Update shell/browser/ui/cocoa/electron_menu_controller.mm Co-authored-by: Robo Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> * fix for lint Co-authored-by: Michaela Laurencin * update docs for sublabel Co-authored-by: Michaela Laurencin --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michaela Laurencin Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> --- docs/api/menu-item.md | 2 +- docs/api/menu.md | 21 +++++++++++++++++++ .../ui/cocoa/electron_menu_controller.mm | 9 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 0bf0753012170..3c53afc222bc6 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -19,7 +19,7 @@ See [`Menu`](menu.md) for examples. * `type` string (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or `radio`. * `label` string (optional) - * `sublabel` string (optional) + * `sublabel` string (optional) _macOS_ - Available in macOS >= 14.4 * `toolTip` string (optional) _macOS_ - Hover text for this menu item. * `accelerator` [Accelerator](accelerator.md) (optional) * `icon` ([NativeImage](native-image.md) | string) (optional) diff --git a/docs/api/menu.md b/docs/api/menu.md index 477729cede80a..c94d8fe70d794 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -332,6 +332,27 @@ name, no matter what label you set. To change it, modify your app bundle's [About Information Property List Files][AboutInformationPropertyListFiles] for more information. +### Menu Sublabels + +Menu sublabels, or [subtitles](https://developer.apple.com/documentation/appkit/nsmenuitem/subtitle?language=objc), can be added to menu items using the `sublabel` option. Below is an example based on the renderer example above: + +```js @ts-expect-error=[12] +// main +ipcMain.on('show-context-menu', (event) => { + const template = [ + { + label: 'Menu Item 1', + sublabel: 'Subtitle 1', + click: () => { event.sender.send('context-menu-command', 'menu-item-1') } + }, + { type: 'separator' }, + { label: 'Menu Item 2', sublabel: 'Subtitle 2', type: 'checkbox', checked: true } + ] + const menu = Menu.buildFromTemplate(template) + menu.popup({ window: BrowserWindow.fromWebContents(event.sender) }) +}) +``` + ## Setting Menu for Specific Browser Window (_Linux_ _Windows_) The [`setMenu` method][setMenu] of browser windows can set the menu of certain diff --git a/shell/browser/ui/cocoa/electron_menu_controller.mm b/shell/browser/ui/cocoa/electron_menu_controller.mm index d23e007dd0bad..71c9d8b49e863 100644 --- a/shell/browser/ui/cocoa/electron_menu_controller.mm +++ b/shell/browser/ui/cocoa/electron_menu_controller.mm @@ -315,12 +315,21 @@ - (NSMenuItem*)menuItemForService:(NSSharingService*)service - (NSMenuItem*)makeMenuItemForIndex:(NSInteger)index fromModel:(electron::ElectronMenuModel*)model { std::u16string label16 = model->GetLabelAt(index); + auto rawSecondaryLabel = model->GetSecondaryLabelAt(index); NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:label action:@selector(itemSelected:) keyEquivalent:@""]; + if (!rawSecondaryLabel.empty()) { + if (@available(macOS 14.4, *)) { + NSString* secondary_label = + l10n_util::FixUpWindowsStyleLabel(rawSecondaryLabel); + item.subtitle = secondary_label; + } + } + // If the menu item has an icon, set it. ui::ImageModel icon = model->GetIconAt(index); if (icon.IsImage()) From 6a0e31633a0f6f771c422094c80cb3c668fb52aa Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 12:23:17 -0700 Subject: [PATCH 217/339] feat: enable innerWidth and innerHeight for window open (#47038) feat: enable innerWidth and innerHeight for window open (#46749) * feat: enable innerWidth and innerHeight for window open * update comment for added special innerWidth and innerHeight * update 100 min spec requirement handling * update testing to include getContentSize * update macOS min requirement handling * adjust refactored consts * update const values from nativewindowviews Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com> --- lib/browser/parse-features-string.ts | 7 +++- shell/browser/native_window_mac.mm | 21 +++++++++-- shell/browser/native_window_views.cc | 23 +++++++++--- shell/common/options_switches.h | 2 ++ spec/chromium-spec.ts | 35 +++++++++++++++++++ .../pages/window-open-size-inner.html | 7 ++++ 6 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 spec/fixtures/pages/window-open-size-inner.html diff --git a/lib/browser/parse-features-string.ts b/lib/browser/parse-features-string.ts index 4d54479c04381..be37505c64a06 100644 --- a/lib/browser/parse-features-string.ts +++ b/lib/browser/parse-features-string.ts @@ -26,7 +26,12 @@ const keysOfTypeNumberCompileTimeCheck: { [K in IntegerBrowserWindowOptionKeys] }; // Note `top` / `left` are special cases from the browser which we later convert // to y / x. -const keysOfTypeNumber = new Set(['top', 'left', ...Object.keys(keysOfTypeNumberCompileTimeCheck)]); +// NOTE(@mlaurencin) `innerWidth` / `innerHeight` are also special cases. The spec +// states that `width` and `height` represent the window content size and are equivalent +// to `innerWidth` / `innerHeight`. However, our implementation currently incorrectly maps +// `width` and `height` to `outerWidth` and `outerHeight`, or the size of the window +// with all border and related window chrome. +const keysOfTypeNumber = new Set(['top', 'left', 'innerWidth', 'innerHeight', ...Object.keys(keysOfTypeNumberCompileTimeCheck)]); /** * Note that we only allow "0" and "1" boolean conversion when the type is known diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 51c1494ab8ff5..8da2b2f36f628 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -120,8 +120,8 @@ static bool FromV8(v8::Isolate* isolate, ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); display::Screen::GetScreen()->AddObserver(this); - const int width = options.ValueOrDefault(options::kWidth, 800); - const int height = options.ValueOrDefault(options::kHeight, 600); + int width = options.ValueOrDefault(options::kWidth, 800); + int height = options.ValueOrDefault(options::kHeight, 600); NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame]; gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2), @@ -283,8 +283,23 @@ static bool FromV8(v8::Isolate* isolate, } // Resize to content bounds. - const bool use_content_size = + // NOTE(@mlaurencin) Spec requirements can be found here: + // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#width + constexpr int kMinSizeReqdBySpec = 100; + int inner_width = 0; + int inner_height = 0; + bool use_content_size = options.ValueOrDefault(options::kUseContentSize, false); + options.Get(options::kinnerWidth, &inner_width); + options.Get(options::kinnerHeight, &inner_height); + if (inner_width || inner_height) { + use_content_size = true; + if (inner_width) + width = std::max(kMinSizeReqdBySpec, inner_width); + if (inner_height) + height = std::max(kMinSizeReqdBySpec, inner_height); + } + if (!has_frame() || use_content_size) SetContentSize(gfx::Size(width, height)); diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 9898a4822c81b..7f92eab063de3 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -265,7 +265,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, const int width = options.ValueOrDefault(options::kWidth, 800); const int height = options.ValueOrDefault(options::kHeight, 600); - const gfx::Rect bounds{0, 0, width, height}; + gfx::Rect bounds{0, 0, width, height}; widget_size_ = bounds.size(); widget()->AddObserver(this); @@ -406,10 +406,25 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, // Default content view. SetContentView(new views::View()); + options.Get(options::kUseContentSize, &use_content_size_); + + // NOTE(@mlaurencin) Spec requirements can be found here: + // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#width + int kMinSizeReqdBySpec = 100; + int inner_width = 0; + int inner_height = 0; + options.Get(options::kinnerWidth, &inner_width); + options.Get(options::kinnerHeight, &inner_height); + if (inner_width || inner_height) { + use_content_size_ = true; + if (inner_width) + bounds.set_width(std::max(kMinSizeReqdBySpec, inner_width)); + if (inner_height) + bounds.set_height(std::max(kMinSizeReqdBySpec, inner_height)); + } + gfx::Size size = bounds.size(); - if (has_frame() && - options.Get(options::kUseContentSize, &use_content_size_) && - use_content_size_) + if (has_frame() && use_content_size_) size = ContentBoundsToWindowBounds(gfx::Rect(size)).size(); widget()->CenterWindow(size); diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 5bb2284acb4e6..7b86b734e5a21 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -26,6 +26,8 @@ inline constexpr std::string_view kMinWidth = "minWidth"; inline constexpr std::string_view kMinHeight = "minHeight"; inline constexpr std::string_view kMaxWidth = "maxWidth"; inline constexpr std::string_view kMaxHeight = "maxHeight"; +inline constexpr std::string_view kinnerWidth = "innerWidth"; +inline constexpr std::string_view kinnerHeight = "innerHeight"; inline constexpr std::string_view kResizable = "resizable"; inline constexpr std::string_view kMovable = "movable"; inline constexpr std::string_view kMinimizable = "minimizable"; diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 7aee5f66f3d90..90cfe8c8b92eb 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -1459,6 +1459,41 @@ describe('chromium features', () => { expect(eventData).to.equal('size: 350 450'); }); + it('window opened with innerWidth option has the same innerWidth', async () => { + const w = new BrowserWindow({ show: false }); + w.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); + const windowUrl = `file://${fixturesPath}/pages/window-open-size-inner.html`; + const windowCreatedPromise = once(app, 'browser-window-created') as Promise<[any, BrowserWindow]>; + const eventDataPromise = w.webContents.executeJavaScript(`(async () => { + const message = new Promise(resolve => window.addEventListener('message', resolve, { once: true })); + b = window.open(${JSON.stringify(windowUrl)}, '', 'show=no,innerWidth=400,height=450'); + const e = await message; + b.close(); + return e.data; + })()`); + const [[, newWindow], eventData] = await Promise.all([windowCreatedPromise, eventDataPromise]); + + expect(newWindow.getContentSize().toString()).to.equal('400,450'); + expect(eventData).to.equal('size: 400 450'); + }); + it('window opened with innerHeight option has the same innerHeight', async () => { + const w = new BrowserWindow({ show: false }); + w.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); + const windowUrl = `file://${fixturesPath}/pages/window-open-size-inner.html`; + const windowCreatedPromise = once(app, 'browser-window-created') as Promise<[any, BrowserWindow]>; + const eventDataPromise = w.webContents.executeJavaScript(`(async () => { + const message = new Promise(resolve => window.addEventListener('message', resolve, {once: true})); + const b = window.open(${JSON.stringify(windowUrl)}, '', 'show=no,width=350,innerHeight=400') + const e = await message; + b.close(); + return e.data; + })()`); + const [[, newWindow], eventData] = await Promise.all([windowCreatedPromise, eventDataPromise]); + + expect(newWindow.getContentSize().toString()).to.equal('350,400'); + expect(eventData).to.equal('size: 350 400'); + }); + it('loads preload script after setting opener to null', async () => { const w = new BrowserWindow({ show: false }); w.webContents.setWindowOpenHandler(() => ({ diff --git a/spec/fixtures/pages/window-open-size-inner.html b/spec/fixtures/pages/window-open-size-inner.html new file mode 100644 index 0000000000000..f06ea6351aa0c --- /dev/null +++ b/spec/fixtures/pages/window-open-size-inner.html @@ -0,0 +1,7 @@ + + + + + From 9c3331ea832c2d9b35a93c3d2ccfc3faa1f83475 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 12:23:45 -0700 Subject: [PATCH 218/339] feat: add support for `--experimental-network-inspection` (#47030) * feat: add support for `--experimental-network-inspection` Co-authored-by: Aman Karmani * docs: fix minor formatting issues visible on both GH[1] and the docs site[2] [1] https://github.com/electron/electron/blob/main/docs/api/command-line-switches.md#nodejs-flags [2] https://www.electronjs.org/docs/latest/api/command-line-switches#--inspect-brkhostport Co-authored-by: Aman Karmani * docs: add entry for new nodejs flag Co-authored-by: Aman Karmani --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Aman Karmani --- docs/api/command-line-switches.md | 10 +++++++--- shell/common/node_bindings.cc | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index dd39f076777c4..8026d9351d375 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -256,7 +256,7 @@ Electron supports some of the [CLI flags][node-cli] supported by Node.js. > [!NOTE] > Passing unsupported command line switches to Electron when it is not running in `ELECTRON_RUN_AS_NODE` will have no effect. -### `--inspect-brk\[=\[host:]port]` +### `--inspect-brk[=[host:]port]` Activate inspector on host:port and break at start of user script. Default host:port is 127.0.0.1:9229. @@ -268,13 +268,13 @@ Activate inspector on `host:port` and break at start of the first internal JavaScript script executed when the inspector is available. Default `host:port` is `127.0.0.1:9229`. -### `--inspect-port=\[host:]port` +### `--inspect-port=[host:]port` Set the `host:port` to be used when the inspector is activated. Useful when activating the inspector by sending the SIGUSR1 signal. Default host is `127.0.0.1`. Aliased to `--debug-port=[host:]port`. -### `--inspect\[=\[host:]port]` +### `--inspect[=[host:]port]` Activate inspector on `host:port`. Default is `127.0.0.1:9229`. @@ -290,6 +290,10 @@ Specify ways of the inspector web socket url exposure. By default inspector websocket url is available in stderr and under /json/list endpoint on `http://host:port/json/list`. +### `--experimental-network-inspector` + +Enable support for devtools network inspector events, for visibility into requests made by the nodejs `http` and `https` modules. + ### `--no-deprecation` Silence deprecation warnings. diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 8db9da3646a42..66d89f7b28e7c 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -348,6 +348,7 @@ bool IsAllowedOption(const std::string_view option) { "--inspect-brk-node", "--inspect-port", "--inspect-publish-uid", + "--experimental-network-inspection", }); // This should be aligned with what's possible to set via the process object. From 12528c6739c28f5900bb388f6b494b0bc161a22f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 17:16:29 -0500 Subject: [PATCH 219/339] docs: add note on DIP and DPI (#47120) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao --- docs/api/screen.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api/screen.md b/docs/api/screen.md index ab51ff27861a0..1710bee4067a1 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -58,6 +58,14 @@ app.whenReady().then(() => { }) ``` +> [!NOTE] +> Screen coordinates used by this module are [`Point`](structures/point.md) structures. +> There are two kinds of coordinates available to the process: +> +> * **Physical screen points** are raw hardware pixels on a display. +> * **Device-independent pixel (DIP) points** are virtualized screen points scaled based on the DPI +> (dots per inch) of the display. + ## Events The `screen` module emits the following events: From 4f9bca79d3a6375b57ace22fe25bbffe6f442377 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 19:38:21 -0500 Subject: [PATCH 220/339] test: fix desktopCapturer mocha syntax (#47114) do not nest `it` calls in desktopCapturer specs Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- spec/api-desktop-capturer-spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/api-desktop-capturer-spec.ts b/spec/api-desktop-capturer-spec.ts index a3ff4df4a3888..af1567efe2a46 100644 --- a/spec/api-desktop-capturer-spec.ts +++ b/spec/api-desktop-capturer-spec.ts @@ -99,7 +99,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt expect(isEmpties.every(e => e === true)).to.be.true(); }); - it('getMediaSourceId should match DesktopCapturerSource.id', async () => { + it('getMediaSourceId should match DesktopCapturerSource.id', async function () { const w = new BrowserWindow({ show: false, width: 100, height: 100, webPreferences: { contextIsolation: false } }); const wShown = once(w, 'show'); const wFocused = once(w, 'focus'); @@ -119,7 +119,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt // bots while it is not on my workstation, as expected, with and without // the --ci parameter. if (process.platform === 'linux' && sources.length === 0) { - it.skip('desktopCapturer.getSources returned an empty source list'); + this.skip(); return; } @@ -130,7 +130,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt expect(mediaSourceId).to.equal(foundSource!.id); }); - it('getSources should not incorrectly duplicate window_id', async () => { + it('getSources should not incorrectly duplicate window_id', async function () { const w = new BrowserWindow({ show: false, width: 100, height: 100, webPreferences: { contextIsolation: false } }); const wShown = once(w, 'show'); const wFocused = once(w, 'focus'); @@ -155,7 +155,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt // bots while it is not on my workstation, as expected, with and without // the --ci parameter. if (process.platform === 'linux' && sources.length === 0) { - it.skip('desktopCapturer.getSources returned an empty source list'); + this.skip(); return; } @@ -180,7 +180,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt expect(w.resizable).to.be.false(); }); - it('moveAbove should move the window at the requested place', async () => { + it('moveAbove should move the window at the requested place', async function () { // DesktopCapturer.getSources() is guaranteed to return in the correct // z-order from foreground to background. const MAX_WIN = 4; @@ -225,7 +225,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt // the --ci parameter. if (process.platform === 'linux' && sources.length === 0) { destroyWindows(); - it.skip('desktopCapturer.getSources returned an empty source list'); + this.skip(); return; } From 6eac8d8af95ca0676c6366cf41d258b46dc8e47b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 19:38:56 -0500 Subject: [PATCH 221/339] refactor: decouple NativeWindowViews and GlobalMenuBarX11 (#47118) The GlobalMenuBar used to hold a raw_ptr reference to its NativeWindow; but since it doesn't use it & only wants the gfx::AcceleratedWidget info, let's remove the NativeWindowViews reference. AFAICT, GlobalMenuBarX11::window_ has never been used Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 3 ++- shell/browser/ui/views/global_menu_bar_x11.cc | 7 ++----- shell/browser/ui/views/global_menu_bar_x11.h | 5 +---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 7f92eab063de3..3366c99fe9491 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1391,7 +1391,8 @@ void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) { .supports_global_application_menus; if (can_use_global_menus && ShouldUseGlobalMenuBar()) { if (!global_menu_bar_) - global_menu_bar_ = std::make_unique(this); + global_menu_bar_ = + std::make_unique(GetAcceleratedWidget()); if (global_menu_bar_->IsServerStarted()) { root_view_.RegisterAcceleratorsWithFocusManager(menu_model); global_menu_bar_->SetMenu(menu_model); diff --git a/shell/browser/ui/views/global_menu_bar_x11.cc b/shell/browser/ui/views/global_menu_bar_x11.cc index c0c997c0da5e3..53e51db0857e1 100644 --- a/shell/browser/ui/views/global_menu_bar_x11.cc +++ b/shell/browser/ui/views/global_menu_bar_x11.cc @@ -9,7 +9,6 @@ #include "base/functional/bind.h" #include "base/strings/utf_string_conversions.h" -#include "shell/browser/native_window_views.h" #include "shell/browser/ui/electron_menu_model.h" #include "shell/browser/ui/views/global_menu_bar_registrar_x11.h" #include "third_party/abseil-cpp/absl/strings/str_format.h" @@ -173,10 +172,8 @@ std::string GetMenuModelStatus(ElectronMenuModel* model) { } // namespace -GlobalMenuBarX11::GlobalMenuBarX11(NativeWindowViews* window) - : window_(window), - xwindow_(static_cast( - window_->GetNativeWindow()->GetHost()->GetAcceleratedWidget())) { +GlobalMenuBarX11::GlobalMenuBarX11(gfx::AcceleratedWidget accelerated_widget) + : xwindow_(static_cast(accelerated_widget)) { EnsureMethodsLoaded(); if (server_new) InitServer(xwindow_); diff --git a/shell/browser/ui/views/global_menu_bar_x11.h b/shell/browser/ui/views/global_menu_bar_x11.h index 893f2cf6a040c..0851822fc3f16 100644 --- a/shell/browser/ui/views/global_menu_bar_x11.h +++ b/shell/browser/ui/views/global_menu_bar_x11.h @@ -22,8 +22,6 @@ class Accelerator; namespace electron { -class NativeWindowViews; - // Controls the Mac style menu bar on Unity. // // Unity has an Apple-like menu bar at the top of the screen that changes @@ -37,7 +35,7 @@ class NativeWindowViews; // from menu models instead, and it is also per-window specific. class GlobalMenuBarX11 { public: - explicit GlobalMenuBarX11(NativeWindowViews* window); + explicit GlobalMenuBarX11(gfx::AcceleratedWidget accelerated_widget); virtual ~GlobalMenuBarX11(); // disable copy @@ -68,7 +66,6 @@ class GlobalMenuBarX11 { void OnItemActivated(DbusmenuMenuitem* item, unsigned int timestamp); void OnSubMenuShow(DbusmenuMenuitem* item); - raw_ptr window_; x11::Window xwindow_; raw_ptr server_ = nullptr; From 747b2f62e0e368e69347973a5582f001cb131496 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 12:17:07 +0900 Subject: [PATCH 222/339] fix: opening package paths as directory on macOS (#47109) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- shell/browser/ui/file_dialog_mac.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index 881054f0445d6..d92461df01c43 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -318,9 +318,10 @@ void ReadDialogPathsWithBookmarks(NSOpenPanel* dialog, BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&is_directory]; - BOOL is_package = - [[NSWorkspace sharedWorkspace] isFilePackageAtPath:path]; - if (!exists || !is_directory || is_package) + BOOL is_package_as_directory = + [[NSWorkspace sharedWorkspace] isFilePackageAtPath:path] && + [dialog treatsFilePackagesAsDirectories]; + if (!exists || !is_directory || !is_package_as_directory) continue; } From ae232703dec15cdaec4ed30287b561104c893418 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 02:32:44 -0500 Subject: [PATCH 223/339] refactor: make TrackableObject::weak_map_id() constexpr (#47116) * refactor: make TrackableObject::weak_map_id() constexpr refactor: make BaseWindow::GetID() inline and constexpr Co-authored-by: Charles Kerr * refactor: make NativeWindow::window_id() constexpr too Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_base_window.cc | 4 ---- shell/browser/api/electron_api_base_window.h | 2 +- shell/browser/native_window.h | 2 +- shell/common/gin_helper/trackable_object.h | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 4140db9e0b630..773d44b7f78c4 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -1155,10 +1155,6 @@ void BaseWindow::SetTitleBarOverlay(const gin_helper::Dictionary& options, } #endif -int32_t BaseWindow::GetID() const { - return weak_map_id(); -} - void BaseWindow::RemoveFromParentChildWindows() { if (parent_window_.IsEmpty()) return; diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 0a44c592781eb..e516bf278d9a6 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -261,7 +261,7 @@ class BaseWindow : public gin_helper::TrackableObject, void SetTitleBarOverlay(const gin_helper::Dictionary& options, gin_helper::Arguments* args); #endif - int32_t GetID() const; + [[nodiscard]] constexpr int32_t GetID() const { return weak_map_id(); } private: // Helpers. diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 8c4618073a028..db46de2fff528 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -404,7 +404,7 @@ class NativeWindow : public base::SupportsUserData, NativeWindow* parent() const { return parent_; } bool is_modal() const { return is_modal_; } - int32_t window_id() const { return window_id_; } + [[nodiscard]] constexpr int32_t window_id() const { return window_id_; } void add_child_window(NativeWindow* child) { child_windows_.push_back(child); diff --git a/shell/common/gin_helper/trackable_object.h b/shell/common/gin_helper/trackable_object.h index aa040aec58891..bedd6352ab2f2 100644 --- a/shell/common/gin_helper/trackable_object.h +++ b/shell/common/gin_helper/trackable_object.h @@ -28,7 +28,7 @@ class TrackableObjectBase : public CleanedUpAtExit { TrackableObjectBase& operator=(const TrackableObjectBase&) = delete; // The ID in weak map. - int32_t weak_map_id() const { return weak_map_id_; } + [[nodiscard]] constexpr int32_t weak_map_id() const { return weak_map_id_; } // Wrap TrackableObject into a class that SupportsUserData. void AttachAsUserData(base::SupportsUserData* wrapped); From 8f908ffce265ee4fd9e4f1ab368682b320b1b3b9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 11:05:26 -0500 Subject: [PATCH 224/339] perf: don't create unused menuitem icons (#47128) GTK >= 3.90.0 removed support for menuitem icons. When Electron is built with GTK >= 3.90.0, our code builds these icons and then throws them away unused. Instead, let's just not build them. Our gtk_util::GdkPixbufFromSkBitmap utility uses BGRAToRGBA and is expensive to call. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/ui/gtk/menu_util.cc | 18 ++++++------------ shell/browser/ui/gtk/menu_util.h | 2 -- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/shell/browser/ui/gtk/menu_util.cc b/shell/browser/ui/gtk/menu_util.cc index 5870a854149df..76bc5862f662a 100644 --- a/shell/browser/ui/gtk/menu_util.cc +++ b/shell/browser/ui/gtk/menu_util.cc @@ -60,29 +60,23 @@ GdkModifierType GetGdkModifierForAccelerator( } // namespace -GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image) { -// GTK4 removed support for image menu items. +GtkWidget* BuildMenuItemWithImage(const std::string& label, + const gfx::Image& icon) { +// GTK4 removed support for menuitem icons. #if GTK_CHECK_VERSION(3, 90, 0) return gtk_menu_item_new_with_mnemonic(label.c_str()); #else G_GNUC_BEGIN_IGNORE_DEPRECATIONS; GtkWidget* menu_item = gtk_image_menu_item_new_with_mnemonic(label.c_str()); + GdkPixbuf* pixbuf = gtk_util::GdkPixbufFromSkBitmap(*icon.ToSkBitmap()); + GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image); + g_object_unref(pixbuf); G_GNUC_END_IGNORE_DEPRECATIONS; return menu_item; #endif } -GtkWidget* BuildMenuItemWithImage(const std::string& label, - const gfx::Image& icon) { - GdkPixbuf* pixbuf = gtk_util::GdkPixbufFromSkBitmap(*icon.ToSkBitmap()); - - GtkWidget* menu_item = - BuildMenuItemWithImage(label, gtk_image_new_from_pixbuf(pixbuf)); - g_object_unref(pixbuf); - return menu_item; -} - GtkWidget* BuildMenuItemWithLabel(const std::string& label) { return gtk_menu_item_new_with_mnemonic(label.c_str()); } diff --git a/shell/browser/ui/gtk/menu_util.h b/shell/browser/ui/gtk/menu_util.h index 1958d9aaf2fd5..11b1bd880dcac 100644 --- a/shell/browser/ui/gtk/menu_util.h +++ b/shell/browser/ui/gtk/menu_util.h @@ -26,8 +26,6 @@ using MenuActivatedCallback = base::RepeatingCallback; // Builds GtkImageMenuItems. GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image); -GtkWidget* BuildMenuItemWithImage(const std::string& label, - const gfx::Image& icon); GtkWidget* BuildMenuItemWithLabel(const std::string& label); ui::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item); From 7d73997d5f6e81ea42d7b1f0bcf31ef726792bfb Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 11:12:15 -0400 Subject: [PATCH 225/339] chore: bump chromium to 136.0.7103.113 (36-x-y) (#47093) * chore: bump chromium in DEPS to 136.0.7103.113 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- patches/chromium/can_create_window.patch | 2 +- patches/chromium/disable_hidden.patch | 4 ++-- ...vert_is_newly_created_to_allow_for_browser_initiated.patch | 2 +- patches/chromium/webview_fullscreen.patch | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DEPS b/DEPS index 4e8a4db8eb763..a53f85c4fc833 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.93', + '136.0.7103.113', 'node_version': 'v22.15.0', 'nan_version': diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index 5ac6345a8f52b..26866d84281ad 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -9,7 +9,7 @@ potentially prevent a window from being created. TODO(loc): this patch is currently broken. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index d9be4e90a7c43a8a7dec829304856daddcc58dc0..f6f98ea0824896e7e0a27e672480660b0671d146 100644 +index 775a132ea523c509433a3a9560fe3a5419dfaa3f..2b99d0b584760ab3f51561c030ca199055bacd26 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -9652,6 +9652,7 @@ void RenderFrameHostImpl::CreateNewWindow( diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index 13007af26e7fe..8f2203169c40f 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -21,10 +21,10 @@ index 573269ba54150d5350e5c3589217c5e7f41d560d..20fcda4eb20459b69247003c51c2a3ed // Prompts should remain open and functional across tab switches. if (!delegate_ || !delegate_->IsWaitingForPointerLockPrompt(this)) { diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h -index c201cff9e5c3b286389a5eb74e1a9ebd86edfef9..949f6a7867758e35c24897add9a4d47a641357b2 100644 +index 3b3eec99c80a8d3081bb0b1b5cfda76c70d8d23c..d4a2affd94a2aa11299222822485e9df84da18fc 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h -@@ -1012,6 +1012,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl +@@ -1016,6 +1016,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Requests a commit and forced redraw in the renderer compositor. void ForceRedrawForTesting(); diff --git a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch index 06b952870f56d..415cf0d435d46 100644 --- a/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch +++ b/patches/chromium/partially_revert_is_newly_created_to_allow_for_browser_initiated.patch @@ -10,7 +10,7 @@ an about:blank check to this area. Ref: https://chromium-review.googlesource.com/c/chromium/src/+/5403876 diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index 80a6556e7b9bebc40ec61cb8b250b931b5d02bc2..b6a527a05d1f1f4286a72ec0efefcfe81288bc7c 100644 +index 9d7b8758cb8f115e5c83f3913c7172f28a4d1fa2..82be480aeec14edb4bdeb761c42652021da741c6 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -809,8 +809,8 @@ void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch( diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch index 44dab18bf4ba5..9c32aa1dc63b6 100644 --- a/patches/chromium/webview_fullscreen.patch +++ b/patches/chromium/webview_fullscreen.patch @@ -15,7 +15,7 @@ Note that we also need to manually update embedder's `api::WebContents::IsFullscreenForTabOrPending` value. diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index f6f98ea0824896e7e0a27e672480660b0671d146..80a6556e7b9bebc40ec61cb8b250b931b5d02bc2 100644 +index 2b99d0b584760ab3f51561c030ca199055bacd26..9d7b8758cb8f115e5c83f3913c7172f28a4d1fa2 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -8762,6 +8762,17 @@ void RenderFrameHostImpl::EnterFullscreen( From 0bd0c54cea9860ffdf240235e4721ed5df41efd1 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 17:22:55 -0500 Subject: [PATCH 226/339] refactor: NativeWindows should prefer widget() over GetWidget() for internal use (#47153) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 6 +++--- shell/browser/native_window_views.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 8460f07e69430..4130208473bfd 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -703,7 +703,7 @@ int NativeWindow::NonClientHitTest(const gfx::Point& point) { // This is to disable dragging in HTML5 full screen mode. // Details: https://github.com/electron/electron/issues/41002 - if (GetWidget()->IsFullscreen()) + if (widget()->IsFullscreen()) return HTNOWHERE; for (auto* provider : draggable_region_providers_) { @@ -743,7 +743,7 @@ void NativeWindow::RemoveBackgroundThrottlingSource( } void NativeWindow::UpdateBackgroundThrottlingState() { - if (!GetWidget() || !GetWidget()->GetCompositor()) { + if (!widget() || !widget()->GetCompositor()) { return; } bool enable_background_throttling = true; @@ -754,7 +754,7 @@ void NativeWindow::UpdateBackgroundThrottlingState() { break; } } - GetWidget()->GetCompositor()->SetBackgroundThrottling( + widget()->GetCompositor()->SetBackgroundThrottling( enable_background_throttling); } diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 3366c99fe9491..bb96f1c1a75cc 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1259,7 +1259,7 @@ void NativeWindowViews::SetBackgroundColor(SkColor background_color) { DeleteObject((HBRUSH)previous_brush); InvalidateRect(GetAcceleratedWidget(), nullptr, 1); #endif - GetWidget()->GetCompositor()->SetBackgroundColor(background_color); + widget()->GetCompositor()->SetBackgroundColor(background_color); } void NativeWindowViews::SetHasShadow(bool has_shadow) { From ef9a67e00b9ad15ea0e81f360bbd2afbfe2a6ae2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 09:51:42 +0200 Subject: [PATCH 227/339] build(dev-deps): update @electron/lint-roller and markdownlint-cli2 (#47146) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- .github/workflows/archaeologist-dig.yml | 2 +- .../pipeline-segment-electron-build.yml | 2 +- .../pipeline-segment-electron-test.yml | 2 +- .markdownlint-cli2.jsonc | 3 +- package.json | 4 +- yarn.lock | 459 +++++++++--------- 6 files changed, 245 insertions(+), 227 deletions(-) diff --git a/.github/workflows/archaeologist-dig.yml b/.github/workflows/archaeologist-dig.yml index f23d7d35c5e33..32563abf96b51 100644 --- a/.github/workflows/archaeologist-dig.yml +++ b/.github/workflows/archaeologist-dig.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Node.js/npm uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x - name: Setting Up Dig Site run: | echo "remote: ${{ github.event.pull_request.head.repo.clone_url }}" diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index a0dcf0688affe..8d2821a61a923 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -104,7 +104,7 @@ jobs: if: ${{ inputs.target-platform == 'macos' }} uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x cache: yarn cache-dependency-path: src/electron/yarn.lock - name: Install Dependencies diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 060f9fbf45fb7..369bb86104703 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -81,7 +81,7 @@ jobs: if: ${{ inputs.target-platform == 'win' }} uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a with: - node-version: 20.11.x + node-version: 20.19.x - name: Add TCC permissions on macOS if: ${{ inputs.target-platform == 'macos' }} run: | diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index aa44ae70b8780..11be56cea63f2 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -1,6 +1,7 @@ { "config": { "extends": "@electron/lint-roller/configs/markdownlint.json", + "descriptive-link-text": false, "link-image-style": { "autolink": false, "shortcut": false @@ -26,6 +27,6 @@ "no-newline-in-links": true }, "customRules": [ - "@electron/lint-roller/markdownlint-rules/" + "./node_modules/@electron/lint-roller/markdownlint-rules/index.mjs" ] } diff --git a/package.json b/package.json index d27cc1b4dd847..709d9147ed438 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@electron/docs-parser": "^2.0.0", "@electron/fiddle-core": "^1.3.4", "@electron/github-app-auth": "^2.2.1", - "@electron/lint-roller": "^2.4.0", + "@electron/lint-roller": "^3.0.0", "@electron/typescript-definitions": "^9.1.2", "@octokit/rest": "^20.0.2", "@primer/octicons": "^10.0.0", @@ -40,7 +40,7 @@ "got": "^11.8.5", "husky": "^8.0.1", "lint-staged": "^10.2.11", - "markdownlint-cli2": "^0.13.0", + "markdownlint-cli2": "^0.18.0", "minimist": "^1.2.8", "null-loader": "^4.0.1", "pre-flight": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index cb940d319816e..67d472fe02555 100644 --- a/yarn.lock +++ b/yarn.lock @@ -263,26 +263,23 @@ "@octokit/auth-app" "^4.0.13" "@octokit/rest" "^19.0.11" -"@electron/lint-roller@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-2.4.0.tgz#67ab5911400ec1e6a842153acc59613a9522d233" - integrity sha512-U1FDBpNxVbu9TlL8O0F9mmaEimINtdr6RB6gGNVm1aBqOvLs579w0k4aqyYqDIV20HHcuWh/287sll6ou8Pfcw== +"@electron/lint-roller@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-3.0.0.tgz#1c1604f9fe87ace82142d8bd25b5c5f0d1e08003" + integrity sha512-YdbWKivSZj+J0yjJhzACU6yXuah0VQMcyijKOaVxX6qG5J/df75oCt/jyuOpRr0HRtz62DaHphEnzGRhTFx9FA== dependencies: "@dsanders11/vscode-markdown-languageservice" "^0.3.0" ajv "^8.16.0" - balanced-match "^2.0.0" - glob "^8.1.0" + balanced-match "^3.0.1" + glob "^10.4.5" hast-util-from-html "^2.0.1" - markdown-it "^13.0.1" - markdownlint-cli "^0.40.0" + markdown-it "^14.1.0" mdast-util-from-markdown "^1.3.0" - minimist "^1.2.8" - rimraf "^4.4.1" standard "^17.0.0" unist-util-visit "^4.1.2" vscode-languageserver "^8.1.0" vscode-languageserver-textdocument "^1.0.8" - vscode-uri "^3.0.7" + vscode-uri "^3.0.8" yaml "^2.4.5" "@electron/typescript-definitions@^9.1.2": @@ -924,6 +921,11 @@ dependencies: "@types/node" "*" +"@types/katex@^0.16.0": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.16.7.tgz#03ab680ab4fa4fbc6cb46ecf987ecad5d8019868" + integrity sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ== + "@types/keyv@*": version "3.1.4" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" @@ -1624,10 +1626,10 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -balanced-match@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" - integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +balanced-match@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-3.0.1.tgz#e854b098724b15076384266497392a271f4a26a0" + integrity sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w== base64-js@^1.3.1: version "1.5.1" @@ -1669,7 +1671,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -1824,6 +1826,11 @@ character-entities-legacy@^2.0.0: resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-2.0.0.tgz#57f4d00974c696e8f74e9f493e7fcb75b44d7ee7" integrity sha512-YwaEtEvWLpFa6Wh3uVLrvirA/ahr9fki/NUd/Bd4OR6EdJ8D22hovYQEOUCBfQfcqnC4IAMGMsHXY1eXgL4ZZA== +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + character-entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.0.tgz#508355fcc8c73893e0909efc1a44d28da2b6fdf3" @@ -1990,10 +1997,10 @@ commander@^5.0.0, commander@^5.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@~12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-12.0.0.tgz#b929db6df8546080adfd004ab215ed48cf6f2592" - integrity sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== compress-brotli@^1.3.8: version "1.3.8" @@ -2115,11 +2122,6 @@ deep-eql@^5.0.1: resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2293,11 +2295,6 @@ entities@^4.4.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -entities@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - env-paths@^2.2.0, env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -2939,6 +2936,17 @@ fast-glob@^3.3.2: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -3172,11 +3180,6 @@ get-stdin@^8.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== -get-stdin@~9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" - integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== - get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -3234,7 +3237,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.0.0, glob@^10.2.2: +glob@^10.0.0, glob@^10.2.2, glob@^10.4.5: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -3258,17 +3261,6 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - glob@^9.2.0: version "9.3.5" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" @@ -3279,17 +3271,6 @@ glob@^9.2.0: minipass "^4.2.4" path-scurry "^1.6.1" -glob@~10.3.12: - version "10.3.12" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" - integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.3.6" - minimatch "^9.0.1" - minipass "^7.0.4" - path-scurry "^1.10.2" - global-agent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" @@ -3328,17 +3309,17 @@ globalthis@^1.0.1, globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@14.0.1: - version "14.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" - integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== +globby@14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.1.0.tgz#138b78e77cf5a8d794e327b15dce80bf1fb0a73e" + integrity sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA== dependencies: "@sindresorhus/merge-streams" "^2.1.0" - fast-glob "^3.3.2" - ignore "^5.2.4" - path-type "^5.0.0" + fast-glob "^3.3.3" + ignore "^7.0.3" + path-type "^6.0.0" slash "^5.1.0" - unicorn-magic "^0.1.0" + unicorn-magic "^0.3.0" gopd@^1.0.1: version "1.0.1" @@ -3556,7 +3537,7 @@ ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.0.0, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4, ignore@~5.3.1: +ignore@^5.0.0, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -3566,6 +3547,11 @@ ignore@^5.3.1: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== +ignore@^7.0.3: + version "7.0.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.4.tgz#a12c70d0f2607c5bf508fb65a40c75f037d7a078" + integrity sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A== + import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3615,11 +3601,6 @@ ini@^4.1.2, ini@^4.1.3: resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.3.tgz#4c359675a6071a46985eb39b14e4a2c0ec98a795" integrity sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg== -ini@~4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.2.tgz#7f646dbd9caea595e61f88ef60bfff8b01f8130a" - integrity sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw== - internal-slot@^1.0.3, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -3958,15 +3939,6 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -jackspeak@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" - integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -4067,10 +4039,10 @@ json5@^2.0.0, json5@^2.1.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@3.2.1, jsonc-parser@~3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" - integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== +jsonc-parser@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== jsonfile@^4.0.0: version "4.0.0" @@ -4088,11 +4060,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonpointer@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" - integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== - jsonwebtoken@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" @@ -4128,6 +4095,13 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +katex@^0.16.0: + version "0.16.22" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.22.tgz#d2b3d66464b1e6d69e6463b28a86ced5a02c5ccd" + integrity sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg== + dependencies: + commander "^8.3.0" + keyv@^4.0.0: version "4.3.1" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.1.tgz#7970672f137d987945821b1a07b524ce5a4edd27" @@ -4164,13 +4138,6 @@ lines-and-columns@^2.0.3: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== -linkify-it@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" - integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== - dependencies: - uc.micro "^1.0.1" - linkify-it@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" @@ -4385,63 +4352,37 @@ markdown-it@14.1.0, markdown-it@^14.1.0: punycode.js "^2.3.1" uc.micro "^2.1.0" -markdown-it@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" - integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== - dependencies: - argparse "^2.0.1" - entities "~3.0.1" - linkify-it "^4.0.1" - mdurl "^1.0.1" - uc.micro "^1.0.5" - -markdownlint-cli2-formatter-default@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz#81e26b0a50409c0357c6f0d38d8246946b236fab" - integrity sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg== +markdownlint-cli2-formatter-default@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.5.tgz#b8fde4e127f9a9c0596e6d45eed352dd0aa0ff98" + integrity sha512-4XKTwQ5m1+Txo2kuQ3Jgpo/KmnG+X90dWt4acufg6HVGadTUG5hzHF/wssp9b5MBYOMCnZ9RMPaU//uHsszF8Q== -markdownlint-cli2@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.13.0.tgz#691cab01994295b4b8c87aa0485c0b1e0f792289" - integrity sha512-Pg4nF7HlopU97ZXtrcVISWp3bdsuc5M0zXyLp2/sJv2zEMlInrau0ZKK482fQURzVezJzWBpNmu4u6vGAhij+g== +markdownlint-cli2@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/markdownlint-cli2/-/markdownlint-cli2-0.18.0.tgz#eb8007f8f276399197c65966d3428e777a9ecbf3" + integrity sha512-gHvff1KxBxTqaN1F5cTxRSxBipx+Qkki430tyg0wPxty67iQNZzxREZkXy8ltbj7ObMz1eYD4aspnYXfV0sHAw== dependencies: - globby "14.0.1" + globby "14.1.0" js-yaml "4.1.0" - jsonc-parser "3.2.1" - markdownlint "0.34.0" - markdownlint-cli2-formatter-default "0.0.4" - micromatch "4.0.5" - -markdownlint-cli@^0.40.0: - version "0.40.0" - resolved "https://registry.yarnpkg.com/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz#57678cabd543c654d2ea88f752e9ac058b31c207" - integrity sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA== - dependencies: - commander "~12.0.0" - get-stdin "~9.0.0" - glob "~10.3.12" - ignore "~5.3.1" - js-yaml "^4.1.0" - jsonc-parser "~3.2.1" - jsonpointer "5.0.1" - markdownlint "~0.34.0" - minimatch "~9.0.4" - run-con "~1.3.2" - toml "~3.0.0" - -markdownlint-micromark@0.1.9: - version "0.1.9" - resolved "https://registry.yarnpkg.com/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz#4876996b60d4dceb3a02f4eee2d3a366eb9569fa" - integrity sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA== - -markdownlint@0.34.0, markdownlint@~0.34.0: - version "0.34.0" - resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.34.0.tgz#bbc2047c952d1644269009a69ba227ed597b23fa" - integrity sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw== - dependencies: + jsonc-parser "3.3.1" markdown-it "14.1.0" - markdownlint-micromark "0.1.9" + markdownlint "0.38.0" + markdownlint-cli2-formatter-default "0.0.5" + micromatch "4.0.8" + +markdownlint@0.38.0: + version "0.38.0" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.38.0.tgz#862ca9d08f3a28f4149bd388ac369bb95865534e" + integrity sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ== + dependencies: + micromark "4.0.2" + micromark-core-commonmark "2.0.3" + micromark-extension-directive "4.0.0" + micromark-extension-gfm-autolink-literal "2.1.0" + micromark-extension-gfm-footnote "2.1.0" + micromark-extension-gfm-table "2.1.1" + micromark-extension-math "3.1.0" + micromark-util-types "2.0.2" matcher-collection@^1.0.0: version "1.1.2" @@ -4542,11 +4483,6 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= - mdurl@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" @@ -4570,6 +4506,28 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +micromark-core-commonmark@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-core-commonmark@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.0.tgz#b767fa7687c205c224175bf067796360a3830350" @@ -4613,6 +4571,67 @@ micromark-core-commonmark@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-extension-directive@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz#af389e33fe0654c15f8466b73a0f5af598d00368" + integrity sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + parse-entities "^4.0.0" + +micromark-extension-gfm-autolink-literal@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-footnote@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-table@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz#fac70bcbf51fe65f5f44033118d39be8a9b5940b" + integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-math@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz#c42ee3b1dd5a9a03584e83dd8f08e3de510212c1" + integrity sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg== + dependencies: + "@types/katex" "^0.16.0" + devlop "^1.0.0" + katex "^0.16.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-destination@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e" @@ -4899,6 +4918,11 @@ micromark-util-symbol@^2.0.0: resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== +micromark-util-types@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + micromark-util-types@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.0.tgz#0ebdfaea3fa7c15fc82b1e06ea1ef0152d0fb2f0" @@ -4909,6 +4933,29 @@ micromark-util-types@^2.0.0: resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== +micromark@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.3.tgz#4c9f76fce8ba68eddf8730bb4fee2041d699d5b7" @@ -4954,15 +5001,7 @@ micromark@^4.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromatch@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@4.0.8, micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -5014,13 +5053,6 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022" - integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g== - dependencies: - brace-expansion "^2.0.1" - minimatch@^8.0.2: version "8.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" @@ -5035,13 +5067,6 @@ minimatch@^9.0.0, minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.1, minimatch@~9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== - dependencies: - brace-expansion "^2.0.1" - minimatch@~3.0.4: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" @@ -5081,7 +5106,7 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": version "7.1.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.0.tgz#b545f84af94e567386770159302ca113469c80b8" integrity sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig== @@ -5471,6 +5496,19 @@ parse-entities@^3.0.0: is-decimal "^2.0.0" is-hexadecimal "^2.0.0" +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== + dependencies: + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + parse-gitignore@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/parse-gitignore/-/parse-gitignore-0.4.0.tgz#abf702e4b900524fff7902b683862857b63f93fe" @@ -5545,14 +5583,6 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" - integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry@^1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" @@ -5574,10 +5604,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path-type@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" - integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== +path-type@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-6.0.0.tgz#2f1bb6791a91ce99194caede5d6c5920ed81eb51" + integrity sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ== pathval@^2.0.0: version "2.0.0" @@ -6556,16 +6586,6 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" -run-con@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/run-con/-/run-con-1.3.2.tgz#755860a10ce326a96b509485fcea50b4d03754e8" - integrity sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg== - dependencies: - deep-extend "^0.6.0" - ini "~4.1.0" - minimist "^1.2.8" - strip-json-comments "~3.1.1" - run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -7042,7 +7062,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7073,7 +7100,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -7229,11 +7256,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toml@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -7414,11 +7436,6 @@ typescript@^5.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== -uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" @@ -7439,10 +7456,10 @@ undici-types@~6.19.2: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -unicorn-magic@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" - integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== unified-args@^11.0.0: version "11.0.1" @@ -7809,10 +7826,10 @@ vscode-uri@^3.0.3: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.6.tgz#5e6e2e1a4170543af30151b561a41f71db1d6f91" integrity sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ== -vscode-uri@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" - integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== +vscode-uri@^3.0.8: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" + integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== walk-sync@^0.3.2: version "0.3.4" From 577ea02d642d2fc7e96cbefa6d96a98bbb45417d Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 13:32:45 +0200 Subject: [PATCH 228/339] chore: bump node to v22.15.1 (36-x-y) (#47103) * chore: bump node in DEPS to v22.15.1 * chore: fixup patch indices * src: fix error handling on async crypto operations https://github.com/nodejs-private/node-private/pull/709 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- DEPS | 2 +- ...g_fileexists_fn_to_legacymainresolve.patch | 2 +- .../fix_crypto_tests_to_run_with_bssl.patch | 27 ++++++++++++++++--- ...ingssl_and_openssl_incompatibilities.patch | 4 +-- ...ted_fields_of_fastapicallbackoptions.patch | 2 +- .../node/support_v8_sandboxed_pointers.patch | 4 +-- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/DEPS b/DEPS index a53f85c4fc833..885c430b1f8d0 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.113', 'node_version': - 'v22.15.0', + 'v22.15.1', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch index 72074233f7954..6ca8291f2b3e6 100644 --- a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch +++ b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch @@ -53,7 +53,7 @@ index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..03082f0ec4f91382933eec48e77331cd const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? packageConfig.main || './' : ''; diff --git a/src/node_file.cc b/src/node_file.cc -index a492b3aa113c4e1d50cc42721bd5eb58380a6264..c7915e2c8161a5d0fa05ccce368ce9c44345c05d 100644 +index 49816349d8bab37fea1d84e5326ee5a11acad7a2..5aea65d6800494def62829432ee48f9c06e65d63 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3237,13 +3237,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { diff --git a/patches/node/fix_crypto_tests_to_run_with_bssl.patch b/patches/node/fix_crypto_tests_to_run_with_bssl.patch index ce3b8f49c39a5..5e27975c22151 100644 --- a/patches/node/fix_crypto_tests_to_run_with_bssl.patch +++ b/patches/node/fix_crypto_tests_to_run_with_bssl.patch @@ -46,10 +46,10 @@ index d033cd204b3200cdd736b581abe027d6e46e4ff3..73fec107a36c3db4af6f492137d0ca17 largeBuffer.toString('utf8', threshold, threshold + 20); +} diff --git a/test/parallel/test-crypto-async-sign-verify.js b/test/parallel/test-crypto-async-sign-verify.js -index 4e3c32fdcd23fbe3e74bd5e624b739d224689f33..29149838ca76986928c7649a5f60a0f5e22a0705 100644 +index b35dd08e6c49796418cd9d10eb5cc9d02b39961e..a49fdde82ea4cbadd60307cdc99439be892ef5a6 100644 --- a/test/parallel/test-crypto-async-sign-verify.js +++ b/test/parallel/test-crypto-async-sign-verify.js -@@ -88,6 +88,7 @@ test('rsa_public.pem', 'rsa_private.pem', 'sha256', false, +@@ -89,6 +89,7 @@ test('rsa_public.pem', 'rsa_private.pem', 'sha256', false, // ED25519 test('ed25519_public.pem', 'ed25519_private.pem', undefined, true); // ED448 @@ -57,7 +57,7 @@ index 4e3c32fdcd23fbe3e74bd5e624b739d224689f33..29149838ca76986928c7649a5f60a0f5 test('ed448_public.pem', 'ed448_private.pem', undefined, true); // ECDSA w/ der signature encoding -@@ -109,6 +110,7 @@ test('dsa_public.pem', 'dsa_private.pem', 'sha256', +@@ -110,6 +111,7 @@ test('dsa_public.pem', 'dsa_private.pem', 'sha256', // DSA w/ ieee-p1363 signature encoding test('dsa_public.pem', 'dsa_private.pem', 'sha256', false, { dsaEncoding: 'ieee-p1363' }); @@ -65,6 +65,27 @@ index 4e3c32fdcd23fbe3e74bd5e624b739d224689f33..29149838ca76986928c7649a5f60a0f5 // Test Parallel Execution w/ KeyObject is threadsafe in openssl3 { +@@ -150,8 +152,10 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc= + const data = crypto.randomBytes(32); + const signature = crypto.randomBytes(16); + +- const expected = hasOpenSSL3 ? +- /operation not supported for this keytype/ : /no default digest/; ++ let expected = /no default digest/; ++ if (hasOpenSSL3 || common.openSSLIsBoringSSL) { ++ expected = /operation[\s_]not[\s_]supported[\s_]for[\s_]this[\s_]keytype/i; ++ } + + crypto.verify(undefined, data, untrustedKey, signature, common.mustCall((err) => { + assert.ok(err); +@@ -165,6 +169,6 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc= + }); + crypto.sign('sha512', 'message', privateKey, common.mustCall((err) => { + assert.ok(err); +- assert.match(err.message, /digest too big for rsa key/); ++ assert.match(err.message, /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i); + })); + } diff --git a/test/parallel/test-crypto-certificate.js b/test/parallel/test-crypto-certificate.js index 4a5f1f149fe6c739f7f1d2ee17df6e61a942d621..b3287f428ce6b3fde11d449c601a57ff5e3843f9 100644 --- a/test/parallel/test-crypto-certificate.js diff --git a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch index 3d8bea2f74f95..8548df12be7ec 100644 --- a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch +++ b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch @@ -274,7 +274,7 @@ index a054e4c1285208c9ba8b9679c284f459f1ace690..3de8ef4fafcdbdc2cb0ce31de162663d X509_STORE_add_cert(sc->GetCertStoreOwnedByThisSecureContext(), ca); diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc -index 7041eb985d9f6d163098a94342aec976cb6c2bb9..5387d9625a28bb7d11f7f0f05a5f07d1fee2c216 100644 +index c26a88b395abfc645da56231635b36fb23c8fa09..f23cedf4f2449d8edc9a8de1b70332e75d693cdd 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -7,7 +7,9 @@ @@ -428,7 +428,7 @@ index b38a9a377738fd5fe6cc89c3a27c403bf6a97715..0cd43c2005b431e180b7483cb89825a7 void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo& args) { diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc -index cb96698aa644c3b6c506c0979910f2b4421d63ad..b9b21329199b49c9e41f9ae708296e5b0edb39b0 100644 +index 78f2093d1d010be6f9c492662f4f582657ff6a13..b6aef7fd27cd974697bcee05955bfd9ccf4d5837 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -143,7 +143,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( diff --git a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch index 1e68d7e6b68f3..250c9daa8f16a 100644 --- a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch +++ b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch @@ -40,7 +40,7 @@ index 0f0cde7be431dcb80c5314b1a9da49886c436d1c..f6d2bd439cad8b9f91c9d9a6cdb302e6 } HistogramBase* histogram; diff --git a/src/node_file.cc b/src/node_file.cc -index c7915e2c8161a5d0fa05ccce368ce9c44345c05d..23347379328794467a497c86cbae0b428b7ba791 100644 +index 5aea65d6800494def62829432ee48f9c06e65d63..80cf6648ed99241eea8c176c5c958d76fe33aa14 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1071,13 +1071,8 @@ static int32_t FastInternalModuleStat( diff --git a/patches/node/support_v8_sandboxed_pointers.patch b/patches/node/support_v8_sandboxed_pointers.patch index f909667671ec9..6a3edccfab578 100644 --- a/patches/node/support_v8_sandboxed_pointers.patch +++ b/patches/node/support_v8_sandboxed_pointers.patch @@ -26,7 +26,7 @@ index 88c2c932a6b045317c83c911b1cd8267b60d9334..7f4f233b26425493a58ce71dfc0c3a92 void* ret; if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc -index 5387d9625a28bb7d11f7f0f05a5f07d1fee2c216..1b3b8c7b70073926f8dbf02759c2e1af5d938679 100644 +index f23cedf4f2449d8edc9a8de1b70332e75d693cdd..976653dd1e9363e046788fc3419a9b649ceb2ea4 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -55,13 +55,32 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const { @@ -143,7 +143,7 @@ index 6346f8f7199cf7b7d3736c59571606fff102fbb6..7eea2eaefcad5780663a6b87985925ae void SecureHeapUsed(const FunctionCallbackInfo& args) { #ifndef OPENSSL_IS_BORINGSSL diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h -index b85c8daeb464097c2e93bdc7ffdfcfe16b76234b..470a0c4adadcd092dd0105c384e87917ac6fe69a 100644 +index 1592134716da2de40de4ba028ee937b765423e37..8f3ba65f1fef2c066d6df6087a08ba71100d1090 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -242,7 +242,7 @@ class ByteSource { From 6350b7535fee4a7b28e7f5ebf410fdd2dd849284 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 11:56:19 -0500 Subject: [PATCH 229/339] fix: prevent gc monitor 2nd pass crash (#47163) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: reito --- shell/common/gin_converters/osr_converter.cc | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/shell/common/gin_converters/osr_converter.cc b/shell/common/gin_converters/osr_converter.cc index afa868ae8a526..2cdb2bb0633f1 100644 --- a/shell/common/gin_converters/osr_converter.cc +++ b/shell/common/gin_converters/osr_converter.cc @@ -143,19 +143,21 @@ v8::Local Converter::ToV8( // texture, output it in second pass callback. data.SetSecondPassCallback([](const v8::WeakCallbackInfo< OffscreenReleaseHolderMonitor>& data) { - auto* iso = data.GetIsolate(); // Emit warning only once static std::once_flag flag; std::call_once(flag, [=] { - electron::util::EmitWarning( - iso, - "[OSR TEXTURE LEAKED] When using OSR with " - "`useSharedTexture`, `texture.release()` " - "must be called explicitly as soon as the texture is " - "copied to your rendering system. " - "Otherwise, it will soon drain the underlying " - "framebuffer and prevent future frames from being generated.", - "SharedTextureOSRNotReleased"); + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce([] { + electron::util::EmitWarning( + "Offscreen rendering shared texture was garbage " + "collected before calling `release()`. When using OSR " + "with `useSharedTexture: true`, `texture.release()` " + "must be called explicitly as soon as the texture is " + "copied to your rendering system. Otherwise, it will " + "soon drain the underlying frame pool and prevent " + "future frames from being sent.", + "OSRSharedTextureNotReleased"); + })); }); }); } From ef6a71a0c6184ef94d18d6ce92fe1f4cdc430f07 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 20:56:04 +0200 Subject: [PATCH 230/339] build: update_depot_tools on initial install (#47166) this ensures that python is setup for proper use from depot_tools Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/install-build-tools/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index a897b6480e886..5890e46e45d50 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -15,6 +15,7 @@ runs: fi export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c npm i -g @electron/build-tools + e d update_depot_tools e auto-update disable e d auto-update disable if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then From cadaaaf714c77627daa438645a28c02a20741567 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 21:02:35 -0500 Subject: [PATCH 231/339] refactor: make `NativeWindow::has_client_frame_` const (#47179) * refactor: make NativeWindow::has_client_frame_ const Co-authored-by: Charles Kerr * refactor: make NativeWindow::has_client_frame_ const but not constexpr Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 27 ++++++++++++++++----------- shell/browser/native_window.h | 7 +++++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 4130208473bfd..c0f4b92c44779 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -123,17 +123,6 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options, if (parent) options.Get("modal", &is_modal_); -#if defined(USE_OZONE) - // Ozone X11 likes to prefer custom frames, but we don't need them unless - // on Wayland. - if (base::FeatureList::IsEnabled(features::kWaylandWindowDecorations) && - !ui::OzonePlatform::GetInstance() - ->GetPlatformRuntimeProperties() - .supports_server_side_window_decorations) { - has_client_frame_ = true; - } -#endif - WindowList::AddWindow(this); } @@ -814,6 +803,22 @@ bool NativeWindow::IsTranslucent() const { return false; } +// static +bool NativeWindow::PlatformHasClientFrame() { +#if defined(USE_OZONE) + // Ozone X11 likes to prefer custom frames, + // but we don't need them unless on Wayland. + static const bool has_client_frame = + base::FeatureList::IsEnabled(features::kWaylandWindowDecorations) && + !ui::OzonePlatform::GetInstance() + ->GetPlatformRuntimeProperties() + .supports_server_side_window_decorations; + return has_client_frame; +#else + return false; +#endif +} + // static void NativeWindowRelay::CreateForWebContents( content::WebContents* web_contents, diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index db46de2fff528..d1dc5f42eef39 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -397,7 +397,8 @@ class NativeWindow : public base::SupportsUserData, bool has_frame() const { return has_frame_; } - bool has_client_frame() const { return has_client_frame_; } + [[nodiscard]] bool has_client_frame() const { return has_client_frame_; } + bool transparent() const { return transparent_; } bool enable_larger_than_screen() const { return enable_larger_than_screen_; } @@ -468,6 +469,8 @@ class NativeWindow : public base::SupportsUserData, std::list child_windows_; private: + static bool PlatformHasClientFrame(); + std::unique_ptr widget_; static inline int32_t next_id_ = 0; @@ -486,7 +489,7 @@ class NativeWindow : public base::SupportsUserData, // Whether window has standard frame, but it's drawn by Electron (the client // application) instead of the OS. Currently only has meaning on Linux for // Wayland hosts. - bool has_client_frame_ = false; + const bool has_client_frame_ = PlatformHasClientFrame(); // Whether window is transparent. bool transparent_ = false; From 6dea52b6c0147123fb55b07f44c788facd99ad5d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 10:27:11 +0200 Subject: [PATCH 232/339] refactor: make `NativeWindow::pending_transitions_` a `base::queue` (#47182) refactor: make NativeWindow::pending_transitions a base::queue Follow the base/containers/README.md advice that "Chromium code should always use `base::circular_deque` or `base::queue` in preference to `std::deque` or `std::queue` due to memory usage and platform variation." Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index d1dc5f42eef39..c6699ca389626 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -8,11 +8,11 @@ #include #include #include -#include #include #include #include +#include "base/containers/queue.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" @@ -462,7 +462,8 @@ class NativeWindow : public base::SupportsUserData, // on HiDPI displays on some environments. std::optional content_size_constraints_; - std::queue pending_transitions_; + base::queue pending_transitions_; + FullScreenTransitionType fullscreen_transition_type_ = FullScreenTransitionType::kNone; From a4dfd9b6f3aceed098b8ac1d9e6f22a718788727 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 22 May 2025 01:57:32 +0900 Subject: [PATCH 233/339] feat: support dip <-> screen conversion on Linux (#47125) * feat: support dip <-> screen conversion on Linux * chore: fix build --- BUILD.gn | 2 + docs/api/base-window.md | 2 +- docs/api/browser-window.md | 2 +- docs/api/screen.md | 9 ++++- shell/browser/api/electron_api_screen.cc | 51 +++++++++++++++++++++++- shell/browser/api/electron_api_screen.h | 4 ++ shell/browser/linux/x11_util.cc | 17 ++++++++ shell/browser/linux/x11_util.h | 14 +++++++ shell/browser/native_window_views.cc | 30 ++++++-------- 9 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 shell/browser/linux/x11_util.cc create mode 100644 shell/browser/linux/x11_util.h diff --git a/BUILD.gn b/BUILD.gn index a64f6f57154b0..1e5061a0c53b5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -670,6 +670,8 @@ source_set("electron_lib") { sources += [ "shell/browser/certificate_manager_model.cc", "shell/browser/certificate_manager_model.h", + "shell/browser/linux/x11_util.cc", + "shell/browser/linux/x11_util.h", "shell/browser/ui/gtk_util.cc", "shell/browser/ui/gtk_util.h", ] diff --git a/docs/api/base-window.md b/docs/api/base-window.md index d59cdb286731d..52e6deb288f8c 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -362,7 +362,7 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. -To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). ### Static Methods diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index bb45135c16009..e61faa458fe26 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -441,7 +441,7 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. -To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). ### Static Methods diff --git a/docs/api/screen.md b/docs/api/screen.md index 1710bee4067a1..c3bc334887eeb 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -134,7 +134,7 @@ Returns [`Display`](structures/display.md) - The display nearest the specified p Returns [`Display`](structures/display.md) - The display that most closely intersects the provided bounds. -### `screen.screenToDipPoint(point)` _Windows_ +### `screen.screenToDipPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -143,7 +143,10 @@ Returns [`Point`](structures/point.md) Converts a screen physical point to a screen DIP point. The DPI scale is performed relative to the display containing the physical point. -### `screen.dipToScreenPoint(point)` _Windows_ +Not currently supported on Wayland - if used there it will return the point passed +in with no changes. + +### `screen.dipToScreenPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -152,6 +155,8 @@ Returns [`Point`](structures/point.md) Converts a screen DIP point to a screen physical point. The DPI scale is performed relative to the display containing the DIP point. +Not currently supported on Wayland. + ### `screen.screenToDipRect(window, rect)` _Windows_ * `window` [BrowserWindow](browser-window.md) | null diff --git a/shell/browser/api/electron_api_screen.cc b/shell/browser/api/electron_api_screen.cc index 1f8fa4828f755..ef6d03e4b2164 100644 --- a/shell/browser/api/electron_api_screen.cc +++ b/shell/browser/api/electron_api_screen.cc @@ -20,11 +20,18 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector2d_conversions.h" #if BUILDFLAG(IS_WIN) #include "ui/display/win/screen_win.h" #endif +#if BUILDFLAG(IS_LINUX) +#include "shell/browser/linux/x11_util.h" +#endif + #if defined(USE_OZONE) #include "ui/ozone/public/ozone_platform.h" #endif @@ -126,6 +133,44 @@ void Screen::OnDisplayMetricsChanged(const display::Display& display, MetricsToArray(changed_metrics))); } +gfx::PointF Screen::ScreenToDIPPoint(const gfx::PointF& point_px) { +#if BUILDFLAG(IS_WIN) + return display::win::ScreenWin::ScreenToDIPPoint(point_px); +#elif BUILDFLAG(IS_LINUX) + if (x11_util::IsX11()) { + gfx::Point pt_px = gfx::ToFlooredPoint(point_px); + display::Display display = GetDisplayNearestPoint(pt_px); + gfx::Vector2d delta_px = pt_px - display.native_origin(); + gfx::Vector2dF delta_dip = + gfx::ScaleVector2d(delta_px, 1.0 / display.device_scale_factor()); + return gfx::PointF(display.bounds().origin()) + delta_dip; + } else { + return point_px; + } +#else + return point_px; +#endif +} + +gfx::Point Screen::DIPToScreenPoint(const gfx::Point& point_dip) { +#if BUILDFLAG(IS_WIN) + return display::win::ScreenWin::DIPToScreenPoint(point_dip); +#elif BUILDFLAG(IS_LINUX) + if (x11_util::IsX11()) { + display::Display display = GetDisplayNearestPoint(point_dip); + gfx::Rect bounds_dip = display.bounds(); + gfx::Vector2d delta_dip = point_dip - bounds_dip.origin(); + gfx::Vector2d delta_px = gfx::ToFlooredVector2d( + gfx::ScaleVector2d(delta_dip, display.device_scale_factor())); + return display.native_origin() + delta_px; + } else { + return point_dip; + } +#else + return point_dip; +#endif +} + // static v8::Local Screen::Create(gin_helper::ErrorThrower error_thrower) { if (!Browser::Get()->is_ready()) { @@ -153,9 +198,11 @@ gin::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder( .SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay) .SetMethod("getAllDisplays", &Screen::GetAllDisplays) .SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint) +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) + .SetMethod("screenToDipPoint", &Screen::ScreenToDIPPoint) + .SetMethod("dipToScreenPoint", &Screen::DIPToScreenPoint) +#endif #if BUILDFLAG(IS_WIN) - .SetMethod("screenToDipPoint", &display::win::ScreenWin::ScreenToDIPPoint) - .SetMethod("dipToScreenPoint", &display::win::ScreenWin::DIPToScreenPoint) .SetMethod("screenToDipRect", &ScreenToDIPRect) .SetMethod("dipToScreenRect", &DIPToScreenRect) #endif diff --git a/shell/browser/api/electron_api_screen.h b/shell/browser/api/electron_api_screen.h index cd5d7f5b5d25c..4b63cd8858890 100644 --- a/shell/browser/api/electron_api_screen.h +++ b/shell/browser/api/electron_api_screen.h @@ -15,6 +15,7 @@ namespace gfx { class Point; +class PointF; class Rect; class Screen; } // namespace gfx @@ -58,6 +59,9 @@ class Screen final : public gin::Wrappable, return screen_->GetDisplayMatching(match_rect); } + gfx::PointF ScreenToDIPPoint(const gfx::PointF& point_px); + gfx::Point DIPToScreenPoint(const gfx::Point& point_dip); + // display::DisplayObserver: void OnDisplayAdded(const display::Display& new_display) override; void OnDisplaysRemoved(const display::Displays& removed_displays) override; diff --git a/shell/browser/linux/x11_util.cc b/shell/browser/linux/x11_util.cc new file mode 100644 index 0000000000000..38a33fe36d15a --- /dev/null +++ b/shell/browser/linux/x11_util.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2025 Microsoft GmbH. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/linux/x11_util.h" + +#include "ui/ozone/public/ozone_platform.h" + +namespace x11_util { + +bool IsX11() { + return ui::OzonePlatform::GetInstance() + ->GetPlatformProperties() + .electron_can_call_x11; +} + +} // namespace x11_util diff --git a/shell/browser/linux/x11_util.h b/shell/browser/linux/x11_util.h new file mode 100644 index 0000000000000..246d1b2aa2f5b --- /dev/null +++ b/shell/browser/linux/x11_util.h @@ -0,0 +1,14 @@ +// Copyright (c) 2025 Microsoft GmbH. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ +#define ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ + +namespace x11_util { + +bool IsX11(); + +} // namespace x11_util + +#endif // ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index bb96f1c1a75cc..2fae07f9cb16f 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -55,6 +55,7 @@ #include "base/strings/string_util.h" #include "shell/browser/browser.h" #include "shell/browser/linux/unity_service.h" +#include "shell/browser/linux/x11_util.h" #include "shell/browser/ui/electron_desktop_window_tree_host_linux.h" #include "shell/browser/ui/views/client_frame_view_linux.h" #include "shell/browser/ui/views/native_frame_view.h" @@ -164,13 +165,6 @@ gfx::Size WindowSizeToContentSizeBuggy(HWND hwnd, const gfx::Size& size) { #endif -[[maybe_unused, nodiscard]] bool IsX11() { - static const bool is_x11 = ui::OzonePlatform::GetInstance() - ->GetPlatformProperties() - .electron_can_call_x11; - return is_x11; -} - class NativeWindowClientView : public views::ClientView { public: NativeWindowClientView(views::Widget* widget, @@ -328,7 +322,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, if (parent) SetParentWindow(parent); - if (IsX11()) { + if (x11_util::IsX11()) { // Before the window is mapped the SetWMSpecState can not work, so we have // to manually set the _NET_WM_STATE. std::vector state_atom_list; @@ -479,7 +473,7 @@ NativeWindowViews::~NativeWindowViews() { void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) { #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { const std::string color = use_dark_theme ? "dark" : "light"; auto* connection = x11::Connection::Get(); connection->SetStringProperty( @@ -545,7 +539,7 @@ void NativeWindowViews::Show() { // On X11, setting Z order before showing the window doesn't take effect, // so we have to call it again. - if (IsX11()) + if (x11_util::IsX11()) widget()->SetZOrderLevel(widget()->GetZOrderLevel()); #endif } @@ -561,7 +555,7 @@ void NativeWindowViews::ShowInactive() { // On X11, setting Z order before showing the window doesn't take effect, // so we have to call it again. - if (IsX11()) + if (x11_util::IsX11()) widget()->SetZOrderLevel(widget()->GetZOrderLevel()); #endif } @@ -606,7 +600,7 @@ bool NativeWindowViews::IsEnabled() const { #if BUILDFLAG(IS_WIN) return ::IsWindowEnabled(GetAcceleratedWidget()); #elif BUILDFLAG(IS_LINUX) - if (IsX11()) + if (x11_util::IsX11()) return !event_disabler_.get(); NOTIMPLEMENTED(); return true; @@ -643,7 +637,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { #if BUILDFLAG(IS_WIN) ::EnableWindow(GetAcceleratedWidget(), enable); #else - if (IsX11()) { + if (x11_util::IsX11()) { views::DesktopWindowTreeHostPlatform* tree_host = views::DesktopWindowTreeHostLinux::GetHostForWidget( GetAcceleratedWidget()); @@ -975,7 +969,7 @@ bool NativeWindowViews::MoveAbove(const std::string& sourceId) { 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); #else - if (IsX11()) { + if (x11_util::IsX11()) { if (!IsWindowValid(static_cast(id.id))) return false; @@ -997,7 +991,7 @@ void NativeWindowViews::MoveTop() { size.width(), size.height(), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); #else - if (IsX11()) + if (x11_util::IsX11()) electron::MoveWindowToForeground( static_cast(GetAcceleratedWidget())); #endif @@ -1312,7 +1306,7 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { SetForwardMouseMessages(forward); } #else - if (IsX11()) { + if (x11_util::IsX11()) { auto* connection = x11::Connection::Get(); if (ignore) { x11::Rectangle r{0, 0, 1, 1}; @@ -1434,7 +1428,7 @@ void NativeWindowViews::SetParentWindow(NativeWindow* parent) { NativeWindow::SetParentWindow(parent); #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { auto* connection = x11::Connection::Get(); connection->SetProperty( static_cast(GetAcceleratedWidget()), @@ -1574,7 +1568,7 @@ bool NativeWindowViews::IsVisibleOnAllWorkspaces() const { return view_native_widget->IsVisibleOnAllWorkspaces(); #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { // Use the presence/absence of _NET_WM_STATE_STICKY in _NET_WM_STATE to // determine whether the current window is visible on all workspaces. x11::Atom sticky_atom = x11::GetAtom("_NET_WM_STATE_STICKY"); From 2d71d65415a55c9559e6e07f7db8f468ccd5137d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 15:49:06 -0500 Subject: [PATCH 234/339] refactor: prefer `base::circular_deque` over `std::deque` (#47193) * refactor: use base::circular_deque in ResolveProxyHelper Co-authored-by: Charles Kerr * refactor: use base::circular_deque in GetExtraCrashKeys() refactor: reduce visibility of kMaxCrashKeyValueSize This change is to match Chromium's usage advice from base/containers/README.md: `base:circular_deque` is preferred over `std::deque` to provide consistent performance across platforms. Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/net/resolve_proxy_helper.h | 4 ++-- shell/common/crash_keys.cc | 20 +++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/shell/browser/net/resolve_proxy_helper.h b/shell/browser/net/resolve_proxy_helper.h index 20c998be26cfd..8ea5b7ccb3dc9 100644 --- a/shell/browser/net/resolve_proxy_helper.h +++ b/shell/browser/net/resolve_proxy_helper.h @@ -5,10 +5,10 @@ #ifndef ELECTRON_SHELL_BROWSER_NET_RESOLVE_PROXY_HELPER_H_ #define ELECTRON_SHELL_BROWSER_NET_RESOLVE_PROXY_HELPER_H_ -#include #include #include +#include "base/containers/circular_deque.h" #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -66,7 +66,7 @@ class ResolveProxyHelper // Self-reference. Owned as long as there's an outstanding proxy lookup. scoped_refptr owned_self_; - std::deque pending_requests_; + base::circular_deque pending_requests_; // Receiver for the currently in-progress request, if any. mojo::Receiver receiver_{this}; diff --git a/shell/common/crash_keys.cc b/shell/common/crash_keys.cc index 3066ac5f01c82..a9232caf0b39b 100644 --- a/shell/common/crash_keys.cc +++ b/shell/common/crash_keys.cc @@ -5,11 +5,11 @@ #include "shell/common/crash_keys.h" #include -#include #include #include #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,19 +28,17 @@ namespace electron::crash_keys { namespace { -constexpr size_t kMaxCrashKeyValueSize = 20320; -static_assert(kMaxCrashKeyValueSize < crashpad::Annotation::kValueMaxSize, - "max crash key value length above what crashpad supports"); - -using ExtraCrashKeys = - std::deque>; -ExtraCrashKeys& GetExtraCrashKeys() { - static base::NoDestructor extra_keys; +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; + static base::NoDestructor> extra_keys; return *extra_keys; } -std::deque& GetExtraCrashKeyNames() { - static base::NoDestructor> crash_key_names; +auto& GetExtraCrashKeyNames() { + static base::NoDestructor> crash_key_names; return *crash_key_names; } From 15ff33a92c4662021a0a45ae0de55f592f6b7482 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 15:50:00 -0500 Subject: [PATCH 235/339] refactor: make `NativeWindow::transparent_` const (#47199) * refactor: use in-class member initialization for NativeWindow::widget_ Co-authored-by: Charles Kerr * refactor: make NativeWindow::transparent_ const refactor: make NativeWindow::enable_larger_than_screen_ const Co-authored-by: Charles Kerr * chore: make linter happy after rebase Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 7 ++++--- shell/browser/native_window.h | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index c0f4b92c44779..8f87141bf9940 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -93,10 +93,11 @@ gfx::Size GetExpandedWindowSize(const NativeWindow* window, gfx::Size size) { NativeWindow::NativeWindow(const gin_helper::Dictionary& options, NativeWindow* parent) - : widget_(std::make_unique()), parent_(parent) { + : transparent_{options.ValueOrDefault(options::kTransparent, false)}, + enable_larger_than_screen_{ + options.ValueOrDefault(options::kEnableLargerThanScreen, false)}, + parent_{parent} { options.Get(options::kFrame, &has_frame_); - options.Get(options::kTransparent, &transparent_); - options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_); options.Get(options::kTitleBarStyle, &title_bar_style_); #if BUILDFLAG(IS_WIN) options.Get(options::kBackgroundMaterial, &background_material_); diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index c6699ca389626..34b7cdf959fa9 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -399,8 +399,11 @@ class NativeWindow : public base::SupportsUserData, [[nodiscard]] bool has_client_frame() const { return has_client_frame_; } - bool transparent() const { return transparent_; } - bool enable_larger_than_screen() const { return enable_larger_than_screen_; } + [[nodiscard]] bool transparent() const { return transparent_; } + + [[nodiscard]] bool enable_larger_than_screen() const { + return enable_larger_than_screen_; + } NativeWindow* parent() const { return parent_; } bool is_modal() const { return is_modal_; } @@ -472,11 +475,17 @@ class NativeWindow : public base::SupportsUserData, private: static bool PlatformHasClientFrame(); - std::unique_ptr widget_; + std::unique_ptr widget_ = std::make_unique(); static inline int32_t next_id_ = 0; const int32_t window_id_ = ++next_id_; + // Whether window is transparent. + const bool transparent_; + + // Whether window can be resized larger than screen. + const bool enable_larger_than_screen_; + // The content view, weak ref. raw_ptr content_view_ = nullptr; @@ -492,12 +501,6 @@ class NativeWindow : public base::SupportsUserData, // Wayland hosts. const bool has_client_frame_ = PlatformHasClientFrame(); - // Whether window is transparent. - bool transparent_ = false; - - // Whether window can be resized larger than screen. - bool enable_larger_than_screen_ = false; - // The windows has been closed. bool is_closed_ = false; From a4c32b1f3d777e1d39d5d8b855912dfbc846227e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 22 May 2025 01:13:27 -0500 Subject: [PATCH 236/339] chore: remove deps_add_v8_object_setinternalfieldfornodecore.patch (36-x-y) (#47195) --- patches/v8/.patches | 1 - ...8_object_setinternalfieldfornodecore.patch | 87 ------------------- 2 files changed, 88 deletions(-) delete mode 100644 patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch diff --git a/patches/v8/.patches b/patches/v8/.patches index 25d88a0b459c7..199780cede8b0 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,3 +1,2 @@ chore_allow_customizing_microtask_policy_per_context.patch -deps_add_v8_object_setinternalfieldfornodecore.patch enable_--perf-prof_flag_on_macos.patch diff --git a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch b/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch deleted file mode 100644 index 8abe837f5bd5d..0000000000000 --- a/patches/v8/deps_add_v8_object_setinternalfieldfornodecore.patch +++ /dev/null @@ -1,87 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Tue, 14 Nov 2023 17:48:11 +0100 -Subject: deps: add v8::Object::SetInternalFieldForNodeCore() - -This is a non-ABI breaking solution added by Node.js in v20.x for: - -* https://chromium-review.googlesource.com/c/v8/v8/+/4827307 -* https://chromium-review.googlesource.com/c/v8/v8/+/4707972 - -which are necessary for backporting the vm-related memory fixes in https://github.com/nodejs/node/pull/48510. - -diff --git a/include/v8-object.h b/include/v8-object.h -index 3e57ae8efe33f326ef0e5d609c311d4be5b8afd6..dc521d39c2280dfc3217e97c1e413b2be9b4f7ff 100644 ---- a/include/v8-object.h -+++ b/include/v8-object.h -@@ -22,6 +22,8 @@ class Function; - class FunctionTemplate; - template - class PropertyCallbackInfo; -+class Module; -+class UnboundScript; - - /** - * A private symbol -@@ -532,6 +534,21 @@ class V8_EXPORT Object : public Value { - index); - } - -+ /** -+ * Warning: These are Node.js-specific extentions used to avoid breaking -+ * changes in Node.js v20.x. They do not exist in V8 upstream and will -+ * not exist in Node.js v21.x. Node.js embedders and addon authors should -+ * not use them from v20.x. -+ */ -+#ifndef NODE_WANT_INTERNALS -+ V8_DEPRECATED("This extention should only be used by Node.js core") -+#endif -+ void SetInternalFieldForNodeCore(int index, Local value); -+#ifndef NODE_WANT_INTERNALS -+ V8_DEPRECATED("This extention should only be used by Node.js core") -+#endif -+ void SetInternalFieldForNodeCore(int index, Local value); -+ - /** Same as above, but works for TracedReference. */ - V8_INLINE static void* GetAlignedPointerFromInternalField( - const BasicTracedReference& object, int index) { -diff --git a/src/api/api.cc b/src/api/api.cc -index 545c1093ee7544da3878637e11c557e3964b8835..f80a7af94a9f2459a96976543f6117fb41f1aabb 100644 ---- a/src/api/api.cc -+++ b/src/api/api.cc -@@ -6324,14 +6324,33 @@ Local v8::Object::SlowGetInternalField(int index) { - i::Cast(*obj)->GetEmbedderField(index), isolate)); - } - --void v8::Object::SetInternalField(int index, v8::Local value) { -- auto obj = Utils::OpenDirectHandle(this); -+template -+void SetInternalFieldImpl(v8::Object* receiver, int index, v8::Local value) { -+ auto obj = Utils::OpenDirectHandle(receiver); - const char* location = "v8::Object::SetInternalField()"; - if (!InternalFieldOK(obj, index, location)) return; - auto val = Utils::OpenDirectHandle(*value); - i::Cast(obj)->SetEmbedderField(index, *val); - } - -+void v8::Object::SetInternalField(int index, v8::Local value) { -+ SetInternalFieldImpl(this, index, value); -+} -+ -+/** -+ * These are Node.js-specific extentions used to avoid breaking changes in -+ * Node.js v20.x. -+ */ -+void v8::Object::SetInternalFieldForNodeCore(int index, -+ v8::Local value) { -+ SetInternalFieldImpl(this, index, value); -+} -+ -+void v8::Object::SetInternalFieldForNodeCore(int index, -+ v8::Local value) { -+ SetInternalFieldImpl(this, index, value); -+} -+ - void* v8::Object::SlowGetAlignedPointerFromInternalField(v8::Isolate* isolate, - int index) { - auto obj = Utils::OpenDirectHandle(this); From 0755f426c6288666e40c27d61e5cea6f804c676d Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 12:37:09 +0200 Subject: [PATCH 237/339] fix: remove extra 'suspend'/'resume' handling from `powerMonitor` (#47189) fix: remove extra 'suspend'/'resume' handling from powerMonitor Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../api/electron_api_power_monitor_mac.mm | 23 ------------------- .../api/electron_api_power_monitor_win.cc | 12 ---------- 2 files changed, 35 deletions(-) diff --git a/shell/browser/api/electron_api_power_monitor_mac.mm b/shell/browser/api/electron_api_power_monitor_mac.mm index 347d0d17d7bc2..76801e71c0abb 100644 --- a/shell/browser/api/electron_api_power_monitor_mac.mm +++ b/shell/browser/api/electron_api_power_monitor_mac.mm @@ -34,17 +34,6 @@ - (id)init { selector:@selector(onScreenUnlocked:) name:@"com.apple.screenIsUnlocked" object:nil]; - // A notification that the workspace posts before the machine goes to sleep. - [distributed_center addObserver:self - selector:@selector(isSuspending:) - name:NSWorkspaceWillSleepNotification - object:nil]; - // A notification that the workspace posts when the machine wakes from - // sleep. - [distributed_center addObserver:self - selector:@selector(isResuming:) - name:NSWorkspaceDidWakeNotification - object:nil]; NSNotificationCenter* shared_center = [[NSWorkspace sharedWorkspace] notificationCenter]; @@ -73,18 +62,6 @@ - (void)addEmitter:(electron::api::PowerMonitor*)monitor_ { self->emitters.push_back(monitor_); } -- (void)isSuspending:(NSNotification*)notify { - for (auto* emitter : self->emitters) { - emitter->Emit("suspend"); - } -} - -- (void)isResuming:(NSNotification*)notify { - for (auto* emitter : self->emitters) { - emitter->Emit("resume"); - } -} - - (void)onScreenLocked:(NSNotification*)notification { for (auto* emitter : self->emitters) { emitter->Emit("lock-screen"); diff --git a/shell/browser/api/electron_api_power_monitor_win.cc b/shell/browser/api/electron_api_power_monitor_win.cc index 7668234bb3ef4..40da9224527c2 100644 --- a/shell/browser/api/electron_api_power_monitor_win.cc +++ b/shell/browser/api/electron_api_power_monitor_win.cc @@ -88,18 +88,6 @@ LRESULT CALLBACK PowerMonitor::WndProc(HWND hwnd, base::Unretained(this))); } } - } else if (message == WM_POWERBROADCAST) { - if (wparam == PBT_APMRESUMEAUTOMATIC) { - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce([](PowerMonitor* pm) { pm->Emit("resume"); }, - base::Unretained(this))); - } else if (wparam == PBT_APMSUSPEND) { - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce([](PowerMonitor* pm) { pm->Emit("suspend"); }, - base::Unretained(this))); - } } return ::DefWindowProc(hwnd, message, wparam, lparam); } From 5d098dfc8224c50b090a2a49c807f3be2fa8326a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 14:39:50 +0200 Subject: [PATCH 238/339] build: fix depot tool pathing on Windows (#47204) build: properly set depot_tools pathing for Windows Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/install-build-tools/action.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index 5890e46e45d50..12cffbc835519 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -15,12 +15,16 @@ runs: fi export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c npm i -g @electron/build-tools + # Update depot_tools to ensure python e d update_depot_tools e auto-update disable + # Disable further updates of depot_tools e d auto-update disable if [ "$(expr substr $(uname -s) 1 10)" == "MSYS_NT-10" ]; then e d cipd.bat --version cp "C:\Python311\python.exe" "C:\Python311\python3.exe" - fi - echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH - echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH + echo "C:\Users\ContainerAdministrator\.electron_build_tools\third_party\depot_tools" >> $GITHUB_PATH + else + echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH + echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH + fi \ No newline at end of file From bbb000e896f1ab63ac80cf2a5c0be20e0818be8f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 11:53:38 -0700 Subject: [PATCH 239/339] ci: add problem matcher for clang output (#47221) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- .github/actions/build-electron/action.yml | 6 ++++++ .github/problem-matchers/clang.json | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 .github/problem-matchers/clang.json diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 58a416e01f404..350866638b46d 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -38,6 +38,9 @@ runs: run: | GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"x64\" v8_snapshot_toolchain=\"//build/toolchain/mac:clang_x64\"" echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV + - name: Add Clang problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json" - name: Build Electron ${{ inputs.step-suffix }} shell: bash run: | @@ -199,6 +202,9 @@ runs: e build --target electron:libcxx_headers_zip -j $NUMBER_OF_NINJA_PROCESSES e build --target electron:libcxxabi_headers_zip -j $NUMBER_OF_NINJA_PROCESSES e build --target electron:libcxx_objects_zip -j $NUMBER_OF_NINJA_PROCESSES + - name: Remove Clang problem matcher + shell: bash + run: echo "::remove-matcher owner=clang::" - name: Generate TypeScript Definitions ${{ inputs.step-suffix }} if: ${{ inputs.is-release == 'true' }} shell: bash diff --git a/.github/problem-matchers/clang.json b/.github/problem-matchers/clang.json new file mode 100644 index 0000000000000..35cf775305bd5 --- /dev/null +++ b/.github/problem-matchers/clang.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "clang", + "fromPath": "src/out/Default/args.gn", + "pattern": [ + { + "regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] +} From fc2df39c33a5c3eceb1308d495814cb0a80bd0da Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 11:54:54 -0700 Subject: [PATCH 240/339] ci: add problem matcher for patch conflict output (#47224) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- .github/actions/checkout/action.yml | 9 +++++++- .github/problem-matchers/patch-conflict.json | 24 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .github/problem-matchers/patch-conflict.json diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 205eefde25816..b4f6ca7f2126f 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -80,6 +80,9 @@ runs: else echo "The cross mount cache has $freespace_human free space - continuing" fi + - name: Add patch conflict problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/patch-conflict.json" - name: Gclient Sync if: steps.check-cache.outputs.cache_exists == 'false' shell: bash @@ -128,7 +131,11 @@ runs: echo "No changes to patches detected" fi fi - + - name: Remove patch conflict problem matcher + shell: bash + run: | + echo "::remove-matcher owner=merge-conflict::" + echo "::remove-matcher owner=patch-conflict::" # delete all .git directories under src/ except for # third_party/angle/ and third_party/dawn/ because of build time generation of files # gen/angle/commit.h depends on third_party/angle/.git/HEAD diff --git a/.github/problem-matchers/patch-conflict.json b/.github/problem-matchers/patch-conflict.json new file mode 100644 index 0000000000000..e8324448cbbfa --- /dev/null +++ b/.github/problem-matchers/patch-conflict.json @@ -0,0 +1,24 @@ +{ + "problemMatcher": [ + { + "owner": "merge-conflict", + "pattern": [ + { + "regexp": "^CONFLICT\\s\\(\\S+\\): (Merge conflict in \\S+)$", + "message": 1 + } + ] + }, + { + "owner": "patch-conflict", + "pattern": [ + { + "regexp": "^error: (patch failed: (\\S+):(\\d+))$", + "message": 1, + "file": 2, + "line": 3 + } + ] + } + ] +} From 9b624e143edee74d30776b1ddcb916a07f3672b0 Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Thu, 22 May 2025 15:48:48 -0700 Subject: [PATCH 241/339] chore: cherry-pick 69d5a982aed6 from chromium (#47233) * chore: cherry-pick 69d5a982aed6 from chromium * chore: update patches --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-69d5a982aed6.patch | 90 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 patches/chromium/cherry-pick-69d5a982aed6.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index afe3ceed903a3..029358022d7aa 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -149,3 +149,4 @@ mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch fix_osr_stutter_fix_backport_for_electron.patch do_not_check_the_order_of_display_id_order_on_windows.patch make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch +cherry-pick-69d5a982aed6.patch diff --git a/patches/chromium/cherry-pick-69d5a982aed6.patch b/patches/chromium/cherry-pick-69d5a982aed6.patch new file mode 100644 index 0000000000000..bbf8bcd3101e4 --- /dev/null +++ b/patches/chromium/cherry-pick-69d5a982aed6.patch @@ -0,0 +1,90 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Guido Urdaneta +Date: Thu, 15 May 2025 06:22:26 -0700 +Subject: Disable relaxed mode for audio devices + +Relaxed mode is required only for cameras and there have been +reports of problems with audio bluetooth devices. + +Bug: 417256410 +Change-Id: Icfff72e5de12ea9efa1f0fe529b8a01ff4b5a149 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6550297 +Commit-Queue: Guido Urdaneta +Reviewed-by: Palak Agarwal +Cr-Commit-Position: refs/heads/main@{#1460672} + +diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc +index 4d3189e34c90f2be18d44c4a6f42859b495f58ae..20ca4a98945f9d7a37bec292442147376659f8df 100644 +--- a/content/browser/renderer_host/media/media_devices_manager.cc ++++ b/content/browser/renderer_host/media/media_devices_manager.cc +@@ -354,7 +354,8 @@ struct MediaDevicesManager::EnumerationRequest { + // considered valid for some time after an enumeration. + class MediaDevicesManager::CacheInfo { + public: +- CacheInfo() = default; ++ explicit CacheInfo(bool allow_relaxed_mode) ++ : allow_relaxed_mode_(allow_relaxed_mode) {} + + void InvalidateCache() { + DCHECK(thread_checker_.CalledOnValidThread()); +@@ -425,7 +426,7 @@ class MediaDevicesManager::CacheInfo { + void RecordSpuriousInvalidation() { + DCHECK(thread_checker_.CalledOnValidThread()); + CHECK(IsRelaxedCacheFeatureEnabled()); +- if (is_in_relaxed_mode_) { ++ if (is_in_relaxed_mode_ || !allow_relaxed_mode_) { + return; + } + if (++num_spurious_invalidations_ >= kMaxSpuriousInvalidations) { +@@ -434,6 +435,8 @@ class MediaDevicesManager::CacheInfo { + } + } + ++ bool allow_relaxed_mode() const { return allow_relaxed_mode_; } ++ + private: + bool IsCacheExpired() const { + CHECK(IsRelaxedCacheFeatureEnabled()); +@@ -451,9 +454,13 @@ class MediaDevicesManager::CacheInfo { + int64_t seq_last_invalidation_ = 0; + bool is_update_ongoing_ = false; + int num_spurious_invalidations_ = 0; ++ const bool allow_relaxed_mode_; + // This is eventually set to true on systems where the normal cache policy + // can result in notification loops. Once set to true, it is never set back + // to false in order to avoid these loops. See https://crbug.com/325590346. ++ // An invariant that must be preserved at all times is that ++ // `is_in_relaxed_mode_` can be set to true only if `allow_relaxed_mode_` is ++ // true. + bool is_in_relaxed_mode_ = false; + base::TimeTicks time_last_update_; + base::ThreadChecker thread_checker_; +@@ -551,7 +558,6 @@ MediaDevicesManager::MediaDevicesManager( + stop_removed_input_device_cb_(std::move(stop_removed_input_device_cb)), + ui_input_device_change_cb_(std::move(ui_input_device_change_cb)), + permission_checker_(std::make_unique()), +- cache_infos_(static_cast(MediaDeviceType::kNumMediaDeviceTypes)), + get_salt_and_origin_cb_( + base::BindRepeating(&GetMediaDeviceSaltAndOrigin)) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +@@ -559,6 +565,20 @@ MediaDevicesManager::MediaDevicesManager( + DCHECK(video_capture_manager_.get()); + DCHECK(!stop_removed_input_device_cb_.is_null()); + DCHECK(!ui_input_device_change_cb_.is_null()); ++ // Enable relaxed mode only for cameras. ++ // Audio devices do not need relaxed mode and it can interfere with bluetooth ++ // notifications. See https://crbug.com/417256410 ++ cache_infos_.emplace_back(/*allow_relaxed_mode=*/false); ++ cache_infos_.emplace_back(/*allow_relaxed_mode=*/true); ++ cache_infos_.emplace_back(/*allow_relaxed_mode=*/false); ++ CHECK_EQ(cache_infos_.size(), ++ static_cast(MediaDeviceType::kNumMediaDeviceTypes)); ++ CHECK(!cache_infos_[static_cast(MediaDeviceType::kMediaAudioInput)] ++ .allow_relaxed_mode()); ++ CHECK(cache_infos_[static_cast(MediaDeviceType::kMediaVideoInput)] ++ .allow_relaxed_mode()); ++ CHECK(!cache_infos_[static_cast(MediaDeviceType::kMediaAudioOutput)] ++ .allow_relaxed_mode()); + SendLogMessage("MediaDevicesManager()"); + cache_policies_.fill(CachePolicy::NO_CACHE); + } From 1c132a3fdb532c29d66d855cf89de37f40cef55e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 23 May 2025 10:15:57 -0500 Subject: [PATCH 242/339] fix: fix and enable `accessibilitySupportEnabled` tests (36-x-y) (#47196) fix: fix and enable `accessibilitySupportEnabled` tests (#46825) * test: enable accessibilitySupportEnabled tests * test: check both getters after calling each setter * fix: do not assume the default initial value of accessibilitySupportEnabled * chore: remove redundant test case * chore: disable accessibilitySupportEnabled tests on Linux --- spec/api-app-spec.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index e3bb800e45581..1aa4daae04e07 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -979,22 +979,22 @@ describe('app module', () => { }); ifdescribe(process.platform !== 'linux')('accessibilitySupportEnabled property', () => { - it('with properties', () => { - it('can set accessibility support enabled', () => { - expect(app.accessibilitySupportEnabled).to.eql(false); - - app.accessibilitySupportEnabled = true; - expect(app.accessibilitySupportEnabled).to.eql(true); - }); - }); - - describe('with functions', () => { - it('can set accessibility support enabled', () => { - expect(app.isAccessibilitySupportEnabled()).to.eql(false); - - app.setAccessibilitySupportEnabled(true); - expect(app.isAccessibilitySupportEnabled()).to.eql(true); - }); + it('is mutable', () => { + const values = [false, true, false]; + const setters: Array<(arg: boolean) => void> = [ + (value) => { app.accessibilitySupportEnabled = value; }, + (value) => app.setAccessibilitySupportEnabled(value) + ]; + const getters: Array<() => boolean> = [ + () => app.accessibilitySupportEnabled, + () => app.isAccessibilitySupportEnabled() + ]; + for (const value of values) { + for (const set of setters) { + set(value); + for (const get of getters) expect(get()).to.eql(value); + } + } }); }); From 5bfea79b6710b0978fbd8cf024fba4e257be9b9c Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 12:06:09 -0400 Subject: [PATCH 243/339] chore: bump chromium to 136.0.7103.115 (36-x-y) (#47215) chore: bump chromium in DEPS to 136.0.7103.115 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 885c430b1f8d0..cf05d087f3d87 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.113', + '136.0.7103.115', 'node_version': 'v22.15.1', 'nan_version': From 38e990c6e0b8241e5c54e5c9360495856feebfc9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 12:58:24 -0500 Subject: [PATCH 244/339] refactor: use `base::fixed_flat_set` in `NativeWindowViews::SetAlwaysOnTop()` (#47239) refactor: use base::fixed_flat_set in NativeWindowViews::SetAlwaysOnTop() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window_views.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 2fae07f9cb16f..6ac9ee7a5b521 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -20,7 +20,7 @@ #include #include -#include "base/containers/contains.h" +#include "base/containers/fixed_flat_set.h" #include "base/memory/raw_ref.h" #include "base/numerics/ranges.h" #include "base/strings/utf_string_conversions.h" @@ -1124,9 +1124,9 @@ void NativeWindowViews::SetAlwaysOnTop(ui::ZOrderLevel z_order, if (z_order != ui::ZOrderLevel::kNormal) { // On macOS the window is placed behind the Dock for the following levels. // Re-use the same names on Windows to make it easier for the user. - static const std::vector levels = { - "floating", "torn-off-menu", "modal-panel", "main-menu", "status"}; - behind_task_bar_ = base::Contains(levels, level); + static constexpr auto levels = base::MakeFixedFlatSet( + {"floating", "torn-off-menu", "modal-panel", "main-menu", "status"}); + behind_task_bar_ = levels.contains(level); } #endif MoveBehindTaskBarIfNeeded(); From 0c226d8d798852722dae07e0219b1b5fdea51f48 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 20:33:02 -0700 Subject: [PATCH 245/339] chore: debug crash on DevTools SetOwnerWindow (#47262) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_web_contents.cc | 5 ++++- shell/browser/ui/inspectable_web_contents.cc | 2 ++ shell/browser/ui/inspectable_web_contents.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 324c69223d53d..2648b7187f240 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2151,8 +2151,11 @@ void WebContents::DevToolsOpened() { // Inherit owner window in devtools when it doesn't have one. auto* devtools = inspectable_web_contents_->GetDevToolsWebContents(); bool has_window = devtools->GetUserData(NativeWindowRelay::UserDataKey()); - if (owner_window() && !has_window) + if (owner_window_ && !has_window) { + DCHECK(!owner_window_.WasInvalidated()); + DCHECK_EQ(handle->owner_window(), nullptr); handle->SetOwnerWindow(devtools, owner_window()); + } Emit("devtools-opened"); } diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc index 60d481a9d5caa..e80d6a6d7662d 100644 --- a/shell/browser/ui/inspectable_web_contents.cc +++ b/shell/browser/ui/inspectable_web_contents.cc @@ -530,6 +530,8 @@ void InspectableWebContents::CloseWindow() { } void InspectableWebContents::LoadCompleted() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + frontend_loaded_ = true; if (managed_devtools_web_contents_) view_->ShowDevTools(activate_); diff --git a/shell/browser/ui/inspectable_web_contents.h b/shell/browser/ui/inspectable_web_contents.h index 012f613895e3a..87fe41aaad7fc 100644 --- a/shell/browser/ui/inspectable_web_contents.h +++ b/shell/browser/ui/inspectable_web_contents.h @@ -15,6 +15,7 @@ #include "base/containers/unique_ptr_adapters.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" #include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_frontend_host.h" @@ -271,6 +272,8 @@ class InspectableWebContents // use, which guarantees that this set must not be persisted. base::flat_set synced_setting_names_; + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory weak_factory_{this}; }; From 6a884abe6bd39da8ac5910723b367fa859a42c25 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 14:24:06 +0200 Subject: [PATCH 246/339] chore: update @electron/lint-roller to 3.1.1 (#47256) * chore: update @electron/lint-roller to 3.1.1 Co-authored-by: David Sanders * docs: fix broken link in breaking-changes.md Co-authored-by: David Sanders * chore: fix for Node.js versions without require(esm) Co-authored-by: David Sanders --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- docs/breaking-changes.md | 2 +- package.json | 2 +- script/lint.js | 3 +- yarn.lock | 328 +++------------------------------------ 4 files changed, 27 insertions(+), 308 deletions(-) diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 534831e1a2ed9..537b4a74c8cd4 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -110,7 +110,7 @@ On Linux, the required portal version for file dialogs has been reverted to 3 from 4. Using the `defaultPath` option of the Dialog API is not supported when using portal file chooser dialogs unless the portal backend is version 4 or higher. The `--xdg-portal-required-version` -[command-line switch](/api/command-line-switches.md#--xdg-portal-required-versionversion) +[command-line switch](api/command-line-switches.md#--xdg-portal-required-versionversion) can be used to force a required version for your application. See [#44426](https://github.com/electron/electron/pull/44426) for more details. diff --git a/package.json b/package.json index 709d9147ed438..d2bfd9bfa718f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@electron/docs-parser": "^2.0.0", "@electron/fiddle-core": "^1.3.4", "@electron/github-app-auth": "^2.2.1", - "@electron/lint-roller": "^3.0.0", + "@electron/lint-roller": "^3.1.1", "@electron/typescript-definitions": "^9.1.2", "@octokit/rest": "^20.0.2", "@primer/octicons": "^10.0.0", diff --git a/script/lint.js b/script/lint.js index 06f0b7ac305cf..63459f47ef173 100755 --- a/script/lint.js +++ b/script/lint.js @@ -1,7 +1,5 @@ #!/usr/bin/env node -const { getCodeBlocks } = require('@electron/lint-roller/dist/lib/markdown'); - const { GitProcess } = require('dugite'); const { ESLint } = require('eslint'); const minimist = require('minimist'); @@ -281,6 +279,7 @@ const LINTERS = [{ ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'], test: filename => filename.endsWith('.md'), run: async (opts, filenames) => { + const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js'); let errors = false; // Run markdownlint on all Markdown files diff --git a/yarn.lock b/yarn.lock index 67d472fe02555..ad0ed30e207f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -263,10 +263,10 @@ "@octokit/auth-app" "^4.0.13" "@octokit/rest" "^19.0.11" -"@electron/lint-roller@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-3.0.0.tgz#1c1604f9fe87ace82142d8bd25b5c5f0d1e08003" - integrity sha512-YdbWKivSZj+J0yjJhzACU6yXuah0VQMcyijKOaVxX6qG5J/df75oCt/jyuOpRr0HRtz62DaHphEnzGRhTFx9FA== +"@electron/lint-roller@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-3.1.1.tgz#a301f1f84ef836e7800c655fa3b5efcda82f95b0" + integrity sha512-s30rM5ksvVuks7bsTKxQALmqY/8/KxJieGWs3QKru2nL4UJlN5PTTbxXh42qCqQ1LRTfE/cZ5CDjF9nomc3mYw== dependencies: "@dsanders11/vscode-markdown-languageservice" "^0.3.0" ajv "^8.16.0" @@ -274,9 +274,9 @@ glob "^10.4.5" hast-util-from-html "^2.0.1" markdown-it "^14.1.0" - mdast-util-from-markdown "^1.3.0" + mdast-util-from-markdown "^2.0.2" standard "^17.0.0" - unist-util-visit "^4.1.2" + unist-util-visit "^5.0.0" vscode-languageserver "^8.1.0" vscode-languageserver-textdocument "^1.0.8" vscode-uri "^3.0.8" @@ -946,13 +946,6 @@ "@types/linkify-it" "^5" "@types/mdurl" "^2" -"@types/mdast@^3.0.0": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.7.tgz#cba63d0cc11eb1605cea5c0ad76e02684394166b" - integrity sha512-YwR7OK8aPmaBvMMUi+pZXBNoW2unbVbfok4YRqGMJBe1dpDlzpRkJrYEYmvjxgs5JhuQmKfDexrN98u941Zasg== - dependencies: - "@types/unist" "*" - "@types/mdast@^4.0.0": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" @@ -1821,11 +1814,6 @@ chalk@^5.0.0, chalk@^5.3.0: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -character-entities-legacy@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-2.0.0.tgz#57f4d00974c696e8f74e9f493e7fcb75b44d7ee7" - integrity sha512-YwaEtEvWLpFa6Wh3uVLrvirA/ahr9fki/NUd/Bd4OR6EdJ8D22hovYQEOUCBfQfcqnC4IAMGMsHXY1eXgL4ZZA== - character-entities-legacy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" @@ -2185,11 +2173,6 @@ diff@^3.1.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" - integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -4115,11 +4098,6 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -kleur@^4.0.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" - integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -4403,24 +4381,6 @@ mdast-comment-marker@^1.0.0: resolved "https://registry.yarnpkg.com/mdast-comment-marker/-/mdast-comment-marker-1.1.1.tgz#9c9c18e1ed57feafc1965d92b028f37c3c8da70d" integrity sha512-TWZDaUtPLwKX1pzDIY48MkSUQRDwX/HqbTB4m3iYdL/zosi/Z6Xqfdv0C0hNVKvzrPjZENrpWDt4p4odeVO0Iw== -mdast-util-from-markdown@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz#0214124154f26154a2b3f9d401155509be45e894" - integrity sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - decode-named-character-reference "^1.0.0" - mdast-util-to-string "^3.1.0" - micromark "^3.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-decode-string "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - unist-util-stringify-position "^3.0.0" - uvu "^0.5.0" - mdast-util-from-markdown@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz#32a6e8f512b416e1f51eb817fc64bd867ebcd9cc" @@ -4439,6 +4399,24 @@ mdast-util-from-markdown@^2.0.0: micromark-util-types "^2.0.0" unist-util-stringify-position "^4.0.0" +mdast-util-from-markdown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + mdast-util-heading-style@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/mdast-util-heading-style/-/mdast-util-heading-style-1.0.5.tgz#81b2e60d76754198687db0e8f044e42376db0426" @@ -4471,11 +4449,6 @@ mdast-util-to-string@^1.0.2: resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.0.6.tgz#7d85421021343b33de1552fc71cb8e5b4ae7536d" integrity sha512-868pp48gUPmZIhfKrLbaDneuzGiw3OTDjHc5M1kAepR2CWBJ+HpEsm252K4aXdiP5coVZaJPOqGtVU6Po8xnXg== -mdast-util-to-string@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9" - integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA== - mdast-util-to-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" @@ -4528,27 +4501,6 @@ micromark-core-commonmark@2.0.3: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-core-commonmark@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.0.tgz#b767fa7687c205c224175bf067796360a3830350" - integrity sha512-y9g7zymcKRBHM/aNBekstvs/Grpf+y4OEBULUTYvGZcusnp+JeOxmilJY4GMpo2/xY7iHQL9fjz5pD9pSAud9A== - dependencies: - micromark-factory-destination "^1.0.0" - micromark-factory-label "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-factory-title "^1.0.0" - micromark-factory-whitespace "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-classify-character "^1.0.0" - micromark-util-html-tag-name "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - parse-entities "^3.0.0" - micromark-core-commonmark@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" @@ -4632,15 +4584,6 @@ micromark-extension-math@3.1.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-destination@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e" - integrity sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-destination@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" @@ -4650,15 +4593,6 @@ micromark-factory-destination@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-label@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.0.tgz#b316ec479b474232973ff13b49b576f84a6f2cbb" - integrity sha512-XWEucVZb+qBCe2jmlOnWr6sWSY6NHx+wtpgYFsm4G+dufOf6tTQRRo0bdO7XSlGPu5fyjpJenth6Ksnc5Mwfww== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-label@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" @@ -4669,14 +4603,6 @@ micromark-factory-label@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-space@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633" - integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-space@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" @@ -4685,16 +4611,6 @@ micromark-factory-space@^2.0.0: micromark-util-character "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-title@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.0.tgz#708f7a8044f34a898c0efdb4f55e4da66b537273" - integrity sha512-flvC7Gx0dWVWorXuBl09Cr3wB5FTuYec8pMGVySIp2ZlqTcIjN/lFohZcP0EG//krTptm34kozHk7aK/CleCfA== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-title@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" @@ -4705,16 +4621,6 @@ micromark-factory-title@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-whitespace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" - integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" @@ -4725,14 +4631,6 @@ micromark-factory-whitespace@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-character@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86" - integrity sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-character@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" @@ -4741,13 +4639,6 @@ micromark-util-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-chunked@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06" - integrity sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-chunked@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" @@ -4755,15 +4646,6 @@ micromark-util-chunked@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-classify-character@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20" - integrity sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-classify-character@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" @@ -4773,14 +4655,6 @@ micromark-util-classify-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-combine-extensions@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5" - integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-combine-extensions@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" @@ -4789,13 +4663,6 @@ micromark-util-combine-extensions@^2.0.0: micromark-util-chunked "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-decode-numeric-character-reference@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946" - integrity sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-decode-numeric-character-reference@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" @@ -4803,16 +4670,6 @@ micromark-util-decode-numeric-character-reference@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-decode-string@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02" - integrity sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-decode-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" @@ -4823,33 +4680,16 @@ micromark-util-decode-string@^2.0.0: micromark-util-decode-numeric-character-reference "^2.0.0" micromark-util-symbol "^2.0.0" -micromark-util-encode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.0.tgz#c409ecf751a28aa9564b599db35640fccec4c068" - integrity sha512-cJpFVM768h6zkd8qJ1LNRrITfY4gwFt+tziPcIf71Ui8yFzY9wG3snZQqiWVq93PG4Sw6YOtcNiKJfVIs9qfGg== - micromark-util-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== -micromark-util-html-tag-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.0.0.tgz#75737e92fef50af0c6212bd309bc5cb8dbd489ed" - integrity sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g== - micromark-util-html-tag-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== -micromark-util-normalize-identifier@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828" - integrity sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-normalize-identifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" @@ -4857,13 +4697,6 @@ micromark-util-normalize-identifier@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-resolve-all@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88" - integrity sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw== - dependencies: - micromark-util-types "^1.0.0" - micromark-util-resolve-all@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" @@ -4871,15 +4704,6 @@ micromark-util-resolve-all@^2.0.0: dependencies: micromark-util-types "^2.0.0" -micromark-util-sanitize-uri@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz#27dc875397cd15102274c6c6da5585d34d4f12b2" - integrity sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-sanitize-uri@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" @@ -4889,15 +4713,6 @@ micromark-util-sanitize-uri@^2.0.0: micromark-util-encode "^2.0.0" micromark-util-symbol "^2.0.0" -micromark-util-subtokenize@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.0.tgz#6f006fa719af92776c75a264daaede0fb3943c6a" - integrity sha512-EsnG2qscmcN5XhkqQBZni/4oQbLFjz9yk3ZM/P8a3YUjwV6+6On2wehr1ALx0MxK3+XXXLTzuBKHDFeDFYRdgQ== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-subtokenize@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" @@ -4908,11 +4723,6 @@ micromark-util-subtokenize@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-symbol@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz#91cdbcc9b2a827c0129a177d36241bcd3ccaa34d" - integrity sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ== - micromark-util-symbol@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" @@ -4923,11 +4733,6 @@ micromark-util-types@2.0.2: resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== -micromark-util-types@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.0.tgz#0ebdfaea3fa7c15fc82b1e06ea1ef0152d0fb2f0" - integrity sha512-psf1WAaP1B77WpW4mBGDkTr+3RsPuDAgsvlP47GJzbH1jmjH8xjOx7Z6kp84L8oqHmy5pYO3Ev46odosZV+3AA== - micromark-util-types@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" @@ -4956,28 +4761,6 @@ micromark@4.0.2: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.3.tgz#4c9f76fce8ba68eddf8730bb4fee2041d699d5b7" - integrity sha512-fWuHx+JKV4zA8WfCFor2DWP9XmsZkIiyWRGofr7P7IGfpRIlb7/C5wwusGsNyr1D8HI5arghZDG1Ikc0FBwS5Q== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - micromark-core-commonmark "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - parse-entities "^3.0.0" - micromark@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" @@ -5136,11 +4919,6 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mri@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" - integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== - ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -5484,18 +5262,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-entities@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-3.0.0.tgz#9ed6d6569b6cfc95ade058d683ddef239dad60dc" - integrity sha512-AJlcIFDNPEP33KyJLguv0xJc83BNvjxwpuUIcetyXUsLpVXAUCePJ5kIoYtEN2R1ac0cYaRu/vk9dVFkewHQhQ== - dependencies: - character-entities "^2.0.0" - character-entities-legacy "^2.0.0" - character-reference-invalid "^2.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - parse-entities@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" @@ -6598,13 +6364,6 @@ rxjs@^6.5.5: dependencies: tslib "^1.9.0" -sade@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" - integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== - dependencies: - mri "^1.1.0" - safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -7553,11 +7312,6 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== -unist-util-is@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" - integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ== - unist-util-is@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" @@ -7577,13 +7331,6 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" -unist-util-stringify-position@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz#d517d2883d74d0daa0b565adc3d10a02b4a8cde9" - integrity sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" @@ -7599,14 +7346,6 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit-parents@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz#868f353e6fce6bf8fa875b251b0f4fec3be709bb" - integrity sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" @@ -7624,15 +7363,6 @@ unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -unist-util-visit@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" - integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.1.1" - unist-util-visit@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" @@ -7698,16 +7428,6 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uvu@^0.5.0: - version "0.5.6" - resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" - integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== - dependencies: - dequal "^2.0.0" - diff "^5.0.0" - kleur "^4.0.3" - sade "^1.7.3" - validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" From 45542e197b5cdc0b4615d70d183573f56aca6c2c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 14:24:21 +0200 Subject: [PATCH 247/339] fix: titlebar showing in content protected window (#47264) Closes https://github.com/electron/electron/issues/47152. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../electron_desktop_window_tree_host_win.cc | 26 +++++++++++++++++-- .../electron_desktop_window_tree_host_win.h | 3 +++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index 26c0f7a3c9425..7ce790b566e61 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -143,11 +143,33 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { void ElectronDesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) { if (native_window_view_->widget()) native_window_view_->widget()->OnNativeWidgetVisibilityChanged(visible); + + if (visible) + UpdateAllowScreenshots(); } void ElectronDesktopWindowTreeHostWin::SetAllowScreenshots(bool allow) { - ::SetWindowDisplayAffinity(GetAcceleratedWidget(), - allow ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE); + if (allow_screenshots_ == allow) + return; + + allow_screenshots_ = allow; + + // If the window is not visible, do not set the window display affinity + // because `SetWindowDisplayAffinity` will attempt to compose the window, + if (!IsVisible()) + return; + + UpdateAllowScreenshots(); +} + +void ElectronDesktopWindowTreeHostWin::UpdateAllowScreenshots() { + bool allowed = views::DesktopWindowTreeHostWin::AreScreenshotsAllowed(); + if (allowed == allow_screenshots_) + return; + + ::SetWindowDisplayAffinity( + GetAcceleratedWidget(), + allow_screenshots_ ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE); } void ElectronDesktopWindowTreeHostWin::OnNativeThemeUpdated( diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h index 3afa5ae84f51f..92a40be9511d3 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -51,8 +51,11 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin, bool ShouldWindowContentsBeTransparent() const override; private: + void UpdateAllowScreenshots(); + raw_ptr native_window_view_; // weak ref std::optional force_should_paint_as_active_; + bool allow_screenshots_ = true; bool widget_init_done_ = false; }; From 3da28fd115f2cf8a2e825de441995c83fb60c0a8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 11:17:09 -0400 Subject: [PATCH 248/339] fix: regression with directory selection in macOS dialogs (#47278) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- shell/browser/ui/file_dialog_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index d92461df01c43..936a907b39f3b 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -321,7 +321,7 @@ void ReadDialogPathsWithBookmarks(NSOpenPanel* dialog, BOOL is_package_as_directory = [[NSWorkspace sharedWorkspace] isFilePackageAtPath:path] && [dialog treatsFilePackagesAsDirectories]; - if (!exists || !is_directory || !is_package_as_directory) + if (!exists || !(is_directory || is_package_as_directory)) continue; } From dd054ea748012cdd8ae15fd2f8ff283eb4f4ac1c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:50:03 -0400 Subject: [PATCH 249/339] build: migrate to new chromium git auth (#47254) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/checkout/action.yml | 4 +- .../actions/set-chromium-cookie/action.yml | 58 ------------------- .../set-chromium-git-helper/action.yml | 41 +++++++++++++ .github/workflows/build.yml | 10 ++-- .github/workflows/linux-publish.yml | 3 +- .github/workflows/macos-publish.yml | 3 +- .github/workflows/pipeline-electron-lint.yml | 7 ++- .../pipeline-segment-electron-build.yml | 8 +-- .../pipeline-segment-electron-gn-check.yml | 4 +- .../pipeline-segment-electron-test.yml | 8 +-- .../pipeline-segment-node-nan-test.yml | 11 ++-- .github/workflows/windows-publish.yml | 3 +- 12 files changed, 75 insertions(+), 85 deletions(-) delete mode 100644 .github/actions/set-chromium-cookie/action.yml create mode 100644 .github/actions/set-chromium-git-helper/action.yml diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index b4f6ca7f2126f..7cfbd542c1b60 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -20,8 +20,8 @@ runs: echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash diff --git a/.github/actions/set-chromium-cookie/action.yml b/.github/actions/set-chromium-cookie/action.yml deleted file mode 100644 index 2011655e29b59..0000000000000 --- a/.github/actions/set-chromium-cookie/action.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: 'Set Chromium Git Cookie' -description: 'Sets an authenticated cookie from Chromium to allow for a higher request limit' -runs: - using: "composite" - steps: - - name: Set the git cookie from chromium.googlesource.com (Unix) - if: ${{ runner.os != 'Windows' }} - shell: bash - run: | - if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then - echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate." - exit 0 - fi - - eval 'set +o history' 2>/dev/null || setopt HIST_IGNORE_SPACE 2>/dev/null - touch ~/.gitcookies - chmod 0600 ~/.gitcookies - - git config --global http.cookiefile ~/.gitcookies - - tr , \\t <<\__END__ >>~/.gitcookies - ${{ env.CHROMIUM_GIT_COOKIE }} - __END__ - eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null - - RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self) - if [[ $RESPONSE == ")]}'"* ]]; then - # Extract account email for verification - EMAIL=$(echo "$RESPONSE" | tail -c +5 | jq -r '.email // "No email found"') - echo "Cookie authentication successful - authenticated as: $EMAIL" - else - echo "Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE is set correctly" - echo $RESPONSE - fi - - name: Set the git cookie from chromium.googlesource.com (Windows) - if: ${{ runner.os == 'Windows' }} - shell: cmd - run: | - if "%CHROMIUM_GIT_COOKIE_WINDOWS_STRING%"=="" ( - echo CHROMIUM_GIT_COOKIE_WINDOWS_STRING is not set - cannot authenticate. - exit /b 0 - ) - - git config --global http.cookiefile "%USERPROFILE%\.gitcookies" - powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies" - - curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt - - findstr /B /C:")]}'" response.txt > nul - if %ERRORLEVEL% EQU 0 ( - echo Cookie authentication successful - powershell -NoProfile -Command "& {$content = Get-Content -Raw response.txt; $content = $content.Substring(4); try { $json = ConvertFrom-Json $content; if($json.email) { Write-Host 'Authenticated as:' $json.email } else { Write-Host 'No email found in response' } } catch { Write-Host 'Error parsing JSON:' $_ }}" - ) else ( - echo Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE_WINDOWS_STRING is set correctly - type response.txt - ) - - del response.txt diff --git a/.github/actions/set-chromium-git-helper/action.yml b/.github/actions/set-chromium-git-helper/action.yml new file mode 100644 index 0000000000000..bdc0ceb303d25 --- /dev/null +++ b/.github/actions/set-chromium-git-helper/action.yml @@ -0,0 +1,41 @@ +name: 'Set Chromium Git Helper' +description: 'Sets Chromium Git Helper to allow for a higher request limit' +runs: + using: "composite" + steps: + - name: Save the chromium git credentials to a file + shell: bash + run: | + if [[ -z "${{ env.CHROMIUM_GIT_AUTH }}" ]]; then + echo "CHROMIUM_GIT_AUTH is not set - cannot authenticate." + exit 0 + fi + if [[ "${{ runner.os }}" != "Windows" ]]; then + cd $HOME + fi + echo "${{ env.CHROMIUM_GIT_AUTH }}" > .chromium_git_auth + + - name: Set the chromium git helper to use auth from a file + shell: bash + run: | + if [[ "${{ runner.os }}" == "Windows" ]]; then + if [[ ! -f "/c/actions-runner/_work/electron/electron/.chromium_git_auth" ]]; then + echo "File /c/actions-runner/_work/electron/electron/.chromium_git_auth does not exist - cannot authenticate." + exit 0 + fi + else + if [[ ! -f "$HOME/.chromium_git_auth" ]]; then + echo "File $HOME/.chromium_git_auth does not exist - cannot authenticate." + exit 0 + fi + fi + if [[ -z "${{ env.CHROMIUM_GIT_USER }}" ]]; then + echo "CHROMIUM_GIT_USER is not set - cannot authenticate." + exit 0 + fi + git config --global credential.https://chromium.googlesource.com.username "${{ env.CHROMIUM_GIT_USER }}" + if [[ "${{ runner.os }}" == "Windows" ]]; then + git config --global credential.https://chromium.googlesource.com.helper '!f() { test "$1" = get && echo "password=$(cat /c/actions-runner/_work/electron/electron/.chromium_git_auth)"; }; f' + else + git config --global credential.https://chromium.googlesource.com.helper '!f() { test "$1" = get && echo "password=$(cat $HOME/.chromium_git_auth)"; }; f' + fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf578ebe26100..ace3bae71f5a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,7 +100,8 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' outputs: build-image-sha: ${{ needs.setup.outputs.build-image-sha }} @@ -128,7 +129,8 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' PATCH_UP_APP_CREDS: ${{ secrets.PATCH_UP_APP_CREDS }} outputs: @@ -154,8 +156,8 @@ jobs: - /mnt/win-cache:/mnt/win-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} - CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index 8cadd26d23bcc..a003a15fc1184 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -27,7 +27,8 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - name: Checkout Electron diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index c7241b6a3bb00..3e4654445092a 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -28,7 +28,8 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' steps: - name: Checkout Electron diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index acbf2a6510945..88445a478a5c0 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -13,7 +13,8 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} jobs: lint: @@ -30,8 +31,8 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Setup third_party Depot Tools shell: bash run: | diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index 8d2821a61a923..297b146be635b 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -65,8 +65,8 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} - CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }} ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }} @@ -127,8 +127,8 @@ jobs: GN_EXTRA_ARGS='is_asan=true' fi echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml index 48fe703078145..9e74404070b13 100644 --- a/.github/workflows/pipeline-segment-electron-gn-check.yml +++ b/.github/workflows/pipeline-segment-electron-gn-check.yml @@ -66,8 +66,8 @@ jobs: - name: Check disk space after freeing up space if: ${{ inputs.target-platform == 'macos' }} run: df -h - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Enable windows toolchain diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 369bb86104703..90e3d6c207e1a 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -36,8 +36,8 @@ permissions: pull-requests: read env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} - CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} ELECTRON_OUT_DIR: Default ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} @@ -126,8 +126,8 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Get Depot Tools timeout-minutes: 5 run: | diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index ee381add0dbb4..6af170aaba43a 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -31,7 +31,8 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} ELECTRON_OUT_DIR: Default ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} @@ -51,8 +52,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -105,8 +106,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Set Chromium Git Cookie - uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Set Chromium Git Helper + uses: ./src/electron/.github/actions/set-chromium-git-helper - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index e8b7c6172fdd8..c0acf202f6d30 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -28,7 +28,8 @@ jobs: - /mnt/win-cache:/mnt/win-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} + CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} + CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' From 14ba395d4582421401efb0d2c84cf32dcfc5f846 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 13:34:45 -0400 Subject: [PATCH 250/339] chore: bump chromium to 136.0.7103.116 (36-x-y) (#47288) chore: bump chromium in DEPS to 136.0.7103.116 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index cf05d087f3d87..7f6f247e5d5d6 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.115', + '136.0.7103.116', 'node_version': 'v22.15.1', 'nan_version': From 6e22f50bf87580a3a54e15018b2fc072be4423cf Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 17:40:55 -0400 Subject: [PATCH 251/339] chore: bump chromium to 136.0.7103.149 (36-x-y) (#47295) * chore: bump chromium in DEPS to 136.0.7103.149 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- patches/chromium/.patches | 1 - .../build_libc_as_static_library.patch | 30 +------ .../chromium/cherry-pick-69d5a982aed6.patch | 90 ------------------- ...ing_dialog_features_to_shell_dialogs.patch | 18 ++-- 5 files changed, 12 insertions(+), 129 deletions(-) delete mode 100644 patches/chromium/cherry-pick-69d5a982aed6.patch diff --git a/DEPS b/DEPS index 7f6f247e5d5d6..6e927afa7f50b 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.116', + '136.0.7103.149', 'node_version': 'v22.15.1', 'nan_version': diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 029358022d7aa..afe3ceed903a3 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -149,4 +149,3 @@ mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch fix_osr_stutter_fix_backport_for_electron.patch do_not_check_the_order_of_display_id_order_on_windows.patch make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch -cherry-pick-69d5a982aed6.patch diff --git a/patches/chromium/build_libc_as_static_library.patch b/patches/chromium/build_libc_as_static_library.patch index 2b4ec2e0833e8..962cbdea673ab 100644 --- a/patches/chromium/build_libc_as_static_library.patch +++ b/patches/chromium/build_libc_as_static_library.patch @@ -7,23 +7,10 @@ Build libc++ as static library to compile and pass nan tests diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn -index 29e8bbcb5e7ee2fb310ce00b0c20913424adc9c8..649330dde1e2b717e0af5a15834bf96ae4e19245 100644 +index d2e6e482d82782d1004e1f8ea377e69cbbe2677b..2bb9ec60180cf80be6e565081d56ea747820196b 100644 --- a/buildtools/third_party/libc++/BUILD.gn +++ b/buildtools/third_party/libc++/BUILD.gn -@@ -269,7 +269,11 @@ libcxx_modules("std_wctype_h") { - if (libcxx_is_shared) { - _libcxx_target_type = "shared_library" - } else { -- _libcxx_target_type = "source_set" -+ if (is_win) { -+ _libcxx_target_type = "source_set" -+ } else { -+ _libcxx_target_type = "static_library" -+ } - } - - target(_libcxx_target_type, "libc++") { -@@ -278,6 +282,7 @@ target(_libcxx_target_type, "libc++") { +@@ -272,6 +272,7 @@ target(libcxx_target_type, "libc++") { # need to explicitly depend on libc++. visibility = [ "//build/config:common_deps", @@ -31,16 +18,3 @@ index 29e8bbcb5e7ee2fb310ce00b0c20913424adc9c8..649330dde1e2b717e0af5a15834bf96a "//third_party/catapult/devil:devil", ] if (is_linux) { -diff --git a/buildtools/third_party/libc++abi/BUILD.gn b/buildtools/third_party/libc++abi/BUILD.gn -index 331ea447ea15e9f439396d4c7d41832de60adf4a..b96a994c43ac2ed0b0d5ec599f907ea0b501156e 100644 ---- a/buildtools/third_party/libc++abi/BUILD.gn -+++ b/buildtools/third_party/libc++abi/BUILD.gn -@@ -6,7 +6,7 @@ import("//build/config/android/config.gni") - import("//build/config/c++/c++.gni") - import("//build/config/unwind.gni") - --source_set("libc++abi") { -+static_library("libc++abi") { - if (export_libcxxabi_from_executables) { - visibility = [ "//build/config:executable_deps" ] - } else { diff --git a/patches/chromium/cherry-pick-69d5a982aed6.patch b/patches/chromium/cherry-pick-69d5a982aed6.patch deleted file mode 100644 index bbf8bcd3101e4..0000000000000 --- a/patches/chromium/cherry-pick-69d5a982aed6.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Guido Urdaneta -Date: Thu, 15 May 2025 06:22:26 -0700 -Subject: Disable relaxed mode for audio devices - -Relaxed mode is required only for cameras and there have been -reports of problems with audio bluetooth devices. - -Bug: 417256410 -Change-Id: Icfff72e5de12ea9efa1f0fe529b8a01ff4b5a149 -Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6550297 -Commit-Queue: Guido Urdaneta -Reviewed-by: Palak Agarwal -Cr-Commit-Position: refs/heads/main@{#1460672} - -diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc -index 4d3189e34c90f2be18d44c4a6f42859b495f58ae..20ca4a98945f9d7a37bec292442147376659f8df 100644 ---- a/content/browser/renderer_host/media/media_devices_manager.cc -+++ b/content/browser/renderer_host/media/media_devices_manager.cc -@@ -354,7 +354,8 @@ struct MediaDevicesManager::EnumerationRequest { - // considered valid for some time after an enumeration. - class MediaDevicesManager::CacheInfo { - public: -- CacheInfo() = default; -+ explicit CacheInfo(bool allow_relaxed_mode) -+ : allow_relaxed_mode_(allow_relaxed_mode) {} - - void InvalidateCache() { - DCHECK(thread_checker_.CalledOnValidThread()); -@@ -425,7 +426,7 @@ class MediaDevicesManager::CacheInfo { - void RecordSpuriousInvalidation() { - DCHECK(thread_checker_.CalledOnValidThread()); - CHECK(IsRelaxedCacheFeatureEnabled()); -- if (is_in_relaxed_mode_) { -+ if (is_in_relaxed_mode_ || !allow_relaxed_mode_) { - return; - } - if (++num_spurious_invalidations_ >= kMaxSpuriousInvalidations) { -@@ -434,6 +435,8 @@ class MediaDevicesManager::CacheInfo { - } - } - -+ bool allow_relaxed_mode() const { return allow_relaxed_mode_; } -+ - private: - bool IsCacheExpired() const { - CHECK(IsRelaxedCacheFeatureEnabled()); -@@ -451,9 +454,13 @@ class MediaDevicesManager::CacheInfo { - int64_t seq_last_invalidation_ = 0; - bool is_update_ongoing_ = false; - int num_spurious_invalidations_ = 0; -+ const bool allow_relaxed_mode_; - // This is eventually set to true on systems where the normal cache policy - // can result in notification loops. Once set to true, it is never set back - // to false in order to avoid these loops. See https://crbug.com/325590346. -+ // An invariant that must be preserved at all times is that -+ // `is_in_relaxed_mode_` can be set to true only if `allow_relaxed_mode_` is -+ // true. - bool is_in_relaxed_mode_ = false; - base::TimeTicks time_last_update_; - base::ThreadChecker thread_checker_; -@@ -551,7 +558,6 @@ MediaDevicesManager::MediaDevicesManager( - stop_removed_input_device_cb_(std::move(stop_removed_input_device_cb)), - ui_input_device_change_cb_(std::move(ui_input_device_change_cb)), - permission_checker_(std::make_unique()), -- cache_infos_(static_cast(MediaDeviceType::kNumMediaDeviceTypes)), - get_salt_and_origin_cb_( - base::BindRepeating(&GetMediaDeviceSaltAndOrigin)) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); -@@ -559,6 +565,20 @@ MediaDevicesManager::MediaDevicesManager( - DCHECK(video_capture_manager_.get()); - DCHECK(!stop_removed_input_device_cb_.is_null()); - DCHECK(!ui_input_device_change_cb_.is_null()); -+ // Enable relaxed mode only for cameras. -+ // Audio devices do not need relaxed mode and it can interfere with bluetooth -+ // notifications. See https://crbug.com/417256410 -+ cache_infos_.emplace_back(/*allow_relaxed_mode=*/false); -+ cache_infos_.emplace_back(/*allow_relaxed_mode=*/true); -+ cache_infos_.emplace_back(/*allow_relaxed_mode=*/false); -+ CHECK_EQ(cache_infos_.size(), -+ static_cast(MediaDeviceType::kNumMediaDeviceTypes)); -+ CHECK(!cache_infos_[static_cast(MediaDeviceType::kMediaAudioInput)] -+ .allow_relaxed_mode()); -+ CHECK(cache_infos_[static_cast(MediaDeviceType::kMediaVideoInput)] -+ .allow_relaxed_mode()); -+ CHECK(!cache_infos_[static_cast(MediaDeviceType::kMediaAudioOutput)] -+ .allow_relaxed_mode()); - SendLogMessage("MediaDevicesManager()"); - cache_policies_.fill(CachePolicy::NO_CACHE); - } diff --git a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch index 3b127aae5909b..7f579fde8b272 100644 --- a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch +++ b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch @@ -16,10 +16,10 @@ It also: This may be partially upstreamed to Chromium in the future. diff --git a/ui/gtk/select_file_dialog_linux_gtk.cc b/ui/gtk/select_file_dialog_linux_gtk.cc -index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e747b95a33 100644 +index 4a9118dcabbc0cffeea17dc26a8e1f2a54604766..4ae6001c0376822d41a77949ce05ea0328abcee4 100644 --- a/ui/gtk/select_file_dialog_linux_gtk.cc +++ b/ui/gtk/select_file_dialog_linux_gtk.cc -@@ -259,8 +259,12 @@ void SelectFileDialogLinuxGtk::SelectFileImpl( +@@ -261,8 +261,12 @@ void SelectFileDialogLinuxGtk::SelectFileImpl( case SELECT_EXISTING_FOLDER: dialog = CreateSelectFolderDialog(type, title_string, default_path, owning_window); @@ -34,7 +34,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 break; case SELECT_OPEN_FILE: dialog = CreateFileOpenDialog(title_string, default_path, owning_window); -@@ -407,9 +411,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( +@@ -409,9 +413,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( const std::string& title, const base::FilePath& default_path, gfx::NativeWindow parent) { @@ -47,7 +47,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 SetGtkTransientForAura(dialog, parent); AddFilters(GTK_FILE_CHOOSER(dialog)); -@@ -425,6 +431,7 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( +@@ -427,6 +433,7 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper( GtkFileChooserSetCurrentFolder(GTK_FILE_CHOOSER(dialog), *last_opened_path()); } @@ -55,7 +55,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -440,11 +447,15 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( +@@ -442,11 +449,15 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( ? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) : l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); } @@ -76,7 +76,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 GtkWidget* dialog = GtkFileChooserDialogNew( title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, -@@ -466,7 +477,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( +@@ -468,7 +479,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog( gtk_file_filter_add_mime_type(only_folders, "inode/directory"); gtk_file_filter_add_mime_type(only_folders, "text/directory"); gtk_file_chooser_add_filter(chooser, only_folders); @@ -86,7 +86,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -503,10 +515,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( +@@ -505,10 +517,11 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( std::string title_string = !title.empty() ? title : l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); @@ -100,7 +100,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 GTK_RESPONSE_ACCEPT); SetGtkTransientForAura(dialog, parent); -@@ -532,9 +545,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( +@@ -534,9 +547,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog( gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); // Overwrite confirmation is always enabled in GTK4. if (!GtkCheckVersion(4)) { @@ -113,7 +113,7 @@ index b83f0177a2adb0a19be49684f865941e6708f626..a8c7032cfc122b97665c41da9e1191e7 return dialog; } -@@ -589,15 +603,29 @@ void SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse( +@@ -591,15 +605,29 @@ void SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse( void SelectFileDialogLinuxGtk::OnSelectMultiFileDialogResponse( GtkWidget* dialog, int response_id) { From d647bb4bd04e98f9edfe68d1633720cde7c67b30 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 11:03:04 +0200 Subject: [PATCH 252/339] ci: add a problem matcher for ESLint output (#47308) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- .github/problem-matchers/eslint-stylish.json | 22 ++++++++++++++++++++ .github/workflows/pipeline-electron-lint.yml | 3 +++ 2 files changed, 25 insertions(+) create mode 100644 .github/problem-matchers/eslint-stylish.json diff --git a/.github/problem-matchers/eslint-stylish.json b/.github/problem-matchers/eslint-stylish.json new file mode 100644 index 0000000000000..a98afcfb11b94 --- /dev/null +++ b/.github/problem-matchers/eslint-stylish.json @@ -0,0 +1,22 @@ +{ + "problemMatcher": [ + { + "owner": "eslint-stylish", + "pattern": [ + { + "regexp": "^\\s*([^\\s].*)$", + "file": 1 + }, + { + "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index 88445a478a5c0..a49ce67b62f85 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -62,6 +62,9 @@ jobs: curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]" + - name: Add ESLint problem matcher + shell: bash + run: echo "::add-matcher::src/electron/.github/problem-matchers/eslint-stylish.json" - name: Run Lint shell: bash run: | From 1487f5d8b481717f0583189d1558ca9fed5619fd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 13:48:09 +0200 Subject: [PATCH 253/339] docs: add documentation for `ImageView` (#47298) * docs: Add documentation for ImageView * docs: Add ImageView main process module list in README.md * test: Add some basic tests for ImageView * test: Fill out Window embedding tests to better reflect how someone might use an ImageView * docs: Add notes about using ImageView as a splash screen * docs: Update ImageView example to show a more complete splash screen example * docs: Remove view resizing logic since the ImageView automatically gets resized --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Will Anderson --- docs/README.md | 1 + docs/api/image-view.md | 61 ++++++++++++++++++++++++++ filenames.auto.gni | 1 + spec/api-image-view-spec.ts | 86 +++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 docs/api/image-view.md create mode 100644 spec/api-image-view-spec.ts diff --git a/docs/README.md b/docs/README.md index 01663cc017769..02d5d1331ca29 100644 --- a/docs/README.md +++ b/docs/README.md @@ -113,6 +113,7 @@ These individual tutorials expand on topics discussed in the guide above. * [dialog](api/dialog.md) * [globalShortcut](api/global-shortcut.md) * [inAppPurchase](api/in-app-purchase.md) +* [ImageView](api/image-view.md) * [ipcMain](api/ipc-main.md) * [Menu](api/menu.md) * [MenuItem](api/menu-item.md) diff --git a/docs/api/image-view.md b/docs/api/image-view.md new file mode 100644 index 0000000000000..854de3054601a --- /dev/null +++ b/docs/api/image-view.md @@ -0,0 +1,61 @@ +# ImageView + +> A View that displays an image. + +Process: [Main](../glossary.md#main-process) + +This module cannot be used until the `ready` event of the `app` +module is emitted. + +Useful for showing splash screens that will be swapped for `WebContentsView`s +when the content finishes loading. + +Note that `ImageView` is experimental and may be changed or removed in the future. + +```js +const { BaseWindow, ImageView, nativeImage, WebContentsView } = require('electron') +const path = require('node:path') + +const win = new BaseWindow({ width: 800, height: 600 }) + +// Create a "splash screen" image to display while the WebContentsView loads +const splashView = new ImageView() +const splashImage = nativeImage.createFromPath(path.join(__dirname, 'loading.png')) +splashView.setImage(splashImage) +win.setContentView(splashView) + +const webContentsView = new WebContentsView() +webContentsView.webContents.once('did-finish-load', () => { + // Now that the WebContentsView has loaded, swap out the "splash screen" ImageView + win.setContentView(webContentsView) +}) +webContentsView.webContents.loadURL('https://electronjs.org') +``` + +## Class: ImageView extends `View` + +> A View that displays an image. + +Process: [Main](../glossary.md#main-process) + +`ImageView` inherits from [`View`](view.md). + +`ImageView` is an [EventEmitter][event-emitter]. + +### `new ImageView()` _Experimental_ + +Creates an ImageView. + +### Instance Methods + +The following methods are available on instances of the `ImageView` class, in +addition to those inherited from [View](view.md): + +#### `image.setImage(image)` _Experimental_ + +* `image` NativeImage + +Sets the image for this `ImageView`. Note that only image formats supported by +`NativeImage` can be used with an `ImageView`. + +[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter diff --git a/filenames.auto.gni b/filenames.auto.gni index df963e2c831b6..ecdc659fd06a4 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -25,6 +25,7 @@ auto_filenames = { "docs/api/extensions-api.md", "docs/api/extensions.md", "docs/api/global-shortcut.md", + "docs/api/image-view.md", "docs/api/in-app-purchase.md", "docs/api/incoming-message.md", "docs/api/ipc-main-service-worker.md", diff --git a/spec/api-image-view-spec.ts b/spec/api-image-view-spec.ts new file mode 100644 index 0000000000000..45f7bfdb96c24 --- /dev/null +++ b/spec/api-image-view-spec.ts @@ -0,0 +1,86 @@ +import { nativeImage } from 'electron/common'; +import { BaseWindow, BrowserWindow, ImageView } from 'electron/main'; + +import { expect } from 'chai'; + +import * as path from 'node:path'; + +import { closeAllWindows } from './lib/window-helpers'; + +describe('ImageView', () => { + afterEach(async () => { + await closeAllWindows(); + }); + + it('can be instantiated with no arguments', () => { + // eslint-disable-next-line no-new + new ImageView(); + }); + + it('can set an empty NativeImage', () => { + const view = new ImageView(); + const image = nativeImage.createEmpty(); + view.setImage(image); + }); + + it('can set a NativeImage', () => { + const view = new ImageView(); + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + view.setImage(image); + }); + + it('can change its NativeImage', () => { + const view = new ImageView(); + const image1 = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const image2 = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'capybara.png')); + view.setImage(image1); + view.setImage(image2); + }); + + it('can be embedded in a BaseWindow', () => { + const w = new BaseWindow({ show: false }); + const view = new ImageView(); + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'capybara.png')); + view.setImage(image); + w.setContentView(view); + w.setContentSize(image.getSize().width, image.getSize().height); + view.setBounds({ + x: 0, + y: 0, + width: image.getSize().width, + height: image.getSize().height + }); + }); + + it('can be embedded in a BrowserWindow', () => { + const w = new BrowserWindow({ show: false }); + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const view = new ImageView(); + view.setImage(image); + w.contentView.addChildView(view); + w.setContentSize(image.getSize().width, image.getSize().height); + view.setBounds({ + x: 0, + y: 0, + width: image.getSize().width, + height: image.getSize().height + }); + + expect(w.contentView.children).to.include(view); + }); + + it('can be removed from a BrowserWindow', async () => { + const w = new BrowserWindow({ show: false }); + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const view = new ImageView(); + view.setImage(image); + + w.contentView.addChildView(view); + expect(w.contentView.children).to.include(view); + + await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'blank.html')); + + w.contentView.removeChildView(view); + expect(w.contentView.children).to.not.include(view); + }); +}); From 2a707ffbd8cfe0a56c81c3af26a357a2e62d9a3e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 10:46:53 -0400 Subject: [PATCH 254/339] fix: Squirrel.Mac crash when zip extraction fails (#47300) * fix: Squirrel.Mac crash when zip extraction process fails to launch Co-authored-by: Niklas Wenzel * chore: add end-to-end test Co-authored-by: Niklas Wenzel --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- patches/squirrel.mac/.patches | 1 + ...ss_to_extract_zip_cannot_be_launched.patch | 30 +++++++++++++ spec/api-autoupdater-darwin-spec.ts | 45 +++++++++++++++++++ .../auto-update/sandbox/block-ditto.sb | 5 +++ 4 files changed, 81 insertions(+) create mode 100644 patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch create mode 100644 spec/fixtures/auto-update/sandbox/block-ditto.sb diff --git a/patches/squirrel.mac/.patches b/patches/squirrel.mac/.patches index a86478c8892a4..4b47e7da493ea 100644 --- a/patches/squirrel.mac/.patches +++ b/patches/squirrel.mac/.patches @@ -7,3 +7,4 @@ fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch feat_add_ability_to_prevent_version_downgrades.patch refactor_use_non-deprecated_nskeyedarchiver_apis.patch chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch +fix_crash_when_process_to_extract_zip_cannot_be_launched.patch diff --git a/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch b/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch new file mode 100644 index 0000000000000..a9438c7b068c8 --- /dev/null +++ b/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Niklas Wenzel +Date: Tue, 27 May 2025 02:03:54 +0200 +Subject: fix: crash when process to extract zip cannot be launched + +Fixes https://github.com/electron/electron/issues/47270 + +diff --git a/Squirrel/SQRLZipArchiver.m b/Squirrel/SQRLZipArchiver.m +index 68f5dac8e553638f41306956df9d38eeda18f8f2..a9cd676df63e19edf9e20473d27b85591c7cb49e 100644 +--- a/Squirrel/SQRLZipArchiver.m ++++ b/Squirrel/SQRLZipArchiver.m +@@ -153,7 +153,17 @@ - (RACSignal *)launchWithArguments:(NSArray *)arguments { + setNameWithFormat:@"-launchWithArguments: %@", arguments]; + + self.dittoTask.arguments = arguments; +- [self.dittoTask launch]; ++ ++ NSError *launchError = nil; ++ ++ if (![self.dittoTask launchAndReturnError:&launchError]) { ++ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; ++ userInfo[NSLocalizedDescriptionKey] = launchError.localizedDescription; ++ ++ NSLog(@"Starting ditto task failed with error: %@", launchError.localizedDescription); ++ ++ return [RACSignal error:[NSError errorWithDomain:SQRLZipArchiverErrorDomain code:SQRLZipArchiverShellTaskFailed userInfo:userInfo]]; ++ } + + return signal; + } diff --git a/spec/api-autoupdater-darwin-spec.ts b/spec/api-autoupdater-darwin-spec.ts index 24925c503c5b9..24709ef21b91d 100644 --- a/spec/api-autoupdater-darwin-spec.ts +++ b/spec/api-autoupdater-darwin-spec.ts @@ -42,6 +42,16 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () { return cp.spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args); }; + const launchAppSandboxed = (appPath: string, profilePath: string, args: string[] = []) => { + return spawn('/usr/bin/sandbox-exec', [ + '-f', + profilePath, + path.resolve(appPath, 'Contents/MacOS/Electron'), + ...args, + '--no-sandbox' + ]); + }; + const getRunningShipIts = async (appPath: string) => { const processes = await psList(); const activeShipIts = processes.filter(p => p.cmd?.includes('Squirrel.framework/Resources/ShipIt com.github.Electron.ShipIt') && p.cmd!.startsWith(appPath)); @@ -740,6 +750,41 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () { }); }); + it('should hit the download endpoint when an update is available and fail when the zip extraction process fails to launch', async () => { + await withUpdatableApp({ + nextVersion: '2.0.0', + startFixture: 'update', + endFixture: 'update' + }, async (appPath, updateZipPath) => { + server.get('/update-file', (req, res) => { + res.download(updateZipPath); + }); + server.get('/update-check', (req, res) => { + res.json({ + url: `http://localhost:${port}/update-file`, + name: 'My Release Name', + notes: 'Theses are some release notes innit', + pub_date: (new Date()).toString() + }); + }); + const launchResult = await launchAppSandboxed( + appPath, + path.resolve(__dirname, 'fixtures/auto-update/sandbox/block-ditto.sb'), + [`http://localhost:${port}/update-check`] + ); + logOnError(launchResult, () => { + expect(launchResult).to.have.property('code', 1); + expect(launchResult.out).to.include('Starting ditto task failed with error:'); + expect(launchResult.out).to.include('SQRLZipArchiverErrorDomain'); + expect(requests).to.have.lengthOf(2); + expect(requests[0]).to.have.property('url', '/update-check'); + expect(requests[1]).to.have.property('url', '/update-file'); + expect(requests[0].header('user-agent')).to.include('Electron/'); + expect(requests[1].header('user-agent')).to.include('Electron/'); + }); + }); + }); + it('should hit the download endpoint when an update is available and update successfully when the zip is provided with JSON update mode', async () => { await withUpdatableApp({ nextVersion: '2.0.0', diff --git a/spec/fixtures/auto-update/sandbox/block-ditto.sb b/spec/fixtures/auto-update/sandbox/block-ditto.sb new file mode 100644 index 0000000000000..79194bdbd03ac --- /dev/null +++ b/spec/fixtures/auto-update/sandbox/block-ditto.sb @@ -0,0 +1,5 @@ +(version 1) +(allow default) +(deny process-exec + (literal "/usr/bin/ditto") +) From 8988a372dacaba2129116a2349daba73fc53b723 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:54:08 +0200 Subject: [PATCH 255/339] docs: correct 'select-bluetooth-device' requirement (#47334) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/web-contents.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 3e89f5e6f7abc..85d2ee83406eb 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -839,9 +839,10 @@ Emitted when a bluetooth device needs to be selected when a call to the `deviceId` of the device to be selected. Passing an empty string to `callback` will cancel the request. -If an event listener is not added for this event, or if `event.preventDefault` -is not called when handling this event, the first available device will be -automatically selected. +If no event listener is added for this event, all bluetooth requests will be cancelled. + +If `event.preventDefault` is not called when handling this event, the first available +device will be automatically selected. Due to the nature of bluetooth, scanning for devices when `navigator.bluetooth.requestDevice` is called may take time and will cause From 492589670b7dbaf291ff741305194b0c96e80cb7 Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Wed, 4 Jun 2025 01:28:25 +0200 Subject: [PATCH 256/339] chore: cherry-pick 1 changes from 1-M137 (#47353) * chore: [36-x-y] cherry-pick 1 changes from 1-M137 * 7bc0a67ebfbf from v8 * chore: update patches --- patches/v8/.patches | 1 + patches/v8/cherry-pick-7bc0a67ebfbf.patch | 53 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 patches/v8/cherry-pick-7bc0a67ebfbf.patch diff --git a/patches/v8/.patches b/patches/v8/.patches index 199780cede8b0..68cfde44f970f 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,2 +1,3 @@ chore_allow_customizing_microtask_policy_per_context.patch enable_--perf-prof_flag_on_macos.patch +cherry-pick-7bc0a67ebfbf.patch diff --git a/patches/v8/cherry-pick-7bc0a67ebfbf.patch b/patches/v8/cherry-pick-7bc0a67ebfbf.patch new file mode 100644 index 0000000000000..ff5e0b4f29805 --- /dev/null +++ b/patches/v8/cherry-pick-7bc0a67ebfbf.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Leszek Swirski +Date: Tue, 27 May 2025 20:33:19 +0200 +Subject: Weaken alias analysis in store-store elimination + +Bug: 420636529 +Change-Id: I7c5a8f47960708cecbb27d811eedc7f754933deb +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594051 +Reviewed-by: Shu-yu Guo +Auto-Submit: Leszek Swirski +Commit-Queue: Leszek Swirski +Cr-Commit-Position: refs/heads/main@{#100530} + +diff --git a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h +index 45654a022fbaa67634d68d7d6e9dab7a5a50cb28..e058a41f23e29bbe4f5098608b340ccfef50bf98 100644 +--- a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h ++++ b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h +@@ -325,10 +325,11 @@ class RedundantStoreAnalysis { + // TODO(nicohartmann@): Use the new effect flags to distinguish heap + // access once available. + const bool is_on_heap_store = store.kind.tagged_base; +- const bool is_field_store = !store.index().valid(); ++ const bool is_fixed_offset_store = !store.index().valid(); + const uint8_t size = store.stored_rep.SizeInBytes(); +- // For now we consider only stores of fields of objects on the heap. +- if (is_on_heap_store && is_field_store) { ++ // For now we consider only stores of fixed offsets of objects on the ++ // heap. ++ if (is_on_heap_store && is_fixed_offset_store) { + bool is_eliminable_store = false; + switch (table_.GetObservability(store.base(), store.offset, size)) { + case StoreObservability::kUnobservable: +@@ -415,11 +416,16 @@ class RedundantStoreAnalysis { + // TODO(nicohartmann@): Use the new effect flags to distinguish heap + // access once available. + const bool is_on_heap_load = load.kind.tagged_base; +- const bool is_field_load = !load.index().valid(); ++ const bool is_fixed_offset_load = !load.index().valid(); + // For now we consider only loads of fields of objects on the heap. +- if (is_on_heap_load && is_field_load) { +- table_.MarkPotentiallyAliasingStoresAsObservable(load.base(), +- load.offset); ++ if (is_on_heap_load) { ++ if (is_fixed_offset_load) { ++ table_.MarkPotentiallyAliasingStoresAsObservable(load.base(), ++ load.offset); ++ } else { ++ // A dynamically indexed load might alias any fixed offset. ++ table_.MarkAllStoresAsObservable(); ++ } + } + break; + } From 2336cd67b7cd11674927a9a187d0a20210e29fe6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:08:44 +0200 Subject: [PATCH 257/339] fix: addChildView() crashes when adding a closed WebContentsView (#47340) fix: addChildView() crashes when add a closed WebContentsView Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Sida Zhu --- shell/browser/api/electron_api_view.cc | 6 ++++++ spec/api-web-contents-view-spec.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/shell/browser/api/electron_api_view.cc b/shell/browser/api/electron_api_view.cc index 661347396826b..4ddf0badd98a4 100644 --- a/shell/browser/api/electron_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -216,6 +216,12 @@ void View::AddChildViewAt(gin::Handle child, if (!view_) return; + if (!child->view()) { + gin_helper::ErrorThrower(isolate()).ThrowError( + "Can't add a destroyed child view to a parent view"); + return; + } + // This will CHECK and crash in View::AddChildViewAtImpl if not handled here. if (view_ == child->view()) { gin_helper::ErrorThrower(isolate()).ThrowError( diff --git a/spec/api-web-contents-view-spec.ts b/spec/api-web-contents-view-spec.ts index 077cb96e30d7b..63f45c140edd1 100644 --- a/spec/api-web-contents-view-spec.ts +++ b/spec/api-web-contents-view-spec.ts @@ -55,6 +55,20 @@ describe('WebContentsView', () => { })).to.throw('options.webContents is already attached to a window'); }); + it('should throw an error when adding a destroyed child view to the parent view', async () => { + const browserWindow = new BrowserWindow(); + + const webContentsView = new WebContentsView(); + webContentsView.webContents.loadURL('about:blank'); + webContentsView.webContents.destroy(); + + const destroyed = once(webContentsView.webContents, 'destroyed'); + await destroyed; + expect(() => browserWindow.contentView.addChildView(webContentsView)).to.throw( + 'Can\'t add a destroyed child view to a parent view' + ); + }); + it('should throw error when created with already attached webContents to other WebContentsView', () => { const browserWindow = new BrowserWindow(); From 63c7215cd17bca3f186a82f9fe737ed23a07be31 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:03:35 +0200 Subject: [PATCH 258/339] chore: bump node to v22.16.0 (36-x-y) (#47214) * chore: bump node in DEPS to v22.16.0 * crypto: remove BoringSSL dh-primes addition https://github.com/nodejs/node/pull/57023 * tools: enable linter in test/fixtures/test\-runner/output https://github.com/nodejs/node/pull/57698 * src: improve thread safety of TaskQueue https://github.com/nodejs/node/pull/57910 * buffer: define global v8::CFunction objects as const https://github.com/nodejs/node/pull/57676 * src: disable abseil deadlock detection https://github.com/nodejs/node/pull/57582 * zlib: fix pointer alignment https://github.com/nodejs/node/pull/57727 * chore: fixup patch indices * src: set default config as node.config.json https://github.com/nodejs/node/pull/57171 * src: update std::vector> to use v8::LocalVector https://github.com/nodejs/node/pull/57578 * test: disable chmod tests failing in Docker https://github.com/nodejs/node/issues/58326 --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- DEPS | 2 +- patches/node/.patches | 1 - ..._to_foreground_task_runner_signature.patch | 8 +-- patches/node/build_add_gn_build_files.patch | 25 +++++---- ...w_unbundling_of_node_js_dependencies.patch | 6 +-- .../build_compile_with_c_20_support.patch | 2 +- patches/node/build_enable_perfetto.patch | 6 +-- ...compilation_fails_if_not_using_a_new.patch | 6 +-- ...f_original-fs_and_custom_embedder_js.patch | 6 +-- ...o_use_custom_inspector_protocol_path.patch | 4 +- ...e_clang_as_default_compiler_on_macos.patch | 2 +- ...de_entrypoint_to_be_a_builtin_module.patch | 4 +- ..._node_tests_set_electron_run_as_node.patch | 11 ++-- ...cli_move_--trace-atomics-wait_to_eol.patch | 20 +++---- .../node/cli_remove_deprecated_v8_flag.patch | 8 +-- ..._values_for_variables_in_common_gypi.patch | 2 +- ...d_source_location_for_v8_task_runner.patch | 24 ++++----- ...ssert_module_in_the_renderer_process.patch | 4 +- .../node/fix_cppgc_initializing_twice.patch | 4 +- ..._do_not_resolve_electron_entrypoints.patch | 2 +- ...separent_bails_on_resource_path_exit.patch | 6 +-- ...se_readfilesync_override_for_modules.patch | 18 +++---- ...ingssl_and_openssl_incompatibilities.patch | 46 +++++++--------- .../fix_remove_fastapitypedarray_usage.patch | 18 +++---- ...rmony-import-assertions_from_node_cc.patch | 4 +- .../pass_all_globals_through_require.patch | 2 +- ...ch_cppgc_heap_on_v8_isolate_creation.patch | 52 +++++++++++-------- ..._on_wrapper-descriptor-based_cppheap.patch | 4 +- ...ted_fields_of_fastapicallbackoptions.patch | 2 +- .../node/support_v8_sandboxed_pointers.patch | 12 ++--- patches/node/zlib_fix_pointer_alignment.patch | 50 ------------------ script/node-disabled-tests.json | 2 + shell/common/node_bindings.cc | 9 ++-- shell/common/node_util.cc | 4 +- shell/common/node_util.h | 4 +- .../electron_sandboxed_renderer_client.cc | 6 +-- shell/renderer/preload_realm_context.cc | 6 +-- shell/renderer/renderer_client_base.cc | 8 +-- 38 files changed, 177 insertions(+), 223 deletions(-) delete mode 100644 patches/node/zlib_fix_pointer_alignment.patch diff --git a/DEPS b/DEPS index 6e927afa7f50b..70868302514f5 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.149', 'node_version': - 'v22.15.1', + 'v22.16.0', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/patches/node/.patches b/patches/node/.patches index 7b326ff7bbbd2..80c88af3b6167 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -47,5 +47,4 @@ fix_ensure_traverseparent_bails_on_resource_path_exit.patch cli_move_--trace-atomics-wait_to_eol.patch fix_cppgc_initializing_twice.patch fix_task_starvation_in_inspector_context_test.patch -zlib_fix_pointer_alignment.patch fix_expose_readfilesync_override_for_modules.patch diff --git a/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch b/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch index 43b5a271bedff..e1497804ce738 100644 --- a/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch +++ b/patches/node/add_v8_taskpirority_to_foreground_task_runner_signature.patch @@ -8,10 +8,10 @@ naturally upstream, and we will be able to remove this patch in a future Node.js upgrade. diff --git a/src/node_platform.cc b/src/node_platform.cc -index 65a9b79ae6ac8b7589e8f8109a709acb41d12b97..743ac069ad579a208a632ef5096ae46c8a0dfd74 100644 +index b438b3774d0aa7680fdbc6c6bf39a87893d221b2..ec355061825fb861c17fa2e6cc967b4c7b8d4586 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc -@@ -556,8 +556,8 @@ bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { +@@ -687,8 +687,8 @@ bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return ForIsolate(isolate)->IdleTasksEnabled(); } @@ -23,10 +23,10 @@ index 65a9b79ae6ac8b7589e8f8109a709acb41d12b97..743ac069ad579a208a632ef5096ae46c } diff --git a/src/node_platform.h b/src/node_platform.h -index dde2d1b5687a5b52a4f09183bb4ff88d7d3e4d01..0a99f5b4b5eeb221ef3a34db7a50955c32d3c163 100644 +index a0222b4a1b074c6708e390d58d04221717069ac1..8015ca1801573c3a7c4a5db6d0f10b4016a9267c 100644 --- a/src/node_platform.h +++ b/src/node_platform.h -@@ -177,7 +177,7 @@ class NodePlatform : public MultiIsolatePlatform { +@@ -213,7 +213,7 @@ class NodePlatform : public MultiIsolatePlatform { void (*callback)(void*), void* data) override; std::shared_ptr GetForegroundTaskRunner( diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index d1d2ec6d8a637..6f6e1a48a0662 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -55,10 +55,10 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f assert(!node_enable_inspector || node_use_openssl, diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index e85860de93dd5753dd4542ecee9f0888af93898a..04eab49c368c8f86837ed2c1384bf3c63e4bde24 100644 +index defb657a62a0316224a02b68505ac1142fd89d03..d637faac88875bfa110e2b8d1f53962061d98279 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -783,6 +783,7 @@ void BuiltinLoader::RegisterExternalReferences( +@@ -785,6 +785,7 @@ void BuiltinLoader::RegisterExternalReferences( registry->Register(GetNatives); RegisterExternalReferencesForInternalizedBuiltinCode(registry); @@ -67,7 +67,7 @@ index e85860de93dd5753dd4542ecee9f0888af93898a..04eab49c368c8f86837ed2c1384bf3c6 } // namespace builtins diff --git a/src/node_builtins.h b/src/node_builtins.h -index a73de23a1debfdac66873e0baccf882e383bfc36..7ac5291be093773ee7efd39e77e01bf5d5ce5247 100644 +index f9426599f2d5dc6ad061407f0c4eb2c9203a4433..302030f610965f07dd6998d282275c1bdf738009 100644 --- a/src/node_builtins.h +++ b/src/node_builtins.h @@ -74,6 +74,8 @@ using BuiltinCodeCacheMap = @@ -258,10 +258,10 @@ index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd1 if sys.platform == 'win32': files = [ x.replace('\\', '/') for x in files ] diff --git a/unofficial.gni b/unofficial.gni -index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50ae50a3d8 100644 +index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187f3c04c5c 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -142,32 +142,39 @@ template("node_gn_build") { +@@ -142,32 +142,42 @@ template("node_gn_build") { public_configs = [ ":node_external_config", "deps/googletest:googletest_config", @@ -296,7 +296,10 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 "$node_v8_path:v8_libplatform", ] -+ cflags_cc = [ "-Wno-unguarded-availability-new" ] ++ cflags_cc = [ ++ "-Wno-unguarded-availability-new", ++ "-Wno-return-stack-address" ++ ] + sources = [ + "src/node_snapshot_stub.cc", @@ -304,7 +307,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 "$target_gen_dir/node_javascript.cc", ] + gypi_values.node_sources -@@ -190,7 +197,7 @@ template("node_gn_build") { +@@ -190,7 +200,7 @@ template("node_gn_build") { } if (node_use_openssl) { deps += [ "deps/ncrypto" ] @@ -313,7 +316,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 sources += gypi_values.node_crypto_sources } if (node_enable_inspector) { -@@ -214,6 +221,10 @@ template("node_gn_build") { +@@ -214,6 +224,10 @@ template("node_gn_build") { } } @@ -324,7 +327,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 executable(target_name) { forward_variables_from(invoker, "*") -@@ -288,6 +299,7 @@ template("node_gn_build") { +@@ -288,6 +302,7 @@ template("node_gn_build") { } executable("node_js2c") { @@ -332,7 +335,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 deps = [ "deps/uv", "$node_simdutf_path", -@@ -298,26 +310,75 @@ template("node_gn_build") { +@@ -298,26 +313,75 @@ template("node_gn_build") { "src/embedded_data.cc", "src/embedded_data.h", ] @@ -418,7 +421,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..672e97436d9220e8d5046b0c92025f50 outputs = [ "$target_gen_dir/node_javascript.cc" ] # Get the path to node_js2c executable of the host toolchain. -@@ -331,11 +392,11 @@ template("node_gn_build") { +@@ -331,11 +395,11 @@ template("node_gn_build") { get_label_info(":node_js2c($host_toolchain)", "name") + host_executable_suffix diff --git a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch index a38d9cc5f0074..c36f65af02e60 100644 --- a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch +++ b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch @@ -14,7 +14,7 @@ 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 672e97436d9220e8d5046b0c92025f50ae50a3d8..a8ce18acfe333350f91b3e5f235db5f756b2e34a 100644 +index e17e4f043af6e4047ab82723ffd83187f3c04c5c..d591dfc99fdea4f830008502786ee44d863a31fc 100644 --- a/unofficial.gni +++ b/unofficial.gni @@ -155,7 +155,6 @@ template("node_gn_build") { @@ -25,7 +25,7 @@ index 672e97436d9220e8d5046b0c92025f50ae50a3d8..a8ce18acfe333350f91b3e5f235db5f7 "deps/nbytes", "deps/nghttp2", "deps/postject", -@@ -191,7 +190,17 @@ template("node_gn_build") { +@@ -194,7 +193,17 @@ template("node_gn_build") { configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] configs += [ "//build/config/gcc:symbol_visibility_default" ] } @@ -44,7 +44,7 @@ index 672e97436d9220e8d5046b0c92025f50ae50a3d8..a8ce18acfe333350f91b3e5f235db5f7 if (v8_enable_i18n_support) { deps += [ "//third_party/icu" ] } -@@ -219,6 +228,19 @@ template("node_gn_build") { +@@ -222,6 +231,19 @@ template("node_gn_build") { sources += node_inspector.node_inspector_sources + node_inspector.node_inspector_generated_sources } diff --git a/patches/node/build_compile_with_c_20_support.patch b/patches/node/build_compile_with_c_20_support.patch index a21de5e73697b..10aec30f9fc6c 100644 --- a/patches/node/build_compile_with_c_20_support.patch +++ b/patches/node/build_compile_with_c_20_support.patch @@ -10,7 +10,7 @@ V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8 This can be removed when Electron upgrades to a version of Node.js containing the required V8 version. diff --git a/common.gypi b/common.gypi -index 53016fc79c3d914982abeb61bf0a76181024e2bf..99b147482b636706b1372b89298f35b60ca2bb31 100644 +index f3476a91e4c3cda7cecf49e07bb594a167ac46ef..de73f6c18131f43e6fe3107c866599aa3398cf10 100644 --- a/common.gypi +++ b/common.gypi @@ -530,7 +530,7 @@ diff --git a/patches/node/build_enable_perfetto.patch b/patches/node/build_enable_perfetto.patch index 6aab4ea140fe8..f0b97e2c4acae 100644 --- a/patches/node/build_enable_perfetto.patch +++ b/patches/node/build_enable_perfetto.patch @@ -64,10 +64,10 @@ index 251f51ec454f9cba4023b8b6729241ee753aac13..1de8cac6e3953ce9cab9db03530da327 module.exports = { diff --git a/node.gyp b/node.gyp -index ec1f90b73f7d119b2c0e0207a5e36f3cec7295e9..66244b6638e34536aed397f56c6a4570a73e9b90 100644 +index ad010a8d99cf08013b7202eddce66e5b3885652d..d735b887d05ddfadec8e56dd8eae09646890aa84 100644 --- a/node.gyp +++ b/node.gyp -@@ -175,7 +175,6 @@ +@@ -176,7 +176,6 @@ 'src/timers.cc', 'src/timer_wrap.cc', 'src/tracing/agent.cc', @@ -75,7 +75,7 @@ index ec1f90b73f7d119b2c0e0207a5e36f3cec7295e9..66244b6638e34536aed397f56c6a4570 'src/tracing/node_trace_writer.cc', 'src/tracing/trace_event.cc', 'src/tracing/traced_value.cc', -@@ -303,7 +302,6 @@ +@@ -305,7 +304,6 @@ 'src/tcp_wrap.h', 'src/timers.h', 'src/tracing/agent.h', diff --git a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch index 8b09ad639398a..0527e9476f670 100644 --- a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch +++ b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch @@ -7,7 +7,7 @@ Subject: build: ensure native module compilation fails if not using a new This should not be upstreamed, it is a quality-of-life patch for downstream module builders. diff --git a/common.gypi b/common.gypi -index f2a45f0f0bbfce93e61d3696a18425af4d022a00..53016fc79c3d914982abeb61bf0a76181024e2bf 100644 +index d9c0b721fe0a629a30efb3c4e04905176ca0a7f5..f3476a91e4c3cda7cecf49e07bb594a167ac46ef 100644 --- a/common.gypi +++ b/common.gypi @@ -88,6 +88,8 @@ @@ -40,7 +40,7 @@ index f2a45f0f0bbfce93e61d3696a18425af4d022a00..53016fc79c3d914982abeb61bf0a7618 # list in v8/BUILD.gn. ['v8_enable_v8_checks == 1', { diff --git a/configure.py b/configure.py -index 95faeeef3867cbf3ca4b1857d893aa127d550a2f..b36f63a5482074f79a20709b8c4774cb6dadec52 100755 +index 932484674e5b15b765b8bfe307bdf99b49b5039f..befaa85527b9ebebad226e603586e23d04ec1e51 100755 --- a/configure.py +++ b/configure.py @@ -1698,6 +1698,7 @@ def configure_library(lib, output, pkgname=None): @@ -52,7 +52,7 @@ index 95faeeef3867cbf3ca4b1857d893aa127d550a2f..b36f63a5482074f79a20709b8c4774cb o['variables']['v8_enable_javascript_promise_hooks'] = 1 o['variables']['v8_enable_lite_mode'] = 1 if options.v8_lite_mode else 0 diff --git a/src/node.h b/src/node.h -index 8b77f7cb4d53105f42ba76d99a76a98b7a73789f..bdc77f8eb7abffa9e6c98cd254daedad3e44b981 100644 +index 835c78145956de3d8c52b6cc0581bcfef600f90b..174fd4d1af4c8cd75aec09f4548a674fd5539fb2 100644 --- a/src/node.h +++ b/src/node.h @@ -22,6 +22,12 @@ diff --git a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch index b20f8418dab33..c4c54c21fdfe0 100644 --- a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch +++ b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch @@ -34,10 +34,10 @@ index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b let kResistStopPropagation; diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index 04eab49c368c8f86837ed2c1384bf3c63e4bde24..c3d2b3c90c206dd81a3d8aa6c14fdf4678a1cddd 100644 +index d637faac88875bfa110e2b8d1f53962061d98279..e0b58c4d0ac5640a677c22d710f88f1b318378d7 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -34,6 +34,7 @@ using v8::Value; +@@ -35,6 +35,7 @@ using v8::Value; BuiltinLoader::BuiltinLoader() : config_(GetConfig()), code_cache_(std::make_shared()) { LoadJavaScriptSource(); @@ -46,7 +46,7 @@ index 04eab49c368c8f86837ed2c1384bf3c63e4bde24..c3d2b3c90c206dd81a3d8aa6c14fdf46 AddExternalizedBuiltin( "internal/deps/cjs-module-lexer/lexer", diff --git a/src/node_builtins.h b/src/node_builtins.h -index 7ac5291be093773ee7efd39e77e01bf5d5ce5247..c3c987d535285be84026ad0c633650bd2067d22d 100644 +index 302030f610965f07dd6998d282275c1bdf738009..35cb7766eeccc62dd2f0ce9484a2f1ec7beccc05 100644 --- a/src/node_builtins.h +++ b/src/node_builtins.h @@ -138,6 +138,7 @@ class NODE_EXTERN_PRIVATE BuiltinLoader { diff --git a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch index 7f4a2c213c82a..8e3aa3dbea37a 100644 --- a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch +++ b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch @@ -65,10 +65,10 @@ index 3d7aa148678b2646b88fa7c32abec91791b02b82..4810d93eb971b253f7dadff7011a632f gypi_values = exec_script( "../../tools/gypi_to_gn.py", diff --git a/unofficial.gni b/unofficial.gni -index a8ce18acfe333350f91b3e5f235db5f756b2e34a..6bcc40b282543fc40f80c5c6659de658209844b8 100644 +index d591dfc99fdea4f830008502786ee44d863a31fc..9e26399482d6a1cdb843efb72c152d5cdd5e08ea 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -211,13 +211,14 @@ template("node_gn_build") { +@@ -214,13 +214,14 @@ template("node_gn_build") { } if (node_enable_inspector) { deps += [ diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index 7c7f11672aebd..73e7e840edf73 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,7 +11,7 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index 99b147482b636706b1372b89298f35b60ca2bb31..5024e5fb0aee210f4986572638a523db6d26b4cc 100644 +index de73f6c18131f43e6fe3107c866599aa3398cf10..e2171e14b9e29dfc3c629f8164545d56d5e9057e 100644 --- a/common.gypi +++ b/common.gypi @@ -127,6 +127,7 @@ diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch index d8640574d1cea..ae864ce06db1d 100644 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch @@ -8,10 +8,10 @@ they use themselves as the entry point. We should try to upstream some form of this. diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js -index d1c05d1717cdc825c4e48885c963c9ed65bcf51c..278665921c5160ff10b3178db27d4df319fab6b3 100644 +index 4e7be0594ca1e1ceaf1963debbce46783893ed77..a6df0672bf6ae6e9a74ebbb0e4debff63599cc99 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js -@@ -243,12 +243,14 @@ function patchProcessObject(expandArgv1) { +@@ -245,12 +245,14 @@ function patchProcessObject(expandArgv1) { // the entry point. if (expandArgv1 && process.argv[1] && process.argv[1][0] !== '-') { // Expand process.argv[1] into a full path. diff --git a/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch b/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch index ef4d76a3e8049..4890f20896718 100644 --- a/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch +++ b/patches/node/ci_ensure_node_tests_set_electron_run_as_node.patch @@ -8,15 +8,18 @@ which causes the `ELECTRON_RUN_AS_NODE` variable to be lost. This patch re-injects it. diff --git a/test/fixtures/test-runner/output/arbitrary-output-colored.js b/test/fixtures/test-runner/output/arbitrary-output-colored.js -index af23e674cb361ed81dafa22670d5633559cd1144..1dd59990cb7cdba8aecf4f499ee6b92e7cd41b30 100644 +index 444531da1df2f9bbbc19bb8a43fb6eb2d1802d1a..81b3b3e37e5664dc53bec987a2ce3a83a8da105f 100644 --- a/test/fixtures/test-runner/output/arbitrary-output-colored.js +++ b/test/fixtures/test-runner/output/arbitrary-output-colored.js -@@ -7,6 +7,6 @@ const fixtures = require('../../../common/fixtures'); +@@ -7,9 +7,9 @@ const fixtures = require('../../../common/fixtures'); (async function run() { const test = fixtures.path('test-runner/output/arbitrary-output-colored-1.js'); const reset = fixtures.path('test-runner/output/reset-color-depth.js'); - await once(spawn(process.execPath, ['-r', reset, '--test', test], { stdio: 'inherit' }), 'exit'); -- await once(spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit' }), 'exit'); + await once(spawn(process.execPath, ['-r', reset, '--test', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 }}), 'exit'); -+ await once(spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 } }), 'exit'); + await once( +- spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit' }), ++ spawn(process.execPath, ['-r', reset, '--test', '--test-reporter', 'tap', test], { stdio: 'inherit', env: { ELECTRON_RUN_AS_NODE: 1 }}), + 'exit', + ); })().then(common.mustCall()); diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch index 70ae224eebb1a..6eec17ed7bf01 100644 --- a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -15,10 +15,10 @@ Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 114b7bbf6b1e105fc1696ed8a064065db73ff519..ad863e52761332c3249a86af0e3d239cd0f73b03 100644 +index 121d8f2bbd2b1d93067a06a902b1e7b986bcdb49..3460ad33c6186dcc3aa3281d80b723a1cc1d50dd 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3313,7 +3313,6 @@ one is included in the list below. +@@ -3367,7 +3367,6 @@ one is included in the list below. * `--tls-min-v1.1` * `--tls-min-v1.2` * `--tls-min-v1.3` @@ -27,10 +27,10 @@ index 114b7bbf6b1e105fc1696ed8a064065db73ff519..ad863e52761332c3249a86af0e3d239c * `--trace-env-js-stack` * `--trace-env-native-stack` diff --git a/doc/node.1 b/doc/node.1 -index 9f534746ef9d9c1c1ee2edd6c195573a2e228600..e01fc511a1034518c0fb9bc5fa925524aecad927 100644 +index 663d123ac728f097e8a76c94cf10c53d059983d7..497f5a61182beafbaa26b945181056353674cfc3 100644 --- a/doc/node.1 +++ b/doc/node.1 -@@ -533,11 +533,6 @@ but the option is supported for compatibility with older Node.js versions. +@@ -539,11 +539,6 @@ but the option is supported for compatibility with older Node.js versions. Set default minVersion to 'TLSv1.3'. Use to disable support for TLSv1.2 in favour of TLSv1.3, which is more secure. . @@ -43,10 +43,10 @@ index 9f534746ef9d9c1c1ee2edd6c195573a2e228600..e01fc511a1034518c0fb9bc5fa925524 Print stack traces for deprecations. . diff --git a/src/node.cc b/src/node.cc -index 2f58a6fa69069dabb99b5ddb8011991b07fa5f02..9f6fa646ba6673f67f5f625e157ed850633a26da 100644 +index 6f8f6386d0db8aef1e2e0126cc9064101cbe6112..bc670a6c8b5027417cdc35e1cd94a60f63fd342d 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -226,44 +226,6 @@ void Environment::WaitForInspectorFrontendByOptions() { +@@ -232,44 +232,6 @@ void Environment::WaitForInspectorFrontendByOptions() { } #endif // HAVE_INSPECTOR @@ -91,7 +91,7 @@ index 2f58a6fa69069dabb99b5ddb8011991b07fa5f02..9f6fa646ba6673f67f5f625e157ed850 void Environment::InitializeDiagnostics() { isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback( Environment::BuildEmbedderGraph, this); -@@ -272,17 +234,6 @@ void Environment::InitializeDiagnostics() { +@@ -278,17 +240,6 @@ void Environment::InitializeDiagnostics() { } if (options_->trace_uncaught) isolate_->SetCaptureStackTraceForUncaughtExceptions(true); @@ -110,10 +110,10 @@ index 2f58a6fa69069dabb99b5ddb8011991b07fa5f02..9f6fa646ba6673f67f5f625e157ed850 isolate_->SetPromiseHook(TracePromises); } diff --git a/src/node_options.cc b/src/node_options.cc -index 54b253aa54f5cdebdb04315f9c6c2506977555c0..acf390bd456c7ddfa6987e440fb45940aec6b1ff 100644 +index a9500716f2a955fc591628a969c5fba40783a2e7..b153d2c6a771e80bcdf5ed6adbc1cd225b3bf97e 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -762,10 +762,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { +@@ -770,10 +770,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "throw an exception on deprecations", &EnvironmentOptions::throw_deprecation, kAllowedInEnvvar); @@ -125,7 +125,7 @@ index 54b253aa54f5cdebdb04315f9c6c2506977555c0..acf390bd456c7ddfa6987e440fb45940 "show stack traces on deprecations", &EnvironmentOptions::trace_deprecation, diff --git a/src/node_options.h b/src/node_options.h -index 065457acfde6ba4d04ed570cc72005cfd2798fd5..150f833bb21bd6d37f652f0785a4a98f3de5f67d 100644 +index 60068b008b2e2a034c3f0c58b947a8d04d55e3b2..d821bc6a9adf28ea312a9c446f8acfc8ed586ae3 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -203,7 +203,6 @@ class EnvironmentOptions : public Options { diff --git a/patches/node/cli_remove_deprecated_v8_flag.patch b/patches/node/cli_remove_deprecated_v8_flag.patch index caff01e09ec1d..70a46ac99e195 100644 --- a/patches/node/cli_remove_deprecated_v8_flag.patch +++ b/patches/node/cli_remove_deprecated_v8_flag.patch @@ -18,10 +18,10 @@ Reviewed-By: Michaël Zasso Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 1b42c5a7f4715e56fa5bc39cd6f78a76473406f2..114b7bbf6b1e105fc1696ed8a064065db73ff519 100644 +index 6f984926a62973ba36bd3c27cc39b01f2bcac819..121d8f2bbd2b1d93067a06a902b1e7b986bcdb49 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3350,7 +3350,6 @@ V8 options that are allowed are: +@@ -3404,7 +3404,6 @@ V8 options that are allowed are: * `--disallow-code-generation-from-strings` * `--enable-etw-stack-walking` * `--expose-gc` @@ -30,10 +30,10 @@ index 1b42c5a7f4715e56fa5bc39cd6f78a76473406f2..114b7bbf6b1e105fc1696ed8a064065d * `--jitless` * `--max-old-space-size` diff --git a/src/node_options.cc b/src/node_options.cc -index b22fbb0a285f6f323779d6ebb2b027a3990b031e..54b253aa54f5cdebdb04315f9c6c2506977555c0 100644 +index bb1e80ece4158dfed1b8bab7dc6d00dd56505aac..a9500716f2a955fc591628a969c5fba40783a2e7 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -984,11 +984,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( +@@ -992,11 +992,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( "disallow eval and friends", V8Option{}, kAllowedInEnvvar); diff --git a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch index 45edf948a98da..f21f31edd6765 100644 --- a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch +++ b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch @@ -7,7 +7,7 @@ common.gypi is a file that's included in the node header bundle, despite the fact that we do not build node with gyp. diff --git a/common.gypi b/common.gypi -index 372409f4b09cc3b3be9809f697816498e5c021fe..f2a45f0f0bbfce93e61d3696a18425af4d022a00 100644 +index 393fe32765794fbc5e92690e968e3c18f0d749fa..d9c0b721fe0a629a30efb3c4e04905176ca0a7f5 100644 --- a/common.gypi +++ b/common.gypi @@ -90,6 +90,23 @@ diff --git a/patches/node/fix_add_source_location_for_v8_task_runner.patch b/patches/node/fix_add_source_location_for_v8_task_runner.patch index 35a8dba0ed365..43896b7b67e60 100644 --- a/patches/node/fix_add_source_location_for_v8_task_runner.patch +++ b/patches/node/fix_add_source_location_for_v8_task_runner.patch @@ -15,10 +15,10 @@ corresponding change. CL: https://chromium-review.googlesource.com/c/v8/v8/+/5300826 diff --git a/src/node_platform.cc b/src/node_platform.cc -index 00ca9757bc4d0cdeb03a3f32be3ef19077cb7969..65a9b79ae6ac8b7589e8f8109a709acb41d12b97 100644 +index 9c4c1e1db5fa7c0ca791e01d9be331e0957e9699..b438b3774d0aa7680fdbc6c6bf39a87893d221b2 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc -@@ -245,11 +245,13 @@ void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) { +@@ -307,11 +307,13 @@ void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) { platform_data->FlushForegroundTasksInternal(); } @@ -31,10 +31,10 @@ index 00ca9757bc4d0cdeb03a3f32be3ef19077cb7969..65a9b79ae6ac8b7589e8f8109a709acb -void PerIsolatePlatformData::PostTask(std::unique_ptr task) { +void PerIsolatePlatformData::PostTaskImpl(std::unique_ptr task, + const v8::SourceLocation& location) { - if (flush_tasks_ == nullptr) { - // V8 may post tasks during Isolate disposal. In that case, the only - // sensible path forward is to discard the task. -@@ -259,8 +261,10 @@ void PerIsolatePlatformData::PostTask(std::unique_ptr task) { + // The task can be posted from any V8 background worker thread, even when + // the foreground task runner is being cleaned up by Shutdown(). In that + // case, make sure we wait until the shutdown is completed (which leads +@@ -330,8 +332,10 @@ void PerIsolatePlatformData::PostTask(std::unique_ptr task) { uv_async_send(flush_tasks_); } @@ -44,10 +44,10 @@ index 00ca9757bc4d0cdeb03a3f32be3ef19077cb7969..65a9b79ae6ac8b7589e8f8109a709acb + std::unique_ptr task, + double delay_in_seconds, + const v8::SourceLocation& location) { - if (flush_tasks_ == nullptr) { - // V8 may post tasks during Isolate disposal. In that case, the only - // sensible path forward is to discard the task. -@@ -274,13 +278,15 @@ void PerIsolatePlatformData::PostDelayedTask( + if (debug_log_level_ != PlatformDebugLogLevel::kNone) { + fprintf(stderr, + "\nPerIsolatePlatformData::PostDelayedTaskImpl %p %f", +@@ -353,13 +357,15 @@ void PerIsolatePlatformData::PostDelayedTask( uv_async_send(flush_tasks_); } @@ -67,10 +67,10 @@ index 00ca9757bc4d0cdeb03a3f32be3ef19077cb7969..65a9b79ae6ac8b7589e8f8109a709acb } diff --git a/src/node_platform.h b/src/node_platform.h -index 77cb5e6e4f891c510cdaf7fd6175a1f00d9bc420..dde2d1b5687a5b52a4f09183bb4ff88d7d3e4d01 100644 +index af30ebeb0c8629ab86d1a55fd63610165abfbabf..a0222b4a1b074c6708e390d58d04221717069ac1 100644 --- a/src/node_platform.h +++ b/src/node_platform.h -@@ -59,18 +59,21 @@ class PerIsolatePlatformData : +@@ -80,18 +80,21 @@ class PerIsolatePlatformData : ~PerIsolatePlatformData() override; std::shared_ptr GetForegroundTaskRunner() override; diff --git a/patches/node/fix_assert_module_in_the_renderer_process.patch b/patches/node/fix_assert_module_in_the_renderer_process.patch index e6ab076547afe..63191d23c70b6 100644 --- a/patches/node/fix_assert_module_in_the_renderer_process.patch +++ b/patches/node/fix_assert_module_in_the_renderer_process.patch @@ -44,10 +44,10 @@ index 59b5a16f1309a5e4055bccfdb7a529045ad30402..bfdaf6211466a01b64b7942f7b16c480 let filename = call.getFileName(); const line = call.getLineNumber() - 1; diff --git a/src/node_options.cc b/src/node_options.cc -index 23cb558a22b4462f5ce4f74833d25c7f712f8139..b22fbb0a285f6f323779d6ebb2b027a3990b031e 100644 +index 8be78889e8234eb3100f309829bf7470db544dcd..bb1e80ece4158dfed1b8bab7dc6d00dd56505aac 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -1535,14 +1535,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { +@@ -1557,14 +1557,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { } Isolate* isolate = args.GetIsolate(); diff --git a/patches/node/fix_cppgc_initializing_twice.patch b/patches/node/fix_cppgc_initializing_twice.patch index 6ecf3b9675de9..432dce585764e 100644 --- a/patches/node/fix_cppgc_initializing_twice.patch +++ b/patches/node/fix_cppgc_initializing_twice.patch @@ -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 9f6fa646ba6673f67f5f625e157ed850633a26da..63a462876c00588d22abdd6ed8ee41ecf6590d03 100644 +index bc670a6c8b5027417cdc35e1cd94a60f63fd342d..49a9670de72c222d6b4a681be001efb4a2651ea3 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -1173,7 +1173,7 @@ InitializeOncePerProcessInternal(const std::vector& args, +@@ -1209,7 +1209,7 @@ InitializeOncePerProcessInternal(const std::vector& args, result->platform_ = per_process::v8_platform.Platform(); } diff --git a/patches/node/fix_do_not_resolve_electron_entrypoints.patch b/patches/node/fix_do_not_resolve_electron_entrypoints.patch index 68ec69cf3ee8f..679f1bf31632d 100644 --- a/patches/node/fix_do_not_resolve_electron_entrypoints.patch +++ b/patches/node/fix_do_not_resolve_electron_entrypoints.patch @@ -33,7 +33,7 @@ index 49aacb6262502ced54e817f99dd596db85b9659c..f9f065bb743275e9b2ce71375e6a9f06 if (!loaded) { module = new CJSModule(filename); diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js -index ab4783a7982b9feb8fa85b62e3e3b181f93309bd..34f91026451d7347ae278712d083e4fe281e50f3 100644 +index 02ba43adc2c8e92a78942bbb04023a16f5870ee9..bbf1ab69b884a9325bebdd07b2c4fd354eee946b 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -2,6 +2,7 @@ diff --git a/patches/node/fix_ensure_traverseparent_bails_on_resource_path_exit.patch b/patches/node/fix_ensure_traverseparent_bails_on_resource_path_exit.patch index 37e8ed1ecbc45..2a3a90362897e 100644 --- a/patches/node/fix_ensure_traverseparent_bails_on_resource_path_exit.patch +++ b/patches/node/fix_ensure_traverseparent_bails_on_resource_path_exit.patch @@ -8,10 +8,10 @@ resource path. This commit ensures that the TraverseParent function bails out if the parent path is outside of the resource path. diff --git a/src/node_modules.cc b/src/node_modules.cc -index 210a01d24e11764dc9fc37a77b354f11383693f8..4e9f70a1c41b44d2a1863b778d4f1e37279178d9 100644 +index 55d628f0c5e7f330e548878807de26d51ef025b5..c06779dea471b6f6a8dd29d4657162ef0faec043 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc -@@ -290,8 +290,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent( +@@ -291,8 +291,41 @@ const BindingData::PackageConfig* BindingData::TraverseParent( Realm* realm, const std::filesystem::path& check_path) { std::filesystem::path current_path = check_path; auto env = realm->env(); @@ -53,7 +53,7 @@ index 210a01d24e11764dc9fc37a77b354f11383693f8..4e9f70a1c41b44d2a1863b778d4f1e37 do { current_path = current_path.parent_path(); -@@ -310,6 +343,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent( +@@ -311,6 +344,12 @@ const BindingData::PackageConfig* BindingData::TraverseParent( return nullptr; } diff --git a/patches/node/fix_expose_readfilesync_override_for_modules.patch b/patches/node/fix_expose_readfilesync_override_for_modules.patch index c54548cd8b874..c2b377ee140aa 100644 --- a/patches/node/fix_expose_readfilesync_override_for_modules.patch +++ b/patches/node/fix_expose_readfilesync_override_for_modules.patch @@ -8,10 +8,10 @@ an API override to replace the native `ReadFileSync` in the `modules` binding. diff --git a/src/env_properties.h b/src/env_properties.h -index ba8c80ff2842c63f16cae51cfa8084617bb35bf5..820aebd18d22bcef4992b09ffc8924e9b758fd3e 100644 +index cbb7eab2df0416087cd3e6fb80eef2079143d9c8..7e9f5e977506149d69c6015e85d031770325e1da 100644 --- a/src/env_properties.h +++ b/src/env_properties.h -@@ -485,6 +485,7 @@ +@@ -501,6 +501,7 @@ V(maybe_cache_generated_source_map, v8::Function) \ V(messaging_deserialize_create_object, v8::Function) \ V(message_port, v8::Object) \ @@ -20,7 +20,7 @@ index ba8c80ff2842c63f16cae51cfa8084617bb35bf5..820aebd18d22bcef4992b09ffc8924e9 V(performance_entry_callback, v8::Function) \ V(prepare_stack_trace_callback, v8::Function) \ diff --git a/src/node_modules.cc b/src/node_modules.cc -index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4088e05ac 100644 +index c06779dea471b6f6a8dd29d4657162ef0faec043..6204986dc97686a248d6ae483f3a413ee5c51e47 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -21,6 +21,7 @@ namespace modules { @@ -31,7 +31,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; -@@ -88,6 +89,7 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { +@@ -89,6 +90,7 @@ Local BindingData::PackageConfig::Serialize(Realm* realm) const { const BindingData::PackageConfig* BindingData::GetPackageJSON( Realm* realm, std::string_view path, ErrorContext* error_context) { @@ -39,7 +39,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 auto binding_data = realm->GetBindingData(); auto cache_entry = binding_data->package_configs_.find(path.data()); -@@ -97,8 +99,36 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( +@@ -98,8 +100,36 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( PackageConfig package_config{}; package_config.file_path = path; @@ -77,7 +77,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 return nullptr; } // In some systems, std::string is annotated to generate an -@@ -248,6 +278,12 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( +@@ -249,6 +279,12 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON( return &cached.first->second; } @@ -90,7 +90,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 void BindingData::ReadPackageJSON(const FunctionCallbackInfo& args) { CHECK_GE(args.Length(), 1); // path, [is_esm, base, specifier] CHECK(args[0]->IsString()); // path -@@ -556,6 +592,8 @@ void GetCompileCacheDir(const FunctionCallbackInfo& args) { +@@ -643,6 +679,8 @@ void InitImportMetaPathHelpers(const FunctionCallbackInfo& args) { void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); @@ -99,7 +99,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 SetMethod(isolate, target, "readPackageJSON", ReadPackageJSON); SetMethod(isolate, target, -@@ -595,6 +633,8 @@ void BindingData::CreatePerContextProperties(Local target, +@@ -685,6 +723,8 @@ void BindingData::CreatePerContextProperties(Local target, void BindingData::RegisterExternalReferences( ExternalReferenceRegistry* registry) { @@ -109,7 +109,7 @@ index 4e9f70a1c41b44d2a1863b778d4f1e37279178d9..c56a32885b8debbd5b95a2c11f3838d4 registry->Register(GetNearestParentPackageJSONType); registry->Register(GetNearestParentPackageJSON); diff --git a/src/node_modules.h b/src/node_modules.h -index 17909b2270454b3275c7bf2e50d4b9b35673ecc8..3d5b0e3ac65524adfe221bfd6f85360dee1f0bee 100644 +index eb2900d8f8385238f89a6dcc972a28e5fcb1d288..e28f38d98f4f8749048af135f0dcbe55aa69c4fe 100644 --- a/src/node_modules.h +++ b/src/node_modules.h @@ -54,6 +54,8 @@ class BindingData : public SnapshotableObject { diff --git a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch index 8548df12be7ec..709e710e07aa5 100644 --- a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch +++ b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch @@ -17,20 +17,10 @@ Upstreams: - https://github.com/nodejs/node/pull/39136 diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc -index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac4e767d90 100644 +index 6f9406eecacb7411a2e84a7b51e60b726d1961f3..bffdb0259eeed7389adb54a8ff13a1ac4e767d90 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc -@@ -11,9 +11,6 @@ - #if OPENSSL_VERSION_MAJOR >= 3 - #include - #endif --#ifdef OPENSSL_IS_BORINGSSL --#include "dh-primes.h" --#endif // OPENSSL_IS_BORINGSSL - - namespace ncrypto { - namespace { -@@ -789,7 +786,7 @@ bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { +@@ -786,7 +786,7 @@ bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { bool ok = true; @@ -39,7 +29,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); if (i != 0) BIO_write(out.get(), ", ", 2); -@@ -813,7 +810,7 @@ bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) { +@@ -810,7 +810,7 @@ bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) { bool ok = true; @@ -48,7 +38,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac ACCESS_DESCRIPTION* desc = sk_ACCESS_DESCRIPTION_value(descs, i); if (i != 0) BIO_write(out.get(), "\n", 1); -@@ -955,13 +952,17 @@ BIOPointer X509View::getValidTo() const { +@@ -952,13 +952,17 @@ BIOPointer X509View::getValidTo() const { int64_t X509View::getValidToTime() const { struct tm tp; @@ -67,7 +57,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac return PortableTimeGM(&tp); } -@@ -1236,7 +1237,11 @@ BIOPointer BIOPointer::NewMem() { +@@ -1233,7 +1237,11 @@ BIOPointer BIOPointer::NewMem() { } BIOPointer BIOPointer::NewSecMem() { @@ -80,7 +70,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac } BIOPointer BIOPointer::New(const BIO_METHOD* method) { -@@ -1306,8 +1311,10 @@ BignumPointer DHPointer::FindGroup(const std::string_view name, +@@ -1303,8 +1311,10 @@ BignumPointer DHPointer::FindGroup(const std::string_view name, #define V(n, p) \ if (EqualNoCase(name, n)) return BignumPointer(p(nullptr)); if (option != FindGroupOption::NO_SMALL_PRIMES) { @@ -91,7 +81,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac V("modp5", BN_get_rfc3526_prime_1536); } V("modp14", BN_get_rfc3526_prime_2048); -@@ -1383,11 +1390,13 @@ DHPointer::CheckPublicKeyResult DHPointer::checkPublicKey( +@@ -1380,11 +1390,13 @@ DHPointer::CheckPublicKeyResult DHPointer::checkPublicKey( int codes = 0; if (DH_check_pub_key(dh_.get(), pub_key.get(), &codes) != 1) return DHPointer::CheckPublicKeyResult::CHECK_FAILED; @@ -106,7 +96,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac return DHPointer::CheckPublicKeyResult::INVALID; } return CheckPublicKeyResult::NONE; -@@ -2330,7 +2339,7 @@ const std::string_view SSLPointer::getClientHelloAlpn() const { +@@ -2327,7 +2339,7 @@ const std::string_view SSLPointer::getClientHelloAlpn() const { const unsigned char* buf; size_t len; size_t rem; @@ -115,7 +105,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac if (!SSL_client_hello_get0_ext( get(), TLSEXT_TYPE_application_layer_protocol_negotiation, -@@ -2343,6 +2352,8 @@ const std::string_view SSLPointer::getClientHelloAlpn() const { +@@ -2340,6 +2352,8 @@ const std::string_view SSLPointer::getClientHelloAlpn() const { len = (buf[0] << 8) | buf[1]; if (len + 2 != rem) return {}; return reinterpret_cast(buf + 3); @@ -124,7 +114,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac } const std::string_view SSLPointer::getClientHelloServerName() const { -@@ -2350,7 +2361,7 @@ const std::string_view SSLPointer::getClientHelloServerName() const { +@@ -2347,7 +2361,7 @@ const std::string_view SSLPointer::getClientHelloServerName() const { const unsigned char* buf; size_t len; size_t rem; @@ -133,7 +123,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac if (!SSL_client_hello_get0_ext(get(), TLSEXT_TYPE_server_name, &buf, &rem) || rem <= 2) { return {}; -@@ -2366,6 +2377,8 @@ const std::string_view SSLPointer::getClientHelloServerName() const { +@@ -2363,6 +2377,8 @@ const std::string_view SSLPointer::getClientHelloServerName() const { len = (*(buf + 3) << 8) | *(buf + 4); if (len + 2 > rem) return {}; return reinterpret_cast(buf + 5); @@ -142,7 +132,7 @@ index ce2e7b384eb1987ddb081f79884fb8cb62ade60b..bffdb0259eeed7389adb54a8ff13a1ac } std::optional SSLPointer::GetServerName( -@@ -2399,8 +2412,11 @@ bool SSLPointer::isServer() const { +@@ -2396,8 +2412,11 @@ bool SSLPointer::isServer() const { EVPKeyPointer SSLPointer::getPeerTempKey() const { if (!ssl_) return {}; EVP_PKEY* raw_key = nullptr; @@ -195,10 +185,10 @@ index 245a43920c7baf000ba63192a84a4c3fd219be7d..56a554175b805c1703f13d62041f8c80 # The location of simdutf - use the one from node's deps by default. node_simdutf_path = "$node_path/deps/simdutf" diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc -index 1754d1f71b8adbcb584bfe4606e2a341836fb671..ac0f529e75c30add0708dc20470846f2f56e4b86 100644 +index 2176fb6982484e2c42538478eeb4dd81c9d50ee1..c00d3616e08b00b1e0a3a29b2dbb5278e1e14fcc 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc -@@ -1033,7 +1033,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { +@@ -1027,7 +1027,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { return ThrowCryptoError(env, ERR_get_error()); } @@ -207,7 +197,7 @@ index 1754d1f71b8adbcb584bfe4606e2a341836fb671..ac0f529e75c30add0708dc20470846f2 int rsa_pkcs1_implicit_rejection = EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); // From the doc -2 means that the option is not supported. -@@ -1048,6 +1048,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { +@@ -1042,6 +1042,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); } @@ -565,10 +555,10 @@ index 6f8cb433ff8059c63d5cf16c8783139ae92fbf61..603ac3dde7d1a1109afbc451b69c8d09 #if NODE_OPENSSL_HAS_QUIC #include diff --git a/src/node_options.cc b/src/node_options.cc -index 1444acd59d42f64831cead5f153419f7c12a88bf..23cb558a22b4462f5ce4f74833d25c7f712f8139 100644 +index 3fc8194475ec0e8a9047c1f3da5d120f25d66190..8be78889e8234eb3100f309829bf7470db544dcd 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -6,7 +6,7 @@ +@@ -7,7 +7,7 @@ #include "node_external_reference.h" #include "node_internals.h" #include "node_sea.h" @@ -578,7 +568,7 @@ index 1444acd59d42f64831cead5f153419f7c12a88bf..23cb558a22b4462f5ce4f74833d25c7f #endif diff --git a/src/node_options.h b/src/node_options.h -index e68a41b60832c4b000e17dd15ce16c1bdaf4b54b..065457acfde6ba4d04ed570cc72005cfd2798fd5 100644 +index 7d14f06370d936a3866f0d988123de9fe614ce09..60068b008b2e2a034c3f0c58b947a8d04d55e3b2 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -11,7 +11,7 @@ diff --git a/patches/node/fix_remove_fastapitypedarray_usage.patch b/patches/node/fix_remove_fastapitypedarray_usage.patch index 7ce2dea2c4fe0..1d5b56cbe1fea 100644 --- a/patches/node/fix_remove_fastapitypedarray_usage.patch +++ b/patches/node/fix_remove_fastapitypedarray_usage.patch @@ -48,7 +48,7 @@ index 867a1c4aca54b9d41490d23a5eb55088b7e941cc..09f4c65a18efea262b1f854f993c6f18 static v8::CFunction fast_equal(v8::CFunction::Make(FastTimingSafeEqual)); diff --git a/src/node_buffer.cc b/src/node_buffer.cc -index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee4d8ef8f0 100644 +index 5bdffc0a4d7f8f643343593a543f2064b670c1b9..6931404b75dbe17bf3c7b561430b8d7c0921d085 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -44,6 +44,14 @@ @@ -74,7 +74,7 @@ index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee using v8::FunctionCallbackInfo; using v8::Global; using v8::HandleScope; -@@ -586,19 +593,24 @@ void SlowCopy(const FunctionCallbackInfo& args) { +@@ -584,19 +591,24 @@ void SlowCopy(const FunctionCallbackInfo& args) { // Assume caller has properly validated args. uint32_t FastCopy(Local receiver, @@ -107,7 +107,7 @@ index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee return to_copy; } -@@ -867,19 +879,17 @@ void Compare(const FunctionCallbackInfo &args) { +@@ -865,19 +877,17 @@ void Compare(const FunctionCallbackInfo &args) { } int32_t FastCompare(v8::Local, @@ -135,7 +135,7 @@ index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee } static v8::CFunction fast_compare(v8::CFunction::Make(FastCompare)); -@@ -1150,14 +1160,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo& args) { +@@ -1148,14 +1158,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo& args) { } int32_t FastIndexOfNumber(v8::Local, @@ -153,7 +153,7 @@ index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee } static v8::CFunction fast_index_of_number( -@@ -1511,21 +1520,31 @@ void SlowWriteString(const FunctionCallbackInfo& args) { +@@ -1495,21 +1504,31 @@ void SlowWriteString(const FunctionCallbackInfo& args) { template uint32_t FastWriteString(Local receiver, @@ -192,7 +192,7 @@ index 10659fbf57e4535736fc001c0dbdd9b93e8f60f1..c0bd975bae23d1c05ace42fd8c9846ee + std::min(dst_size - offset, max_length)); } - static v8::CFunction fast_write_string_ascii( + static const v8::CFunction fast_write_string_ascii( diff --git a/src/node_external_reference.h b/src/node_external_reference.h index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d46afdce75 100644 --- a/src/node_external_reference.h @@ -246,10 +246,10 @@ index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d4 // This class manages the external references from the V8 heap // to the C++ addresses in Node.js. diff --git a/src/util.h b/src/util.h -index 48d3c7b8a304049cdb4d4ab2c027b300dc533dc0..f9d5a5b36701b3c65fda65ed8920521ff68e32cd 100644 +index a77332f583402af956cc886fd5b9771390cc4827..f0d7571caa458a3f9a9c252a95f72eb5fbddd06d 100644 --- a/src/util.h +++ b/src/util.h -@@ -59,6 +59,7 @@ +@@ -60,6 +60,7 @@ namespace node { constexpr char kPathSeparator = std::filesystem::path::preferred_separator; @@ -257,7 +257,7 @@ index 48d3c7b8a304049cdb4d4ab2c027b300dc533dc0..f9d5a5b36701b3c65fda65ed8920521f #ifdef _WIN32 /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ -@@ -584,6 +585,16 @@ class BufferValue : public MaybeStackBuffer { +@@ -585,6 +586,16 @@ class BufferValue : public MaybeStackBuffer { static_cast(name->Buffer()->Data()) + name##_offset; \ if (name##_length > 0) CHECK_NE(name##_data, nullptr); diff --git a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch index adb0fcc8506a3..81346e984b9a8 100644 --- a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch +++ b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch @@ -11,10 +11,10 @@ This patch can be removed when we upgrade to a V8 version that contains the above CL. diff --git a/src/node.cc b/src/node.cc -index a0f1deadfc58f18f23467889680219360386f9dd..8da5f5344051663f92d72848fbac9d041ac4fac3 100644 +index 0fbcd55d674b1d0cae88f04fe337cfcca702255f..092b1c525c7d4d50a09f99dc088d0698afcaf8a6 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -808,7 +808,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, +@@ -814,7 +814,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, } // TODO(nicolo-ribaudo): remove this once V8 doesn't enable it by default // anymore. diff --git a/patches/node/pass_all_globals_through_require.patch b/patches/node/pass_all_globals_through_require.patch index 024cfdbfdddd5..918c6d2aac4f2 100644 --- a/patches/node/pass_all_globals_through_require.patch +++ b/patches/node/pass_all_globals_through_require.patch @@ -6,7 +6,7 @@ Subject: Pass all globals through "require" (cherry picked from commit 7d015419cb7a0ecfe6728431a4ed2056cd411d62) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 33385fa792b71ea3802904dd3c59ce845342c595..92b368394e17a9257578cd5b7422391689732d6d 100644 +index ccd2b4ced3134d81ddd37b8b5b90218457633421..a19fc5e52109bf2ad63fbe554c02a458c3096081 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -200,6 +200,13 @@ const { diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch index 7d775d9ef61ba..a526b752d46d3 100644 --- a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -18,10 +18,10 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs from the above issue. diff --git a/src/api/environment.cc b/src/api/environment.cc -index 7f4f233b26425493a58ce71dfc0c3a92b7c0bef8..c3f422c6b212bf737a906d2a89df85b63c218617 100644 +index 5b7f6e0609c8414c686d2d5ca603ea5c8bc484d0..6c9c81ff3c08fc28dc35578229a785521322b5dc 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc -@@ -312,6 +312,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, +@@ -313,6 +313,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params, MultiIsolatePlatform* platform, const SnapshotData* snapshot_data, const IsolateSettings& settings) { @@ -32,7 +32,7 @@ index 7f4f233b26425493a58ce71dfc0c3a92b7c0bef8..c3f422c6b212bf737a906d2a89df85b6 Isolate* isolate = Isolate::Allocate(); if (isolate == nullptr) return nullptr; -@@ -355,9 +359,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, +@@ -356,9 +360,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const EmbedderSnapshotData* snapshot_data, @@ -102,36 +102,44 @@ index 9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f..c429eecd937d1df32a2ff90bc0a22a2e worker::Worker* worker_context_ = nullptr; PerIsolateWrapperData* wrapper_data_; diff --git a/src/node.cc b/src/node.cc -index 8da5f5344051663f92d72848fbac9d041ac4fac3..2f58a6fa69069dabb99b5ddb8011991b07fa5f02 100644 +index 092b1c525c7d4d50a09f99dc088d0698afcaf8a6..6f8f6386d0db8aef1e2e0126cc9064101cbe6112 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -1222,10 +1222,6 @@ InitializeOncePerProcessInternal(const std::vector& args, +@@ -1258,6 +1258,14 @@ InitializeOncePerProcessInternal(const std::vector& args, result->platform_ = per_process::v8_platform.Platform(); } -- if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { -- V8::Initialize(); -- } -- - if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) { - v8::PageAllocator* allocator = nullptr; - if (result->platform_ != nullptr) { -@@ -1234,6 +1230,10 @@ InitializeOncePerProcessInternal(const std::vector& args, - cppgc::InitializeProcess(allocator); - } - -+ if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { -+ V8::Initialize(); ++ if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) { ++ v8::PageAllocator* allocator = nullptr; ++ if (result->platform_ != nullptr) { ++ allocator = result->platform_->GetPageAllocator(); ++ } ++ cppgc::InitializeProcess(allocator); + } + + if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { + V8::Initialize(); + +@@ -1267,14 +1275,6 @@ InitializeOncePerProcessInternal(const std::vector& args, + absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore); + } + +- if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) { +- v8::PageAllocator* allocator = nullptr; +- if (result->platform_ != nullptr) { +- allocator = result->platform_->GetPageAllocator(); +- } +- cppgc::InitializeProcess(allocator); +- } +- #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.h b/src/node.h -index 98ad0ea649eaef43d1f5231f7bc4044e100e08d7..c295cce8f5c7965cce4d2e4c0614dbe076986a4c 100644 +index 42d55d24bd0770795ae0c0e19241d25a6350ae08..4335c7cf53b7e08c95dcee3461384ac12c8ebe41 100644 --- a/src/node.h +++ b/src/node.h -@@ -589,7 +589,8 @@ NODE_EXTERN v8::Isolate* NewIsolate( +@@ -590,7 +590,8 @@ NODE_EXTERN v8::Isolate* NewIsolate( struct uv_loop_s* event_loop, MultiIsolatePlatform* platform, const EmbedderSnapshotData* snapshot_data = nullptr, @@ -166,7 +174,7 @@ index 4119ac1b002681d39711eac810ca2fcc2702ffc7..790347056cde949ffe6cf8498a7eca0c ExitCode NodeMainInstance::Run() { diff --git a/src/node_worker.cc b/src/node_worker.cc -index 1fc3774948dae3c0aae7d2aef563e18ecd4243a3..9d35cbf3dff538f38e8d5b8660d40c1fbaa56474 100644 +index 9d56d8f793ef48a79867f465530554ae0226f2cd..842eb999c6ef0cb877cc2ee4acf75bb597a117da 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -162,6 +162,9 @@ class WorkerThreadData { @@ -195,7 +203,7 @@ index 1fc3774948dae3c0aae7d2aef563e18ecd4243a3..9d35cbf3dff538f38e8d5b8660d40c1f // Wait until the platform has cleaned up all relevant resources. while (!platform_finished) { diff --git a/src/util.cc b/src/util.cc -index 3e9dfb4392fb3e3deaab5506771f01be65bc5dda..416e0479ddf740c6d3e2d4ea9466ac060b038294 100644 +index 0c01d338b9d1ced7f173ac862239315f91326791..5ca32f026f9f001ddadc14965705fe005600eddd 100644 --- a/src/util.cc +++ b/src/util.cc @@ -726,8 +726,8 @@ RAIIIsolateWithoutEntering::RAIIIsolateWithoutEntering(const SnapshotData* data) diff --git a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch index eb31ed5e0e4a6..3936c9bc7017a 100644 --- a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch +++ b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch @@ -161,10 +161,10 @@ index cfe917c797a6e4bb0f0284ec56be82637f840129..9f1c7ef45b6df10f811936a78ea6d9fc inline MultiIsolatePlatform* platform() const; inline const SnapshotData* snapshot_data() const; diff --git a/src/node.h b/src/node.h -index bdc77f8eb7abffa9e6c98cd254daedad3e44b981..98ad0ea649eaef43d1f5231f7bc4044e100e08d7 100644 +index 174fd4d1af4c8cd75aec09f4548a674fd5539fb2..42d55d24bd0770795ae0c0e19241d25a6350ae08 100644 --- a/src/node.h +++ b/src/node.h -@@ -1553,24 +1553,14 @@ void RegisterSignalHandler(int signal, +@@ -1560,24 +1560,14 @@ void RegisterSignalHandler(int signal, bool reset_handler = false); #endif // _WIN32 diff --git a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch index 250c9daa8f16a..4a6e3d5147779 100644 --- a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch +++ b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch @@ -26,7 +26,7 @@ index 3d8ccc77b5952a999c5fe48792259d32b402c460..867a1c4aca54b9d41490d23a5eb55088 } diff --git a/src/histogram.cc b/src/histogram.cc -index 0f0cde7be431dcb80c5314b1a9da49886c436d1c..f6d2bd439cad8b9f91c9d9a6cdb302e64130a5e2 100644 +index 5641990e0bac455c33ddf7b9a865deba871516e7..bd757f42e02391abbeec007d9c4cea60bcdfa6a4 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -195,7 +195,8 @@ void HistogramBase::FastRecord(Local unused, diff --git a/patches/node/support_v8_sandboxed_pointers.patch b/patches/node/support_v8_sandboxed_pointers.patch index 6a3edccfab578..2aedd3ceeae9a 100644 --- a/patches/node/support_v8_sandboxed_pointers.patch +++ b/patches/node/support_v8_sandboxed_pointers.patch @@ -7,10 +7,10 @@ This refactors several allocators to allocate within the V8 memory cage, allowing them to be compatible with the V8_SANDBOXED_POINTERS feature. diff --git a/src/api/environment.cc b/src/api/environment.cc -index 88c2c932a6b045317c83c911b1cd8267b60d9334..7f4f233b26425493a58ce71dfc0c3a92b7c0bef8 100644 +index df5fb942aa893c22d14d7eb21a5211a46a472a5f..5b7f6e0609c8414c686d2d5ca603ea5c8bc484d0 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc -@@ -102,6 +102,14 @@ MaybeLocal PrepareStackTraceCallback(Local context, +@@ -103,6 +103,14 @@ MaybeLocal PrepareStackTraceCallback(Local context, return result; } @@ -156,7 +156,7 @@ index 1592134716da2de40de4ba028ee937b765423e37..8f3ba65f1fef2c066d6df6087a08ba71 v8::Local ToArrayBuffer(Environment* env); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc -index 3465454e4de4a78912b81e7eca0de395fbe89911..c8ae863460107c69dd77d67c76c11843114e99c4 100644 +index f616223cfb0f6e10f7cf57ada9704316bde2797e..eb6dad44a49d997097c8fb5009eeb60a7305da27 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -167,6 +167,19 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { @@ -229,10 +229,10 @@ index ea7810e41e2667713a896250dc1b904b0a7cf198..865b3128c1edfe7074769f25a0b87878 constexpr const char* EncodingName(const enum encoding encoding) { diff --git a/src/node_internals.h b/src/node_internals.h -index 382df89a2312f76b5293412a8d51969ae5d9fa9c..1c90da9bbcb9547ab36de4d01088c03f3350b787 100644 +index 275534285ec28f02b46639142ab4195b24267476..5f9d123f9d4b9feb7bc0b627b1e6309fdbd6e30d 100644 --- a/src/node_internals.h +++ b/src/node_internals.h -@@ -117,7 +117,9 @@ v8::Maybe InitializePrimordials(v8::Local context); +@@ -120,7 +120,9 @@ v8::MaybeLocal InitializePrivateSymbols( class NodeArrayBufferAllocator : public ArrayBufferAllocator { public: @@ -243,7 +243,7 @@ index 382df89a2312f76b5293412a8d51969ae5d9fa9c..1c90da9bbcb9547ab36de4d01088c03f void* Allocate(size_t size) override; // Defined in src/node.cc void* AllocateUninitialized(size_t size) override; -@@ -135,7 +137,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator { +@@ -138,7 +140,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator { } private: diff --git a/patches/node/zlib_fix_pointer_alignment.patch b/patches/node/zlib_fix_pointer_alignment.patch deleted file mode 100644 index 418e74d53ccdd..0000000000000 --- a/patches/node/zlib_fix_pointer_alignment.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeroen Hofstee -Date: Tue, 1 Apr 2025 20:09:31 +0000 -Subject: zlib: fix pointer alignment - -The function AllocForBrotli prefixes the allocated memory with its -size, and returns a pointer to the region after it. This pointer can -however no longer be suitably aligned. Correct this by allocating -the maximum of the the size of the size_t and the max alignment. - -On Arm 32bits the size_t is 4 bytes long, but the alignment is 8 for -some NEON instructions. When Brotli is compiled with optimizations -enabled newer GCC versions will use the NEON instructions and trigger -a bus error killing node. - -see https://github.com/google/brotli/issues/1159 - -diff --git a/src/node_zlib.cc b/src/node_zlib.cc -index 0b7c47b326c7c5480086228b3d40d54c260ef16a..7e6b38ecd1aa360012c0d73e94412530a48cb8c3 100644 ---- a/src/node_zlib.cc -+++ b/src/node_zlib.cc -@@ -608,7 +608,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { - } - - static void* AllocForBrotli(void* data, size_t size) { -- size += sizeof(size_t); -+ constexpr size_t offset = std::max(sizeof(size_t), alignof(max_align_t)); -+ size += offset; - CompressionStream* ctx = static_cast(data); - char* memory = UncheckedMalloc(size); - if (memory == nullptr) [[unlikely]] { -@@ -617,7 +618,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { - *reinterpret_cast(memory) = size; - ctx->unreported_allocations_.fetch_add(size, - std::memory_order_relaxed); -- return memory + sizeof(size_t); -+ return memory + offset; - } - - static void FreeForZlib(void* data, void* pointer) { -@@ -625,7 +626,8 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork { - return; - } - CompressionStream* ctx = static_cast(data); -- char* real_pointer = static_cast(pointer) - sizeof(size_t); -+ constexpr size_t offset = std::max(sizeof(size_t), alignof(max_align_t)); -+ char* real_pointer = static_cast(pointer) - offset; - size_t real_size = *reinterpret_cast(real_pointer); - ctx->unreported_allocations_.fetch_sub(real_size, - std::memory_order_relaxed); diff --git a/script/node-disabled-tests.json b/script/node-disabled-tests.json index 5c23b69386d60..1d12a775ac119 100644 --- a/script/node-disabled-tests.json +++ b/script/node-disabled-tests.json @@ -7,6 +7,7 @@ "parallel/test-code-cache", "parallel/test-cluster-primary-error", "parallel/test-cluster-primary-kill", + "parallel/test-config-file", "parallel/test-crypto-aes-wrap", "parallel/test-crypto-authenticated", "parallel/test-crypto-authenticated-stream", @@ -73,6 +74,7 @@ "parallel/test-snapshot-weak-reference", "parallel/test-snapshot-worker", "parallel/test-strace-openat-openssl", + "parallel/test-sqlite-backup", "parallel/test-tls-alpn-server-client", "parallel/test-tls-cli-min-version-1.0", "parallel/test-tls-cli-max-version-1.2", diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 66d89f7b28e7c..10d8287fc1ea6 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -992,11 +992,10 @@ void OnNodePreload(node::Environment* env, } // Execute lib/node/init.ts. - std::vector> bundle_params = { - node::FIXED_ONE_BYTE_STRING(env->isolate(), "process"), - node::FIXED_ONE_BYTE_STRING(env->isolate(), "require"), - }; - std::vector> bundle_args = {process, require}; + v8::LocalVector bundle_params( + env->isolate(), {node::FIXED_ONE_BYTE_STRING(env->isolate(), "process"), + node::FIXED_ONE_BYTE_STRING(env->isolate(), "require")}); + v8::LocalVector bundle_args(env->isolate(), {process, require}); electron::util::CompileAndCall(env->context(), "electron/js2c/node_init", &bundle_params, &bundle_args); } diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index 89eaf88942194..7f6ffa93ea636 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -22,8 +22,8 @@ namespace electron::util { v8::MaybeLocal CompileAndCall( v8::Local context, const char* id, - std::vector>* parameters, - std::vector>* arguments) { + v8::LocalVector* parameters, + v8::LocalVector* arguments) { v8::Isolate* isolate = context->GetIsolate(); v8::TryCatch try_catch(isolate); diff --git a/shell/common/node_util.h b/shell/common/node_util.h index fdc92c0f6b9e5..7e7cef6705cd2 100644 --- a/shell/common/node_util.h +++ b/shell/common/node_util.h @@ -51,8 +51,8 @@ void EmitDeprecationWarning(std::string_view warning_msg, v8::MaybeLocal CompileAndCall( v8::Local context, const char* id, - std::vector>* parameters, - std::vector>* arguments); + v8::LocalVector* parameters, + v8::LocalVector* arguments); // Wrapper for node::CreateEnvironment that logs failure node::Environment* CreateEnvironment(v8::Isolate* isolate, diff --git a/shell/renderer/electron_sandboxed_renderer_client.cc b/shell/renderer/electron_sandboxed_renderer_client.cc index 2c985c9a59c03..4fb71d38da853 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.cc +++ b/shell/renderer/electron_sandboxed_renderer_client.cc @@ -125,10 +125,10 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext( auto binding = v8::Object::New(isolate); InitializeBindings(binding, context, render_frame); - std::vector> sandbox_preload_bundle_params = { - node::FIXED_ONE_BYTE_STRING(isolate, "binding")}; + v8::LocalVector sandbox_preload_bundle_params( + isolate, {node::FIXED_ONE_BYTE_STRING(isolate, "binding")}); - std::vector> sandbox_preload_bundle_args = {binding}; + v8::LocalVector sandbox_preload_bundle_args(isolate, {binding}); util::CompileAndCall( isolate->GetCurrentContext(), "electron/js2c/sandbox_bundle", diff --git a/shell/renderer/preload_realm_context.cc b/shell/renderer/preload_realm_context.cc index 5f104fe26bcf1..a8e47b2d0ab69 100644 --- a/shell/renderer/preload_realm_context.cc +++ b/shell/renderer/preload_realm_context.cc @@ -176,10 +176,10 @@ class PreloadRealmLifetimeController process.SetReadOnly("type", "service-worker"); process.SetReadOnly("contextIsolated", true); - std::vector> preload_realm_bundle_params = { - node::FIXED_ONE_BYTE_STRING(isolate, "binding")}; + v8::LocalVector preload_realm_bundle_params( + isolate, {node::FIXED_ONE_BYTE_STRING(isolate, "binding")}); - std::vector> preload_realm_bundle_args = {binding}; + v8::LocalVector preload_realm_bundle_args(isolate, {binding}); util::CompileAndCall(context, "electron/js2c/preload_realm_bundle", &preload_realm_bundle_params, diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index e1aeaae304a5c..5eb0fe04fc6e1 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -597,11 +597,11 @@ void RendererClientBase::SetupMainWorldOverrides( } } - std::vector> isolated_bundle_params = { - node::FIXED_ONE_BYTE_STRING(isolate, "isolatedApi")}; + v8::LocalVector isolated_bundle_params( + isolate, {node::FIXED_ONE_BYTE_STRING(isolate, "isolatedApi")}); - std::vector> isolated_bundle_args = { - isolated_api.GetHandle()}; + v8::LocalVector isolated_bundle_args(isolate, + {isolated_api.GetHandle()}); util::CompileAndCall(context, "electron/js2c/isolated_bundle", &isolated_bundle_params, &isolated_bundle_args); From 62fbb1d67a60048dc98e8e6cc05786ac663be54c Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Thu, 5 Jun 2025 10:00:41 +0200 Subject: [PATCH 259/339] chore: cherry-pick 2 changes from 1-M137 (#47368) * chore: cherry-pick 45eb42cd398e from v8 * chore: cherry-pick f1e6422a355c from chromium --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-f1e6422a355c.patch | 273 ++++++++++++++++++ patches/v8/.patches | 1 + patches/v8/cherry-pick-45eb42cd398e.patch | 32 ++ 4 files changed, 307 insertions(+) create mode 100644 patches/chromium/cherry-pick-f1e6422a355c.patch create mode 100644 patches/v8/cherry-pick-45eb42cd398e.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index afe3ceed903a3..e3ad536f9142f 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -149,3 +149,4 @@ mac_fix_check_on_ime_reconversion_due_to_invalid_replacement_range.patch fix_osr_stutter_fix_backport_for_electron.patch do_not_check_the_order_of_display_id_order_on_windows.patch make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch +cherry-pick-f1e6422a355c.patch diff --git a/patches/chromium/cherry-pick-f1e6422a355c.patch b/patches/chromium/cherry-pick-f1e6422a355c.patch new file mode 100644 index 0000000000000..82f87c24146fa --- /dev/null +++ b/patches/chromium/cherry-pick-f1e6422a355c.patch @@ -0,0 +1,273 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yoshisato Yanagisawa +Date: Wed, 21 May 2025 23:25:12 -0700 +Subject: Enforce SharedWorker::Terminate() procedure order + +During the investigation of crbug.com/409059706, we observed that +PerformShutdownOnWorkerThread() is called during the status is +running. + +I suppose the root cause is race condition between `Terminate()` +procedure and a child process termination procedure in different +thread. WorkerThread can be terminated if two conditions are met; +`Terminate()` is called and all child worker threads have been +terminated. Both `Terminate()` and the child process termination +procedure may call `PerformShutdownOnWorkerThread()`, and former +is executed regardless of two conditions are met. The latter +is called if `Terminate()` is called and no child processes. +To be clear, "`Terminate()` is called" does not mean +`PrepareForShutdownOnWorkerThread()` is executed. `Terminate()` +queues it after the flag to tell `Terminate()` call. And, when +the issue happen, I am quite sure the flag is set but, +`PrepareForShutdownOnWorkerThread()` won't be executed yet. + +The fix is that: +1. The "Terminate() is called" flag to be multi staged. + The flag is used for two purpose; a. avoid re-enter of + `Terminate()`, and b. `PrepareForShutdownOnWorkerThread()` is + in flight. The CL changed the flag to enum to represent + the stage properly. +2. `PerformShutdownOnWorkerThread()` is queued even if it is + called within the child process termination procedure. + It avoid the execution order flip between + `PrepareForShutdownOnWorkerThread()` and + `PerformShutdownOnWorkerThread()`. + +In addition, this change ensures `PerformShutdownOnWorkerThread()` +is called once. While `PerformShutdownOnWorkerThread()` touches +fields inside, the fields must not be touched at some point within +the function, the function is actually not re-entrant when it reaches +to the end. Upon mikt@ suggestion, I made +`PerformShutdownOnWorkerThread()` is called only when two conditions +are fulfilled. i.e. `Terminate()` is called and the number of child +threads is 0. Also, the CL uses the enum to show +`PerformShutdownOnWorkerThread()` is in-flight to avoid re-entrance +in this level. + +Bug: 409059706 +Change-Id: I81a1c3b1a34e827fa75ec2d1a9b37023965dbe27 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6543412 +Reviewed-by: Hiroki Nakagawa +Commit-Queue: Yoshisato Yanagisawa +Cr-Commit-Position: refs/heads/main@{#1463892} + +diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc +index 720c38313bd132a12b8465a4a2ca1bc32cf6f569..c2c8ee137a4b3a500954dcc9eaf3dea003613876 100644 +--- a/third_party/blink/common/features.cc ++++ b/third_party/blink/common/features.cc +@@ -2828,6 +2828,13 @@ BASE_FEATURE(kWebviewAccelerateSmallCanvases, + "WebviewAccelerateSmallCanvases", + base::FEATURE_DISABLED_BY_DEFAULT); + ++// WorkerThread termination procedure (prepare and shutdown) runs sequentially ++// in the same task without calling another cross thread post task. ++// Kill switch for crbug.com/409059706. ++BASE_FEATURE(kWorkerThreadSequentialShutdown, ++ "WorkerThreadSequentialShutdown", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + BASE_FEATURE(kNoReferrerForPreloadFromSubresource, + "NoReferrerForPreloadFromSubresource", + base::FEATURE_ENABLED_BY_DEFAULT); +diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h +index 9970347ef0ccb1801bbdd9e3ff904ae7591567c6..1cd31c80405bac33b11749587e5b05c0fd461856 100644 +--- a/third_party/blink/public/common/features.h ++++ b/third_party/blink/public/common/features.h +@@ -1844,6 +1844,8 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebUSBTransferSizeLimit); + + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebviewAccelerateSmallCanvases); + ++BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWorkerThreadSequentialShutdown); ++ + // Kill switch for https://crbug.com/415810136. + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kNoReferrerForPreloadFromSubresource); + +diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc +index 9b2878bc23c78f092816524608776dd32fbde5a1..6d1f954733fbc574bcb1fda229fd30e7303fb9aa 100644 +--- a/third_party/blink/renderer/core/workers/worker_thread.cc ++++ b/third_party/blink/renderer/core/workers/worker_thread.cc +@@ -264,9 +264,10 @@ void WorkerThread::Terminate() { + DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_); + { + base::AutoLock locker(lock_); +- if (requested_to_terminate_) ++ if (termination_progress_ != TerminationProgress::kNotRequested) { + return; +- requested_to_terminate_ = true; ++ } ++ termination_progress_ = TerminationProgress::kRequested; + } + + // Schedule a task to forcibly terminate the script execution in case that the +@@ -282,10 +283,33 @@ void WorkerThread::Terminate() { + *task_runner, FROM_HERE, + CrossThreadBindOnce(&WorkerThread::PrepareForShutdownOnWorkerThread, + CrossThreadUnretained(this))); +- PostCrossThreadTask( +- *task_runner, FROM_HERE, +- CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, +- CrossThreadUnretained(this))); ++ ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ PostCrossThreadTask( ++ *task_runner, FROM_HERE, ++ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ CrossThreadUnretained(this))); ++ return; ++ } ++ ++ bool perform_shutdown = false; ++ { ++ base::AutoLock locker(lock_); ++ CHECK_EQ(TerminationProgress::kRequested, termination_progress_); ++ termination_progress_ = TerminationProgress::kPrepared; ++ if (num_child_threads_ == 0) { ++ termination_progress_ = TerminationProgress::kPerforming; ++ perform_shutdown = true; ++ } ++ } ++ ++ if (perform_shutdown) { ++ PostCrossThreadTask( ++ *task_runner, FROM_HERE, ++ CrossThreadBindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ CrossThreadUnretained(this))); ++ } + } + + void WorkerThread::TerminateForTesting() { +@@ -422,20 +446,48 @@ scoped_refptr WorkerThread::GetTaskRunner( + + void WorkerThread::ChildThreadStartedOnWorkerThread(WorkerThread* child) { + DCHECK(IsCurrentThread()); +-#if DCHECK_IS_ON() ++ child_threads_.insert(child); + { + base::AutoLock locker(lock_); + DCHECK_EQ(ThreadState::kRunning, thread_state_); ++ CHECK_EQ(TerminationProgress::kNotRequested, termination_progress_); ++ if (base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ ++num_child_threads_; ++ CHECK_EQ(child_threads_.size(), num_child_threads_); ++ } + } +-#endif +- child_threads_.insert(child); + } + + void WorkerThread::ChildThreadTerminatedOnWorkerThread(WorkerThread* child) { + DCHECK(IsCurrentThread()); + child_threads_.erase(child); +- if (child_threads_.empty() && CheckRequestedToTerminate()) +- PerformShutdownOnWorkerThread(); ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ if (child_threads_.empty() && CheckRequestedToTerminate()) { ++ PerformShutdownOnWorkerThread(); ++ } ++ return; ++ } ++ ++ bool perform_shutdown = false; ++ { ++ base::AutoLock locker(lock_); ++ --num_child_threads_; ++ CHECK_EQ(child_threads_.size(), num_child_threads_); ++ if (num_child_threads_ == 0 && ++ termination_progress_ == TerminationProgress::kPrepared) { ++ termination_progress_ = TerminationProgress::kPerforming; ++ perform_shutdown = true; ++ } ++ } ++ if (perform_shutdown) { ++ scoped_refptr task_runner = ++ GetWorkerBackingThread().BackingThread().GetTaskRunner(); ++ GetWorkerBackingThread().BackingThread().GetTaskRunner()->PostTask( ++ FROM_HERE, WTF::BindOnce(&WorkerThread::PerformShutdownOnWorkerThread, ++ WTF::Unretained(this))); ++ } + } + + WorkerThread::WorkerThread(WorkerReportingProxy& worker_reporting_proxy) +@@ -781,18 +833,32 @@ void WorkerThread::PerformShutdownOnWorkerThread() { + DCHECK(IsCurrentThread()); + { + base::AutoLock locker(lock_); +- DCHECK(requested_to_terminate_); ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ DCHECK_NE(TerminationProgress::kNotRequested, termination_progress_); ++ } else { ++ DCHECK_EQ(TerminationProgress::kPerforming, termination_progress_); ++ } + DCHECK_EQ(ThreadState::kReadyToShutdown, thread_state_); + if (exit_code_ == ExitCode::kNotTerminated) + SetExitCode(ExitCode::kGracefullyTerminated); + } + +- // When child workers are present, wait for them to shutdown before shutting +- // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible +- // for completing shutdown on the worker thread after the last child shuts +- // down. +- if (!child_threads_.empty()) +- return; ++ if (!base::FeatureList::IsEnabled( ++ blink::features::kWorkerThreadSequentialShutdown)) { ++ // When child workers are present, wait for them to shutdown before shutting ++ // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible ++ // for completing shutdown on the worker thread after the last child shuts ++ // down. ++ if (!child_threads_.empty()) { ++ return; ++ } ++ } else { ++ // Child workers must not exist when `PerformShutdownOnWorkerThread()` ++ // is called because it has already been checked before calling the ++ // function. ++ CHECK(child_threads_.empty()); ++ } + + inspector_task_runner_->Dispose(); + if (worker_inspector_controller_) { +@@ -848,7 +914,7 @@ void WorkerThread::SetExitCode(ExitCode exit_code) { + + bool WorkerThread::CheckRequestedToTerminate() { + base::AutoLock locker(lock_); +- return requested_to_terminate_; ++ return termination_progress_ != TerminationProgress::kNotRequested; + } + + void WorkerThread::PauseOrFreeze(mojom::blink::FrameLifecycleState state, +diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h +index 48174fc364fd6d98f90b5a99a4ae403602691a52..e0423486b891d4d8638f455a01d07adc5b10b25b 100644 +--- a/third_party/blink/renderer/core/workers/worker_thread.h ++++ b/third_party/blink/renderer/core/workers/worker_thread.h +@@ -321,6 +321,13 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver { + kTerminationUnnecessary, + }; + ++ enum class TerminationProgress { ++ kNotRequested, ++ kRequested, ++ kPrepared, ++ kPerforming, ++ }; ++ + // Returns true if we should synchronously terminate the script execution so + // that a shutdown task can be handled by the thread event loop. + TerminationState ShouldTerminateScriptExecution() +@@ -422,8 +429,10 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver { + // A unique identifier among all WorkerThreads. + const int worker_thread_id_; + +- // Set on the parent thread. +- bool requested_to_terminate_ GUARDED_BY(lock_) = false; ++ // Represents progress after the Terminate() call. ++ TerminationProgress termination_progress_ GUARDED_BY(lock_) = ++ TerminationProgress::kNotRequested; ++ size_t num_child_threads_ GUARDED_BY(lock_) = 0; + + ThreadState thread_state_ GUARDED_BY(lock_) = ThreadState::kNotStarted; + ExitCode exit_code_ GUARDED_BY(lock_) = ExitCode::kNotTerminated; diff --git a/patches/v8/.patches b/patches/v8/.patches index 68cfde44f970f..eaac2faf9f98c 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,3 +1,4 @@ chore_allow_customizing_microtask_policy_per_context.patch enable_--perf-prof_flag_on_macos.patch cherry-pick-7bc0a67ebfbf.patch +cherry-pick-45eb42cd398e.patch diff --git a/patches/v8/cherry-pick-45eb42cd398e.patch b/patches/v8/cherry-pick-45eb42cd398e.patch new file mode 100644 index 0000000000000..ad85ca62646f0 --- /dev/null +++ b/patches/v8/cherry-pick-45eb42cd398e.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Igor Sheludko +Date: Tue, 27 May 2025 21:34:45 +0200 +Subject: Convert Smi to Word64 using zero extension + +... when a known type range contains only positive values. + +Bug: 420637585 +Change-Id: I8d9bb3f2fe2e5268e1659bb4ea7bbf97bfb52288 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594731 +Reviewed-by: Nico Hartmann +Commit-Queue: Igor Sheludko +Cr-Commit-Position: refs/heads/main@{#100538} + +diff --git a/src/compiler/representation-change.cc b/src/compiler/representation-change.cc +index f0d4f59c7625103a3fed789bfc1d08c407e00c08..8ce36e6a0f4a4a119a35c61dedcb22d2f55fa982 100644 +--- a/src/compiler/representation-change.cc ++++ b/src/compiler/representation-change.cc +@@ -1323,7 +1323,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor( + } + } else if (output_rep == MachineRepresentation::kTaggedSigned) { + if (output_type.Is(Type::SignedSmall())) { +- op = simplified()->ChangeTaggedSignedToInt64(); ++ if (output_type.IsRange() && output_type.AsRange()->Min() >= 0) { ++ node = InsertChangeTaggedSignedToInt32(node); ++ op = machine()->ChangeUint32ToUint64(); ++ } else { ++ op = simplified()->ChangeTaggedSignedToInt64(); ++ } + } else { + return TypeError(node, output_rep, output_type, + MachineRepresentation::kWord64); From 8e93599b53c05af0d53f7abe1dda15f41293d62c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:04:29 +0200 Subject: [PATCH 260/339] fix: do not load source for `electron` module in ESM loader synchronous flow (#47343) * fix: do not load electron from ESM loader's CJS compatibility Co-authored-by: clavin * chore: update patches --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: clavin --- ..._do_not_resolve_electron_entrypoints.patch | 13 -------- ...n_electron_module_via_the_esm_loader.patch | 33 ++++++++++++++++++- ...in_esm_loaders_to_apply_asar_patches.patch | 2 +- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/patches/node/fix_do_not_resolve_electron_entrypoints.patch b/patches/node/fix_do_not_resolve_electron_entrypoints.patch index 679f1bf31632d..a489cf55632f5 100644 --- a/patches/node/fix_do_not_resolve_electron_entrypoints.patch +++ b/patches/node/fix_do_not_resolve_electron_entrypoints.patch @@ -5,19 +5,6 @@ Subject: fix: do not resolve electron entrypoints This wastes fs cycles and can result in strange behavior if this path actually exists on disk -diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js -index c9d4a3536d0f60375ae623b48ca2fa7095c88d42..d818320fbbc430d06a0c2852e4723981d6e1a844 100644 ---- a/lib/internal/modules/esm/load.js -+++ b/lib/internal/modules/esm/load.js -@@ -109,7 +109,7 @@ async function defaultLoad(url, context = kEmptyObject) { - source = null; - format ??= 'builtin'; - } else if (format !== 'commonjs' || defaultType === 'module') { -- if (source == null) { -+ if (format !== 'electron' && source == null) { - ({ responseURL, source } = await getSource(urlInstance, context)); - context = { __proto__: context, source }; - } diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 49aacb6262502ced54e817f99dd596db85b9659c..f9f065bb743275e9b2ce71375e6a9f06e00c0f36 100644 --- a/lib/internal/modules/esm/translators.js diff --git a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch index baa89612d412d..3fbb7b9faf5c4 100644 --- a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch +++ b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch @@ -18,9 +18,18 @@ index 9519f947b8dfdc69808839948c9cb8434a0acf0e..23ce72d479f638c33edffcea7c35f5da /** diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js -index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2801c376e 100644 +index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..09a332c0999086b30fd952d9456f788925240e27 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js +@@ -106,7 +106,7 @@ async function defaultLoad(url, context = kEmptyObject) { + + throwIfUnsupportedURLScheme(urlInstance); + +- if (urlInstance.protocol === 'node:') { ++ if (urlInstance.protocol === 'node:' || format === 'electron') { + source = null; + format ??= 'builtin'; + } else if (format !== 'commonjs' || defaultType === 'module') { @@ -119,7 +119,7 @@ async function defaultLoad(url, context = kEmptyObject) { // Now that we have the source for the module, run `defaultGetFormat` to detect its format. format = await defaultGetFormat(urlInstance, context); @@ -30,6 +39,15 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2 // For backward compatibility reasons, we need to discard the source in // order for the CJS loader to re-fetch it. source = null; +@@ -167,7 +167,7 @@ function defaultLoadSync(url, context = kEmptyObject) { + + throwIfUnsupportedURLScheme(urlInstance, false); + +- if (urlInstance.protocol === 'node:') { ++ if (urlInstance.protocol === 'node:' || format === 'electron') { + source = null; + } else if (source == null) { + ({ responseURL, source } = getSourceSync(urlInstance, context)); @@ -200,12 +200,13 @@ function throwIfUnsupportedURLScheme(parsed) { protocol !== 'file:' && protocol !== 'data:' && @@ -45,6 +63,19 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..a00b5979e3b5deb4ba315b4635c7e5d2 throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes); } } +diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js +index ae03073aff8140b11c63b6c05d831ba573568dba..b70c7cfe40e2eaaeea7b5ad6fcf0aaee87276aa1 100644 +--- a/lib/internal/modules/esm/loader.js ++++ b/lib/internal/modules/esm/loader.js +@@ -492,7 +492,7 @@ class ModuleLoader { + } + + const cjsModule = wrap[imported_cjs_symbol]; +- if (cjsModule) { ++ if (cjsModule && finalFormat !== 'electron') { + assert(finalFormat === 'commonjs-sync'); + // Check if the ESM initiating import CJS is being required by the same CJS module. + if (cjsModule?.[kIsExecuting]) { diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index bfd9bd3d127404de1cbb6f30c43ab0342590759d..9e7d8ef0adef3b68a3ec186e4b218f591aa69266 100644 --- a/lib/internal/modules/esm/resolve.js diff --git a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch index 8de78da28f2fe..3eb241ca1eb19 100644 --- a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch +++ b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch @@ -6,7 +6,7 @@ Subject: fix: lazyload fs in esm loaders to apply asar patches Changes { foo } from fs to just "fs.foo" so that our patching of fs is applied to esm loaders diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js -index a00b5979e3b5deb4ba315b4635c7e5d2801c376e..c9d4a3536d0f60375ae623b48ca2fa7095c88d42 100644 +index 09a332c0999086b30fd952d9456f788925240e27..910ac85cdc86e7fb3f850f7edf0b95ea09d464dc 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js @@ -10,7 +10,7 @@ const { From 92dd9527571b1136192db9d6d6552e1f72fecd2c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:20:32 +0200 Subject: [PATCH 261/339] docs: update link to `runAsNode` fuse (#47375) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao --- docs/api/environment-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/environment-variables.md b/docs/api/environment-variables.md index 643b5fb7be419..f0efc56404ac6 100644 --- a/docs/api/environment-variables.md +++ b/docs/api/environment-variables.md @@ -104,7 +104,7 @@ you would when running the normal Node.js executable, with the exception of the These flags are disabled owing to the fact that Electron uses BoringSSL instead of OpenSSL when building Node.js' `crypto` module, and so will not work as designed. -If the [`runAsNode` fuse](../tutorial/fuses.md#L13) is disabled, `ELECTRON_RUN_AS_NODE` will be ignored. +If the [`runAsNode` fuse](../tutorial/fuses.md#runasnode) is disabled, `ELECTRON_RUN_AS_NODE` will be ignored. ### `ELECTRON_NO_ATTACH_CONSOLE` _Windows_ From a0633d9e2507d7dd263edd31d1b1968f211660ea Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:47:23 +0200 Subject: [PATCH 262/339] feat: expose `win.isContentProtected()` (#47311) * feat: expose win.isContentProtected() Co-authored-by: Shelley Vohr * chore: remove stray _isContentProtected Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/base-window.md | 4 ++++ docs/api/browser-window.md | 4 ++++ shell/browser/api/electron_api_base_window.cc | 2 +- spec/api-browser-window-spec.ts | 9 +++------ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 52e6deb288f8c..98909de846e22 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -1346,6 +1346,10 @@ On Windows it calls SetWindowDisplayAffinity with `WDA_EXCLUDEFROMCAPTURE`. For Windows 10 version 2004 and up the window will be removed from capture entirely, older Windows versions behave as if `WDA_MONITOR` is applied capturing a black window. +#### `win.isContentProtected()` _macOS_ _Windows_ + +Returns `boolean` - whether or not content protection is currently enabled. + #### `win.setFocusable(focusable)` _macOS_ _Windows_ * `focusable` boolean diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index e61faa458fe26..78997aabd44fe 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1528,6 +1528,10 @@ On Windows it calls SetWindowDisplayAffinity with `WDA_EXCLUDEFROMCAPTURE`. For Windows 10 version 2004 and up the window will be removed from capture entirely, older Windows versions behave as if `WDA_MONITOR` is applied capturing a black window. +#### `win.isContentProtected()` _macOS_ _Windows_ + +Returns `boolean` - whether or not content protection is currently enabled. + #### `win.setFocusable(focusable)` _macOS_ _Windows_ * `focusable` boolean diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 773d44b7f78c4..81ada7acf9294 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -1261,7 +1261,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("isDocumentEdited", &BaseWindow::IsDocumentEdited) .SetMethod("setIgnoreMouseEvents", &BaseWindow::SetIgnoreMouseEvents) .SetMethod("setContentProtection", &BaseWindow::SetContentProtection) - .SetMethod("_isContentProtected", &BaseWindow::IsContentProtected) + .SetMethod("isContentProtected", &BaseWindow::IsContentProtected) .SetMethod("setFocusable", &BaseWindow::SetFocusable) .SetMethod("isFocusable", &BaseWindow::IsFocusable) .SetMethod("setMenu", &BaseWindow::SetMenu) diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 36004ef59ca7d..5cd300f481c40 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -300,8 +300,7 @@ describe('BrowserWindow module', () => { afterEach(closeAllWindows); it('can set content protection', async () => { const w = new BrowserWindow({ show: false }); - // @ts-expect-error This is a private API - expect(w._isContentProtected()).to.equal(false); + expect(w.isContentProtected()).to.equal(false); const shown = once(w, 'show'); @@ -309,8 +308,7 @@ describe('BrowserWindow module', () => { await shown; w.setContentProtection(true); - // @ts-expect-error This is a private API - expect(w._isContentProtected()).to.equal(true); + expect(w.isContentProtected()).to.equal(true); }); it('does not remove content protection after the window is hidden and shown', async () => { @@ -329,8 +327,7 @@ describe('BrowserWindow module', () => { w.show(); await shown; - // @ts-expect-error This is a private API - expect(w._isContentProtected()).to.equal(true); + expect(w.isContentProtected()).to.equal(true); }); }); From b66cc92391c16f7913eb00702d98edcee808f6d7 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:10:18 -0400 Subject: [PATCH 263/339] chore: bump chromium to 136.0.7103.156 (36-x-y) (#47347) chore: bump chromium in DEPS to 136.0.7103.156 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 70868302514f5..afcb492647b79 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.149', + '136.0.7103.156', 'node_version': 'v22.16.0', 'nan_version': From bb09671746f6b2c86752eebb7d06c250e7b93ca4 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:35:33 +0200 Subject: [PATCH 264/339] feat: allow intercepting mouse events (#47365) * feat: allow intercepting mouse events Co-authored-by: Shelley Vohr * test: add specs Co-authored-by: Shelley Vohr * Update spec/api-web-contents-spec.ts Co-authored-by: David Sanders Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/web-contents.md | 53 ++++++++++++-- .../browser/api/electron_api_web_contents.cc | 6 ++ shell/browser/api/electron_api_web_contents.h | 2 + .../common/gin_converters/blink_converter.cc | 45 ++++++++++++ shell/common/gin_converters/blink_converter.h | 2 + spec/api-web-contents-spec.ts | 70 +++++++++++++++++++ spec/fixtures/pages/mouse-events.html | 20 ++++++ 7 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 spec/fixtures/pages/mouse-events.html diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 85d2ee83406eb..37876c608966e 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -534,14 +534,55 @@ To only prevent the menu shortcuts, use [`setIgnoreMenuShortcuts`](#contentssetignoremenushortcutsignore): ```js -const { BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron') -const win = new BrowserWindow({ width: 800, height: 600 }) +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 800, height: 600 }) + + win.webContents.on('before-input-event', (event, input) => { + // Enable application menu keyboard shortcuts when Ctrl/Cmd are down. + win.webContents.setIgnoreMenuShortcuts(!input.control && !input.meta) + }) +}) +``` + +#### Event: 'before-mouse-event' + +Returns: -win.webContents.on('before-input-event', (event, input) => { - // For example, only enable application menu keyboard shortcuts when - // Ctrl/Cmd are down. - win.webContents.setIgnoreMenuShortcuts(!input.control && !input.meta) +* `event` Event +* `mouse` [MouseInputEvent](structures/mouse-input-event.md) + +Emitted before dispatching mouse events in the page. + +Calling `event.preventDefault` will prevent the page mouse events. + +```js +const { app, BrowserWindow } = require('electron') + +app.whenReady().then(() => { + const win = new BrowserWindow({ width: 800, height: 600 }) + + win.webContents.on('before-mouse-event', (event, mouse) => { + // Prevent mouseDown events. + if (mouse.type === 'mouseDown') { + console.log(mouse) + /* + { + type: 'mouseDown', + clickCount: 1, + movementX: 0, + movementY: 0, + button: 'left', + x: 632.359375, + y: 480.6875, + globalX: 168.359375, + globalY: 193.6875 + } + */ + event.preventDefault() + } + }) }) ``` diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 2648b7187f240..b47e9c5766079 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -1355,6 +1355,12 @@ bool WebContents::PlatformHandleKeyboardEvent( } #endif +bool WebContents::PreHandleMouseEvent(content::WebContents* source, + const blink::WebMouseEvent& event) { + // |true| means that the event should be prevented. + return Emit("before-mouse-event", event); +} + content::KeyboardEventProcessingResult WebContents::PreHandleKeyboardEvent( content::WebContents* source, const input::NativeWebKeyboardEvent& event) { diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 6793733db9518..7fe49020a734d 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -533,6 +533,8 @@ class WebContents final : public ExclusiveAccessContext, const input::NativeWebKeyboardEvent& event) override; bool PlatformHandleKeyboardEvent(content::WebContents* source, const input::NativeWebKeyboardEvent& event); + bool PreHandleMouseEvent(content::WebContents* source, + const blink::WebMouseEvent& event) override; content::KeyboardEventProcessingResult PreHandleKeyboardEvent( content::WebContents* source, const input::NativeWebKeyboardEvent& event) override; diff --git a/shell/common/gin_converters/blink_converter.cc b/shell/common/gin_converters/blink_converter.cc index f172c74cfd2e1..2b79b6b2ac702 100644 --- a/shell/common/gin_converters/blink_converter.cc +++ b/shell/common/gin_converters/blink_converter.cc @@ -146,6 +146,26 @@ struct Converter { }); return FromV8WithLowerLookup(isolate, val, Lookup, out); } + static v8::Local ToV8(v8::Isolate* isolate, + const blink::WebMouseEvent::Button& in) { + switch (in) { + case blink::WebMouseEvent::Button::kLeft: + return StringToV8(isolate, "left"); + case blink::WebMouseEvent::Button::kMiddle: + return StringToV8(isolate, "middle"); + case blink::WebMouseEvent::Button::kRight: + return StringToV8(isolate, "right"); + case blink::WebMouseEvent::Button::kNoButton: + return StringToV8(isolate, "none"); + case blink::WebMouseEvent::Button::kBack: + return StringToV8(isolate, "back"); + case blink::WebMouseEvent::Button::kForward: + return StringToV8(isolate, "forward"); + case blink::WebMouseEvent::Button::kEraser: + return StringToV8(isolate, "eraser"); + } + return v8::Null(isolate); + } }; // clang-format off @@ -246,6 +266,9 @@ v8::Local Converter::ToV8( if (blink::WebInputEvent::IsKeyboardEventType(in.GetType())) return gin::ConvertToV8(isolate, *static_cast(&in)); + else if (blink::WebInputEvent::IsMouseEventType(in.GetType())) + return gin::ConvertToV8(isolate, + *static_cast(&in)); return gin::DataObjectBuilder(isolate) .Set("type", in.GetType()) .Set("modifiers", ModifiersToArray(in.GetModifiers())) @@ -379,6 +402,28 @@ bool Converter::FromV8(v8::Isolate* isolate, return true; } +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const blink::WebMouseEvent& in) { + auto dict = gin_helper::Dictionary::CreateEmpty(isolate); + + dict.Set("type", in.GetType()); + dict.Set("clickCount", in.ClickCount()); + dict.Set("movementX", in.movement_x); + dict.Set("movementY", in.movement_y); + dict.Set("button", in.button); + + gfx::PointF position_in_screen = in.PositionInScreen(); + dict.Set("globalX", position_in_screen.x()); + dict.Set("globalY", position_in_screen.y()); + + gfx::PointF position_in_widget = in.PositionInWidget(); + dict.Set("x", position_in_widget.x()); + dict.Set("y", position_in_widget.y()); + + return dict.GetHandle(); +} + bool Converter::FromV8( v8::Isolate* isolate, v8::Local val, diff --git a/shell/common/gin_converters/blink_converter.h b/shell/common/gin_converters/blink_converter.h index f293181c4a6be..6197a09de2cec 100644 --- a/shell/common/gin_converters/blink_converter.h +++ b/shell/common/gin_converters/blink_converter.h @@ -57,6 +57,8 @@ struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, blink::WebMouseEvent* out); + static v8::Local ToV8(v8::Isolate* isolate, + const blink::WebMouseEvent& in); }; template <> diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index 69e462c1df2c4..4d0a3eebda205 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -1071,6 +1071,76 @@ describe('webContents module', () => { }); }); + describe('before-mouse-event event', () => { + afterEach(closeAllWindows); + it('can prevent document mouse events', async () => { + const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); + await w.loadFile(path.join(fixturesPath, 'pages', 'mouse-events.html')); + const mouseDown = new Promise(resolve => { + ipcMain.once('mousedown', (event, button) => resolve(button)); + }); + w.webContents.once('before-mouse-event', (event, input) => { + if (input.button === 'left') event.preventDefault(); + }); + w.webContents.sendInputEvent({ type: 'mouseDown', button: 'left', x: 100, y: 100 }); + w.webContents.sendInputEvent({ type: 'mouseDown', button: 'right', x: 100, y: 100 }); + expect(await mouseDown).to.equal(2); // Right button is 2 + }); + + it('has the correct properties', async () => { + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); + const testBeforeMouse = async (opts: Electron.MouseInputEvent) => { + const p = once(w.webContents, 'before-mouse-event'); + w.webContents.sendInputEvent({ + type: opts.type, + button: opts.button, + x: opts.x, + y: opts.y, + globalX: opts.globalX, + globalY: opts.globalY, + clickCount: opts.clickCount + }); + const [, input] = await p; + + expect(input.type).to.equal(opts.type); + expect(input.button).to.equal(opts.button); + expect(input.x).to.equal(opts.x); + expect(input.y).to.equal(opts.y); + expect(input.globalX).to.equal(opts.globalX); + expect(input.globalY).to.equal(opts.globalY); + expect(input.clickCount).to.equal(opts.clickCount); + }; + await testBeforeMouse({ + type: 'mouseDown', + button: 'left', + x: 100, + y: 100, + globalX: 200, + globalY: 200, + clickCount: 1 + }); + await testBeforeMouse({ + type: 'mouseUp', + button: 'right', + x: 150, + y: 150, + globalX: 250, + globalY: 250, + clickCount: 2 + }); + await testBeforeMouse({ + type: 'mouseMove', + button: 'middle', + x: 200, + y: 200, + globalX: 300, + globalY: 300, + clickCount: 0 + }); + }); + }); + describe('before-input-event event', () => { afterEach(closeAllWindows); it('can prevent document keyboard events', async () => { diff --git a/spec/fixtures/pages/mouse-events.html b/spec/fixtures/pages/mouse-events.html new file mode 100644 index 0000000000000..51610f4a1e86e --- /dev/null +++ b/spec/fixtures/pages/mouse-events.html @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file From a997e6e53af2ceae824f7874fd9b8b294ae749b0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:12:57 +0200 Subject: [PATCH 265/339] docs: Add Swift/macOS tutorial (#47380) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Felix Rieseberg --- .../native-code-and-electron-swift-macos.md | 1175 +++++++++++++++++ 1 file changed, 1175 insertions(+) create mode 100644 docs/tutorial/native-code-and-electron-swift-macos.md diff --git a/docs/tutorial/native-code-and-electron-swift-macos.md b/docs/tutorial/native-code-and-electron-swift-macos.md new file mode 100644 index 0000000000000..1e11b6ac3d3b1 --- /dev/null +++ b/docs/tutorial/native-code-and-electron-swift-macos.md @@ -0,0 +1,1175 @@ +# Native Code and Electron: Swift (macOS) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for macOS using Swift. + +Swift is a modern, powerful language designed for safety and performance. While you can't use Swift directly with the Node.js N-API as used by Electron, you can create a bridge using Objective-C++ to connect Swift with JavaScript in your Electron application. + +To illustrate how you can embed native macOS code in your Electron app, we'll be building a basic native macOS GUI (using SwiftUI) that communicates with Electron's JavaScript. + +This tutorial will be most useful to those who already have some familiarity with Objective-C, Swift, and SwiftUI development. You should understand basic concepts like Swift syntax, optionals, closures, SwiftUI views, property wrappers, and the Objective-C/Swift interoperability mechanisms such as the `@objc` attribute and bridging headers. + +> [!NOTE] +> If you're not already familiar with these concepts, Apple's [Swift Programming Language Guide](https://docs.swift.org/swift-book/), [SwiftUI Documentation](https://developer.apple.com/documentation/swiftui/), and [Swift and Objective-C Interoperability Guide](https://developer.apple.com/documentation/swift/importing-swift-into-objective-c) are excellent starting points. + +## Requirements + +Just like our [general introduction to Native Code and Electron](./native-code-and-electron.md), this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code on macOS. You'll need: + +* Xcode installed (available from the Mac App Store) +* Xcode Command Line Tools (can be installed by running `xcode-select --install` in Terminal) + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +swift-native-addon/ +├── binding.gyp # Build configuration +├── include/ +│ └── SwiftBridge.h # Objective-C header for the bridge +├── js/ +│ └── index.js # JavaScript interface +├── package.json # Package configuration +└── src/ + ├── SwiftCode.swift # Swift implementation + ├── SwiftBridge.m # Objective-C bridge implementation + └── swift_addon.mm # Node.js addon implementation +``` + +Our `package.json` should look like this: + +```json title='package.json' +{ + "name": "swift-macos", + "version": "1.0.0", + "description": "A demo module that exposes Swift code to Electron", + "main": "js/index.js", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.3.0" + }, + "devDependencies": { + "node-gyp": "^11.1.0" + } +} +``` + +## 2) Setting Up the Build Configuration + +In our other tutorials focusing on other native languages, we could use `node-gyp` to build the entirety of our code. With Swift, things are a bit more tricky: We need to first build and then link our Swift code. This is because Swift has its own compilation model and runtime requirements that don't directly integrate with node-gyp's C/C++ focused build system. + +The process involves: + +1. Compiling Swift code separately into a static library (.a file) +2. Creating an Objective-C bridge that exposes Swift functionality +3. Linking the compiled Swift library with our Node.js addon +4. Managing Swift runtime dependencies + +This two-step compilation process ensures that Swift's advanced language features and runtime are properly handled while still allowing us to expose the functionality to JavaScript through Node.js's native addon system. + +Let's start by adding a basic structure: + +```json title='binding.gyp' +{ + "targets": [{ + "target_name": "swift_addon", + "conditions": [ + ['OS=="mac"', { + "sources": [ + "src/swift_addon.mm", + "src/SwiftBridge.m", + "src/SwiftCode.swift" + ], + "include_dirs": [ + " + +@interface SwiftBridge : NSObject ++ (NSString*)helloWorld:(NSString*)input; ++ (void)helloGui; + ++ (void)setTodoAddedCallback:(void(^)(NSString* todoJson))callback; ++ (void)setTodoUpdatedCallback:(void(^)(NSString* todoJson))callback; ++ (void)setTodoDeletedCallback:(void(^)(NSString* todoId))callback; +@end + +#endif +``` + +This header defines the Objective-C interface that we'll use to bridge between our Swift code and the Node.js addon. It includes: + +* A simple `helloWorld` method that takes a string input and returns a string +* A `helloGui` method that will display a native SwiftUI interface +* Methods to set callbacks for todo operations (add, update, delete) + +## 4) Implementing the Objective-C Bridge + +Now, let's create the Objective-C bridge itself in `src/SwiftBridge.m`: + +```objc title='src/SwiftBridge.m' +#import "SwiftBridge.h" +#import "swift_addon-Swift.h" +#import + +@implementation SwiftBridge + +static void (^todoAddedCallback)(NSString*); +static void (^todoUpdatedCallback)(NSString*); +static void (^todoDeletedCallback)(NSString*); + ++ (NSString*)helloWorld:(NSString*)input { + return [SwiftCode helloWorld:input]; +} + ++ (void)helloGui { + [SwiftCode helloGui]; +} + ++ (void)setTodoAddedCallback:(void(^)(NSString*))callback { + todoAddedCallback = callback; + [SwiftCode setTodoAddedCallback:callback]; +} + ++ (void)setTodoUpdatedCallback:(void(^)(NSString*))callback { + todoUpdatedCallback = callback; + [SwiftCode setTodoUpdatedCallback:callback]; +} + ++ (void)setTodoDeletedCallback:(void(^)(NSString*))callback { + todoDeletedCallback = callback; + [SwiftCode setTodoDeletedCallback:callback]; +} + +@end +``` + +This bridge: + +* Imports the Swift-generated header (`swift_addon-Swift.h`) +* Implements the methods defined in our header +* Simply forwards calls to the Swift code +* Stores the callbacks for later use in static variables, allowing them to persist throughout the application's lifecycle. This ensures that the JavaScript callbacks can be invoked at any time when todo items are added, updated, or deleted. + +## 5) Implementing the Swift Code + +Now, let's implement our Objective-C code in `src/SwiftCode.swift`. This is where we'll create our native macOS GUI using SwiftUI. + +To make this tutorial easier to follow, we'll start with the basic structure and add features incrementally - step by step. + +### Setting Up the Basic Structure + +Let's start with the basic structure. Here, we're just setting up variables, some basic callback methods, and a simple helper method we'll use later to convert data into formats ready for the JavaScript world. + +```swift title='src/SwiftCode.swift' +import Foundation +import SwiftUI + +@objc +public class SwiftCode: NSObject { + private static var windowController: NSWindowController? + private static var todoAddedCallback: ((String) -> Void)? + private static var todoUpdatedCallback: ((String) -> Void)? + private static var todoDeletedCallback: ((String) -> Void)? + + @objc + public static func helloWorld(_ input: String) -> String { + return "Hello from Swift! You said: \(input)" + } + + @objc + public static func setTodoAddedCallback(_ callback: @escaping (String) -> Void) { + todoAddedCallback = callback + } + + @objc + public static func setTodoUpdatedCallback(_ callback: @escaping (String) -> Void) { + todoUpdatedCallback = callback + } + + @objc + public static func setTodoDeletedCallback(_ callback: @escaping (String) -> Void) { + todoDeletedCallback = callback + } + + private static func encodeToJson(_ item: T) -> String? { + let encoder = JSONEncoder() + + // Encode date as milliseconds since 1970, which is what the JS side expects + encoder.dateEncodingStrategy = .custom { date, encoder in + let milliseconds = Int64(date.timeIntervalSince1970 * 1000) + var container = encoder.singleValueContainer() + try container.encode(milliseconds) + } + + guard let jsonData = try? encoder.encode(item), + let jsonString = String(data: jsonData, encoding: .utf8) else { + return nil + } + return jsonString + } + + // More code to follow... +} +``` + +This first part of our Swift code: + +1. Declares a class with the `@objc` attribute, making it accessible from Objective-C +2. Implements the `helloWorld` method +3. Adds callback setters for todo operations +4. Includes a helper method to encode Swift objects to JSON strings + +### Implementing `helloGui()` + +Let's continue with the `helloGui` method and the SwiftUI implementation. This is where we start adding user interface elements to the screen. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + @objc + public static func helloGui() -> Void { + let contentView = NSHostingView(rootView: ContentView( + onTodoAdded: { todo in + if let jsonString = encodeToJson(todo) { + todoAddedCallback?(jsonString) + } + }, + onTodoUpdated: { todo in + if let jsonString = encodeToJson(todo) { + todoUpdatedCallback?(jsonString) + } + }, + onTodoDeleted: { todoId in + todoDeletedCallback?(todoId.uuidString) + } + )) + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), + styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, + defer: false + ) + + window.title = "Todo List" + window.contentView = contentView + window.center() + + windowController = NSWindowController(window: window) + windowController?.showWindow(nil) + + NSApp.activate(ignoringOtherApps: true) + } +} +``` + +This helloGui method: + +1. Creates a SwiftUI view hosted in an `NSHostingView`. This is a crucial bridging component that allows SwiftUI views to be used in AppKit applications. The `NSHostingView` acts as a container that wraps our SwiftUI `ContentView` and handles the translation between SwiftUI's declarative UI system and AppKit's imperative UI system. This enables us to leverage SwiftUI's modern UI framework while still integrating with the traditional macOS window management system. +2. Sets up callbacks to notify JavaScript when todo items change. We'll setup the actual callbacks later, for now we'll just call them if one is available. +3. Creates and displays a native macOS window. +4. Activates the app to bring the window to the front. + +### Implementing the Todo Item + +Next, we'll define a `TodoItem` model with an ID, text, and date. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + private struct TodoItem: Identifiable, Codable { + let id: UUID + var text: String + var date: Date + + init(id: UUID = UUID(), text: String, date: Date) { + self.id = id + self.text = text + self.date = date + } + } +} +``` + +### Implementing the View + +Next, we can implement the actual view. Swift is fairly verbose here, so the code below might look scary if you're new to Swift. The many lines of code obfuscate the simplicity in it - we're just setting up some UI elements. Nothing here is specific to Electron. + +```swift title='src/SwiftCode.swift' +// Other code... + +@objc +public class SwiftCode: NSObject { + // Other code... + + private struct ContentView: View { + @State private var todos: [TodoItem] = [] + @State private var newTodo: String = "" + @State private var newTodoDate: Date = Date() + @State private var editingTodo: UUID? + @State private var editedText: String = "" + @State private var editedDate: Date = Date() + + let onTodoAdded: (TodoItem) -> Void + let onTodoUpdated: (TodoItem) -> Void + let onTodoDeleted: (UUID) -> Void + + private func todoTextField(_ text: Binding, placeholder: String, maxWidth: CGFloat? = nil) -> some View { + TextField(placeholder, text: text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(maxWidth: maxWidth ?? .infinity) + } + + private func todoDatePicker(_ date: Binding) -> some View { + DatePicker("Due date", selection: date, displayedComponents: [.date]) + .datePickerStyle(CompactDatePickerStyle()) + .labelsHidden() + .frame(width: 100) + .textFieldStyle(RoundedBorderTextFieldStyle()) + } + + var body: some View { + VStack(spacing: 16) { + HStack(spacing: 12) { + todoTextField($newTodo, placeholder: "New todo") + todoDatePicker($newTodoDate) + Button(action: { + if !newTodo.isEmpty { + let todo = TodoItem(text: newTodo, date: newTodoDate) + todos.append(todo) + onTodoAdded(todo) + newTodo = "" + newTodoDate = Date() + } + }) { + Text("Add") + .frame(width: 50) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + List { + ForEach(todos) { todo in + if editingTodo == todo.id { + HStack(spacing: 12) { + todoTextField($editedText, placeholder: "Edit todo", maxWidth: 250) + todoDatePicker($editedDate) + Button(action: { + if let index = todos.firstIndex(where: { $0.id == todo.id }) { + let updatedTodo = TodoItem(id: todo.id, text: editedText, date: editedDate) + todos[index] = updatedTodo + onTodoUpdated(updatedTodo) + editingTodo = nil + } + }) { + Text("Save") + .frame(width: 60) + } + } + .padding(.vertical, 4) + } else { + HStack(spacing: 12) { + Text(todo.text) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + Text(todo.date.formatted(date: .abbreviated, time: .shortened)) + .foregroundColor(.gray) + Button(action: { + editingTodo = todo.id + editedText = todo.text + editedDate = todo.date + }) { + Image(systemName: "pencil") + } + .buttonStyle(BorderlessButtonStyle()) + Button(action: { + todos.removeAll(where: { $0.id == todo.id }) + onTodoDeleted(todo.id) + }) { + Image(systemName: "trash") + .foregroundColor(.red) + } + .buttonStyle(BorderlessButtonStyle()) + } + .padding(.vertical, 4) + } + } + } + } + } + } +} +``` + +This part of the code: + +* Creates a SwiftUI view with a form to add new todos, featuring a text field for the todo description, a date picker for setting due dates, and an Add button that validates input, creates a new TodoItem, adds it to the local array, triggers the `onTodoAdded` callback to notify JavaScript, and then resets the input fields for the next entry. +* Implements a list to display todos with edit and delete capabilities +* Calls the appropriate callbacks when todos are added, updated, or deleted + +The final file should look as follows: + +```swift title='src/SwiftCode.swift' +import Foundation +import SwiftUI + +@objc +public class SwiftCode: NSObject { + private static var windowController: NSWindowController? + private static var todoAddedCallback: ((String) -> Void)? + private static var todoUpdatedCallback: ((String) -> Void)? + private static var todoDeletedCallback: ((String) -> Void)? + + @objc + public static func helloWorld(_ input: String) -> String { + return "Hello from Swift! You said: \(input)" + } + + @objc + public static func setTodoAddedCallback(_ callback: @escaping (String) -> Void) { + todoAddedCallback = callback + } + + @objc + public static func setTodoUpdatedCallback(_ callback: @escaping (String) -> Void) { + todoUpdatedCallback = callback + } + + @objc + public static func setTodoDeletedCallback(_ callback: @escaping (String) -> Void) { + todoDeletedCallback = callback + } + + private static func encodeToJson(_ item: T) -> String? { + let encoder = JSONEncoder() + + // Encode date as milliseconds since 1970, which is what the JS side expects + encoder.dateEncodingStrategy = .custom { date, encoder in + let milliseconds = Int64(date.timeIntervalSince1970 * 1000) + var container = encoder.singleValueContainer() + try container.encode(milliseconds) + } + + guard let jsonData = try? encoder.encode(item), + let jsonString = String(data: jsonData, encoding: .utf8) else { + return nil + } + return jsonString + } + + @objc + public static func helloGui() -> Void { + let contentView = NSHostingView(rootView: ContentView( + onTodoAdded: { todo in + if let jsonString = encodeToJson(todo) { + todoAddedCallback?(jsonString) + } + }, + onTodoUpdated: { todo in + if let jsonString = encodeToJson(todo) { + todoUpdatedCallback?(jsonString) + } + }, + onTodoDeleted: { todoId in + todoDeletedCallback?(todoId.uuidString) + } + )) + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), + styleMask: [.titled, .closable, .miniaturizable, .resizable], + backing: .buffered, + defer: false + ) + + window.title = "Todo List" + window.contentView = contentView + window.center() + + windowController = NSWindowController(window: window) + windowController?.showWindow(nil) + + NSApp.activate(ignoringOtherApps: true) + } + + private struct TodoItem: Identifiable, Codable { + let id: UUID + var text: String + var date: Date + + init(id: UUID = UUID(), text: String, date: Date) { + self.id = id + self.text = text + self.date = date + } + } + + private struct ContentView: View { + @State private var todos: [TodoItem] = [] + @State private var newTodo: String = "" + @State private var newTodoDate: Date = Date() + @State private var editingTodo: UUID? + @State private var editedText: String = "" + @State private var editedDate: Date = Date() + + let onTodoAdded: (TodoItem) -> Void + let onTodoUpdated: (TodoItem) -> Void + let onTodoDeleted: (UUID) -> Void + + private func todoTextField(_ text: Binding, placeholder: String, maxWidth: CGFloat? = nil) -> some View { + TextField(placeholder, text: text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(maxWidth: maxWidth ?? .infinity) + } + + private func todoDatePicker(_ date: Binding) -> some View { + DatePicker("Due date", selection: date, displayedComponents: [.date]) + .datePickerStyle(CompactDatePickerStyle()) + .labelsHidden() + .frame(width: 100) + .textFieldStyle(RoundedBorderTextFieldStyle()) + } + + var body: some View { + VStack(spacing: 16) { + HStack(spacing: 12) { + todoTextField($newTodo, placeholder: "New todo") + todoDatePicker($newTodoDate) + Button(action: { + if !newTodo.isEmpty { + let todo = TodoItem(text: newTodo, date: newTodoDate) + todos.append(todo) + onTodoAdded(todo) + newTodo = "" + newTodoDate = Date() + } + }) { + Text("Add") + .frame(width: 50) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + + List { + ForEach(todos) { todo in + if editingTodo == todo.id { + HStack(spacing: 12) { + todoTextField($editedText, placeholder: "Edit todo", maxWidth: 250) + todoDatePicker($editedDate) + Button(action: { + if let index = todos.firstIndex(where: { $0.id == todo.id }) { + let updatedTodo = TodoItem(id: todo.id, text: editedText, date: editedDate) + todos[index] = updatedTodo + onTodoUpdated(updatedTodo) + editingTodo = nil + } + }) { + Text("Save") + .frame(width: 60) + } + } + .padding(.vertical, 4) + } else { + HStack(spacing: 12) { + Text(todo.text) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + Text(todo.date.formatted(date: .abbreviated, time: .shortened)) + .foregroundColor(.gray) + Button(action: { + editingTodo = todo.id + editedText = todo.text + editedDate = todo.date + }) { + Image(systemName: "pencil") + } + .buttonStyle(BorderlessButtonStyle()) + Button(action: { + todos.removeAll(where: { $0.id == todo.id }) + onTodoDeleted(todo.id) + }) { + Image(systemName: "trash") + .foregroundColor(.red) + } + .buttonStyle(BorderlessButtonStyle()) + } + .padding(.vertical, 4) + } + } + } + } + } + } +} +``` + +## 6) Creating the Node.js Addon Bridge + +We now have working Objective-C code, which in turn is able to call working Swift code. To make sure it can be safely and properly called from the JavaScript world, we need to build a bridge between Objective-C and C++, which we can do with Objective-C++. We'll do that in `src/swift_addon.mm`. + +```objc title='src/swift_addon.mm' +#import +#import "SwiftBridge.h" +#include + +class SwiftAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "SwiftAddon", { + InstanceMethod("helloWorld", &SwiftAddon::HelloWorld), + InstanceMethod("helloGui", &SwiftAddon::HelloGui), + InstanceMethod("on", &SwiftAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("SwiftAddon", func); + return exports; + } + + // More code to follow... +``` + +This first part: + +1. Defines a C++ class that inherits from `Napi::ObjectWrap` +2. Creates a static `Init` method to register our class with Node.js +3. Defines three methods: `helloWorld`, `helloGui`, and `on` + +### Callback Mechanism + +Next, let's implement the callback mechanism: + +```objc title='src/swift_addon.mm' +// Previous code... + + struct CallbackData { + std::string eventType; + std::string payload; + SwiftAddon* addon; + }; + + SwiftAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "SwiftCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } +``` + +This part: + +1. Defines a struct to pass data between threads +2. Sets up a constructor for our addon +3. Creates a threadsafe function to handle callbacks from Swift + +Let's continue with setting up the Swift callbacks: + +```objc title='src/swift_addon.mm' +// Previous code... + + auto makeCallback = [this](const char* eventType) { + return ^(NSString* payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + std::string([payload UTF8String]), + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + [SwiftBridge setTodoAddedCallback:makeCallback("todoAdded")]; + [SwiftBridge setTodoUpdatedCallback:makeCallback("todoUpdated")]; + [SwiftBridge setTodoDeletedCallback:makeCallback("todoDeleted")]; + } + + ~SwiftAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } +``` + +This part: + +1. Creates a helper function to generate Objective-C blocks that will be used as callbacks for Swift events. This lambda function `makeCallback` takes an event type string and returns an Objective-C block that captures the event type and payload. When Swift calls this block, it creates a CallbackData structure with the event information and passes it to the threadsafe function, which safely bridges between Swift's thread and Node.js's event loop. +2. Sets up the carefully constructed callbacks for todo operations +3. Implements a destructor to clean up resources + +### Instance Methods + +Finally, let's implement the instance methods: + +```objc title='src/swift_addon.mm' +// Previous code... + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + NSString* nsInput = [NSString stringWithUTF8String:input.c_str()]; + NSString* result = [SwiftBridge helloWorld:nsInput]; + + return Napi::String::New(env, [result UTF8String]); + } + + void HelloGui(const Napi::CallbackInfo& info) { + [SwiftBridge helloGui]; + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return SwiftAddon::Init(env, exports); +} + +NODE_API_MODULE(swift_addon, Init) +``` + +This final part does multiple things: + +1. The code defines private member variables for the environment, event emitter, callback storage, and thread-safe function that are essential for the addon's operation. +2. The HelloWorld method implementation takes a string input from JavaScript, passes it to the Swift code, and returns the processed result back to the JavaScript environment. +3. The `HelloGui` method implementation provides a simple wrapper that calls the Swift UI creation function to display the native macOS window. +4. The `On` method implementation allows JavaScript code to register callback functions that will be invoked when specific events occur in the native Swift code. +5. The code sets up the module initialization process that registers the addon with Node.js and makes its functionality available to JavaScript. + +The final and full `src/swift_addon.mm` should look like: + +```objc title='src/swift_addon.mm' +#import +#import "SwiftBridge.h" +#include + +class SwiftAddon : public Napi::ObjectWrap { +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "SwiftAddon", { + InstanceMethod("helloWorld", &SwiftAddon::HelloWorld), + InstanceMethod("helloGui", &SwiftAddon::HelloGui), + InstanceMethod("on", &SwiftAddon::On) + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("SwiftAddon", func); + return exports; + } + + struct CallbackData { + std::string eventType; + std::string payload; + SwiftAddon* addon; + }; + + SwiftAddon(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) + , env_(info.Env()) + , emitter(Napi::Persistent(Napi::Object::New(info.Env()))) + , callbacks(Napi::Persistent(Napi::Object::New(info.Env()))) + , tsfn_(nullptr) { + + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "SwiftCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void* context, void* data) { + auto* callbackData = static_cast(data); + if (!callbackData) return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) { + delete callbackData; + return; + } + + try { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } catch (...) {} + + delete callbackData; + }, + &tsfn_ + ); + + if (status != napi_ok) { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + auto makeCallback = [this](const char* eventType) { + return ^(NSString* payload) { + if (tsfn_ != nullptr) { + auto* data = new CallbackData{ + eventType, + std::string([payload UTF8String]), + this + }; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + [SwiftBridge setTodoAddedCallback:makeCallback("todoAdded")]; + [SwiftBridge setTodoUpdatedCallback:makeCallback("todoUpdated")]; + [SwiftBridge setTodoDeletedCallback:makeCallback("todoDeleted")]; + } + + ~SwiftAddon() { + if (tsfn_ != nullptr) { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + NSString* nsInput = [NSString stringWithUTF8String:input.c_str()]; + NSString* result = [SwiftBridge helloWorld:nsInput]; + + return Napi::String::New(env, [result UTF8String]); + } + + void HelloGui(const Napi::CallbackInfo& info) { + [SwiftBridge helloGui]; + } + + Napi::Value On(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + return SwiftAddon::Init(env, exports); +} + +NODE_API_MODULE(swift_addon, Init) +``` + +## 6) Creating a JavaScript Wrapper + +You're so close! We now have working Objective-C, Swift, and thread-safe ways to expose methods and events to JavaScript. In this final step, let's create a JavaScript wrapper in `js/index.js` to provide a more friendly API: + +```js title='js/index.js' @ts-expect-error=[10] +const EventEmitter = require('events') + +class SwiftAddon extends EventEmitter { + constructor () { + super() + + if (process.platform !== 'darwin') { + throw new Error('This module is only available on macOS') + } + + const native = require('bindings')('swift_addon') + this.addon = new native.SwiftAddon() + + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }) + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.parse(payload)) + }) + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.parse(payload)) + }) + } + + helloWorld (input = '') { + return this.addon.helloWorld(input) + } + + helloGui () { + this.addon.helloGui() + } + + parse (payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'darwin') { + module.exports = new SwiftAddon() +} else { + module.exports = {} +} +``` + +This wrapper: + +1. Extends EventEmitter to provide event support +2. Checks if we're running on macOS +3. Loads the native addon +4. Sets up event listeners and forwards them +5. Provides a clean API for our functions +6. Parses JSON payloads and converts timestamps to JavaScript Date objects + +## 7) Building and Testing the Addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +Please note that you _cannot_ call this script from Node.js directly, since Node.js doesn't set up an "app" in the eyes of macOS. Electron does though, so you can test your code by requiring and calling it from Electron. + +## Conclusion + +You've now built a complete native Node.js addon for macOS using Swift and SwiftUI. This provides a foundation for building more complex macOS-specific features in your Electron apps, giving you the best of both worlds: the ease of web technologies with the power of native macOS code. + +The approach demonstrated here allows you to: + +* Setting up a project structure that bridges Swift, Objective-C, and JavaScript +* Implementing Swift code with SwiftUI for native UI +* Creating an Objective-C bridge to connect Swift with Node.js +* Setting up bidirectional communication using callbacks and events +* Configuring a custom build process to compile Swift code + +For more information on developing with Swift and Swift, refer to Apple's developer documentation: + +* [Swift Programming Language](https://developer.apple.com/swift/) +* [SwiftUI Framework](https://developer.apple.com/documentation/swiftui) +* [macOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/macos) +* [Swift and Objective-C Interoperability Guide](https://developer.apple.com/documentation/swift/importing-swift-into-objective-c) From ec1704a1dd67fd4c892bcd1e369f70b25b526cdd Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 20:11:34 +0200 Subject: [PATCH 266/339] docs: mention Azure Trusted Signing (#47383) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- docs/tutorial/code-signing.md | 37 ++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/tutorial/code-signing.md b/docs/tutorial/code-signing.md index 880b36f16f53e..d0eff31e5fe38 100644 --- a/docs/tutorial/code-signing.md +++ b/docs/tutorial/code-signing.md @@ -78,6 +78,8 @@ See the [Mac App Store Guide][]. ## Signing Windows builds +### Using traditional certificates + Before you can code sign your application, you need to acquire a code signing certificate. Unlike Apple, Microsoft allows developers to purchase those certificates on the open market. They are usually sold by the same companies @@ -117,13 +119,13 @@ expose configuration options through a `windowsSign` property. You can either us to sign files directly - or use the same `windowsSign` configuration across Electron Forge, [`@electron/packager`][], [`electron-winstaller`][], and [`electron-wix-msi`][]. -### Using Electron Forge +#### Using Electron Forge Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows` and `WiX MSI` installers. Detailed instructions on how to configure your application can be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-windows). -### Using Electron Packager +#### Using Electron Packager If you're not using an integrated build pipeline like Forge, you are likely using [`@electron/packager`][], which includes [`@electron/windows-sign`][]. @@ -146,7 +148,7 @@ packager({ }) ``` -### Using electron-winstaller (Squirrel.Windows) +#### Using electron-winstaller (Squirrel.Windows) [`electron-winstaller`][] is a package that can generate Squirrel.Windows installers for your Electron app. This is the tool used under the hood by Electron Forge's @@ -178,7 +180,7 @@ try { For full configuration options, check out the [`electron-winstaller`][] repository! -### Using electron-wix-msi (WiX MSI) +#### Using electron-wix-msi (WiX MSI) [`electron-wix-msi`][] is a package that can generate MSI installers for your Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi]. @@ -221,11 +223,32 @@ await msiCreator.compile() For full configuration options, check out the [`electron-wix-msi`][] repository! -### Using Electron Builder +#### Using Electron Builder Electron Builder comes with a custom solution for signing your application. You can find [its documentation here](https://www.electron.build/code-signing). +### Using Azure Trusted Signing + +[Azure Trusted Signing][] is Microsoft's modern cloud-based alternative to EV certificates. +It is the cheapest option for code signing on Windows, and it gets rid of SmartScreen warnings. + +As of May 2025, Azure Trusted Signing is [available][trusted-signing-availability] to US and +Canada-based organizations with 3+ years of verifiable business history. Microsoft is looking +to make the program more widely available. If you're reading this at a later point, it could +make sense to check if the eligibility criteria have changed. + +#### Using Electron Forge + +Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows` +and `WiX MSI` installers. Instructions for Azure Trusted Signing can be found +[here][forge-trusted-signing]. + +#### Using Electron Builder + +The Electron Builder documentation for Azure Trusted Signing can be found +[here][builder-trusted-signing]. + ### Signing Windows Store applications See the [Windows Store Guide][]. @@ -243,3 +266,7 @@ See the [Windows Store Guide][]. [windows store guide]: ./windows-store-guide.md [maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows [maker-msi]: https://www.electronforge.io/config/makers/wix-msi +[azure trusted signing]: https://azure.microsoft.com/en-us/products/trusted-signing +[trusted-signing-availability]: https://techcommunity.microsoft.com/blog/microsoft-security-blog/trusted-signing-public-preview-update/4399713 +[forge-trusted-signing]: https://www.electronforge.io/guides/code-signing/code-signing-windows#using-azure-trusted-signing +[builder-trusted-signing]: https://www.electron.build/code-signing-win#using-azure-trusted-signing-beta From b328de39e560739092d06991466730e733f2bddc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 13:01:32 +0200 Subject: [PATCH 267/339] feat: [net] add "priority" option to net.request (#47320) document the default value of priority option Update the priority test to not use the httpbin.org as server Fixed the lint errors Fixed the build error Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Zeeker <13848632+zeeker999@users.noreply.github.com> --- docs/api/client-request.md | 4 ++ lib/common/api/net-client-request.ts | 6 +- shell/common/api/electron_api_url_loader.cc | 18 +++++ spec/api-net-spec.ts | 75 ++++++++++++++++++++- typings/internal-ambient.d.ts | 2 + 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/docs/api/client-request.md b/docs/api/client-request.md index 21490bf1f1018..d6e27d6deb951 100644 --- a/docs/api/client-request.md +++ b/docs/api/client-request.md @@ -60,6 +60,10 @@ following properties: `strict-origin-when-cross-origin`. * `cache` string (optional) - can be `default`, `no-store`, `reload`, `no-cache`, `force-cache` or `only-if-cached`. + * `priority` string (optional) - can be `throttled`, `idle`, `lowest`, + `low`, `medium`, or `highest`. Defaults to `idle`. + * `priorityIncremental` boolean (optional) - the incremental loading flag as part + of HTTP extensible priorities (RFC 9218). Default is `true`. `options` properties such as `protocol`, `host`, `hostname`, `port` and `path` strictly follow the Node.js model as described in the diff --git a/lib/common/api/net-client-request.ts b/lib/common/api/net-client-request.ts index 1a087ae476477..a2682da670d22 100644 --- a/lib/common/api/net-client-request.ts +++ b/lib/common/api/net-client-request.ts @@ -288,8 +288,12 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod origin: options.origin, referrerPolicy: options.referrerPolicy, cache: options.cache, - allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols) + allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols), + priority: options.priority }; + if ('priorityIncremental' in options) { + urlLoaderOptions.priorityIncremental = options.priorityIncremental; + } const headers: Record = options.headers || {}; for (const [name, value] of Object.entries(headers)) { validateHeader(name, value); diff --git a/shell/common/api/electron_api_url_loader.cc b/shell/common/api/electron_api_url_loader.cc index be953ab7fea19..099fcd8d5963d 100644 --- a/shell/common/api/electron_api_url_loader.cc +++ b/shell/common/api/electron_api_url_loader.cc @@ -644,6 +644,24 @@ gin::Handle SimpleURLLoaderWrapper::Create( break; } + if (std::string priority; opts.Get("priority", &priority)) { + static constexpr auto Lookup = + base::MakeFixedFlatMap({ + {"throttled", net::THROTTLED}, + {"idle", net::IDLE}, + {"lowest", net::LOWEST}, + {"low", net::LOW}, + {"medium", net::MEDIUM}, + {"highest", net::HIGHEST}, + }); + if (auto iter = Lookup.find(priority); iter != Lookup.end()) + request->priority = iter->second; + } + if (bool priorityIncremental = request->priority_incremental; + opts.Get("priorityIncremental", &priorityIncremental)) { + request->priority_incremental = priorityIncremental; + } + const bool use_session_cookies = opts.ValueOrDefault("useSessionCookies", false); int options = network::mojom::kURLLoadOptionSniffMimeType; diff --git a/spec/api-net-spec.ts b/spec/api-net-spec.ts index 5d98385d60f4a..dbbab83c8947d 100644 --- a/spec/api-net-spec.ts +++ b/spec/api-net-spec.ts @@ -1,15 +1,19 @@ -import { net, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main'; +import { net, session, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main'; import { expect } from 'chai'; import { once } from 'node:events'; +import * as fs from 'node:fs'; import * as http from 'node:http'; +import * as http2 from 'node:http2'; import * as path from 'node:path'; import { setTimeout } from 'node:timers/promises'; import { collectStreamBody, collectStreamBodyBuffer, getResponse, kOneKiloByte, kOneMegaByte, randomBuffer, randomString, respondNTimes, respondOnce } from './lib/net-helpers'; +import { listen, defer } from './lib/spec-helpers'; const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js'); +const fixturesPath = path.resolve(__dirname, 'fixtures'); async function itUtility (name: string, fn?: Function, args?: {[key:string]: any}) { it(`${name} in utility process`, async () => { @@ -46,6 +50,34 @@ describe('net module', () => { } }); + let http2URL: string; + + const certPath = path.join(fixturesPath, 'certificates'); + const h2server = http2.createSecureServer({ + key: fs.readFileSync(path.join(certPath, 'server.key')), + cert: fs.readFileSync(path.join(certPath, 'server.pem')) + }, async (req, res) => { + if (req.method === 'POST') { + const chunks = []; + for await (const chunk of req) chunks.push(chunk); + res.end(Buffer.concat(chunks).toString('utf8')); + } else if (req.method === 'GET' && req.headers[':path'] === '/get') { + res.end(JSON.stringify({ + headers: req.headers + })); + } else { + res.end(''); + } + }); + + before(async () => { + http2URL = (await listen(h2server)).url + '/'; + }); + + after(() => { + h2server.close(); + }); + for (const test of [itIgnoringArgs, itUtility]) { describe('HTTP basics', () => { test('should be able to issue a basic GET request', async () => { @@ -1615,4 +1647,45 @@ describe('net module', () => { }); }); } + + for (const test of [itIgnoringArgs]) { + describe('ClientRequest API', () => { + for (const [priorityName, urgency] of Object.entries({ + throttled: 'u=5', + idle: 'u=4', + lowest: '', + low: 'u=2', + medium: 'u=1', + highest: 'u=0' + })) { + for (const priorityIncremental of [true, false]) { + test(`should set priority to ${priorityName}/${priorityIncremental} if requested`, async () => { + // Priority header is available on HTTP/2, which is only + // supported over TLS, so... + session.defaultSession.setCertificateVerifyProc((req, cb) => cb(0)); + defer(() => { + session.defaultSession.setCertificateVerifyProc(null); + }); + + const urlRequest = net.request({ + url: `${http2URL}get`, + priority: priorityName as any, + priorityIncremental + }); + const response = await getResponse(urlRequest); + const data = JSON.parse(await collectStreamBody(response)); + let expectedPriority = urgency; + if (priorityIncremental) { + expectedPriority = expectedPriority ? expectedPriority + ', i' : 'i'; + } + if (expectedPriority === '') { + expect(data.headers.priority).to.be.undefined(); + } else { + expect(data.headers.priority).to.be.a('string').and.equal(expectedPriority); + } + }, { priorityName, urgency, priorityIncremental }); + } + } + }); + } }); diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 21e652df5e453..2f0e6b87f0e4d 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -177,6 +177,8 @@ declare namespace NodeJS { mode?: string; destination?: string; bypassCustomProtocolHandlers?: boolean; + priority?: 'throttled' | 'idle' | 'lowest' | 'low' | 'medium' | 'highest'; + priorityIncremental?: boolean; }; type ResponseHead = { statusCode: number; From 15e536b14b47001a4f4f728bad3f2d6eac21051f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:00:00 +0200 Subject: [PATCH 268/339] build: cache gitcache dir (#47409) * build: cache gitcache dir (#47328) * revert build: migrate to new chromium git auth * build: cache gitcache dir Co-authored-by: John Kleinschmidt * chore: update patches * build: allow updating git cache from workflow dispatch * temporarily build cache for 36 * revert temp changes --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/build-git-cache/action.yml | 83 +++++++++++++++++++ .github/actions/checkout/action.yml | 16 +++- .../actions/set-chromium-cookie/action.yml | 58 +++++++++++++ .../set-chromium-git-helper/action.yml | 41 --------- .github/workflows/build-git-cache.yml | 74 +++++++++++++++++ .github/workflows/build.yml | 10 +-- .github/workflows/linux-publish.yml | 3 +- .github/workflows/macos-publish.yml | 3 +- .github/workflows/pipeline-electron-lint.yml | 7 +- .../pipeline-segment-electron-build.yml | 8 +- .../pipeline-segment-electron-gn-check.yml | 4 +- .../pipeline-segment-electron-test.yml | 8 +- .../pipeline-segment-node-nan-test.yml | 11 ++- .github/workflows/windows-publish.yml | 3 +- .../chromium/cherry-pick-f1e6422a355c.patch | 2 +- ...n_electron_module_via_the_esm_loader.patch | 2 +- patches/v8/.patches | 2 - patches/v8/cherry-pick-45eb42cd398e.patch | 32 ------- patches/v8/cherry-pick-7bc0a67ebfbf.patch | 53 ------------ 19 files changed, 256 insertions(+), 164 deletions(-) create mode 100644 .github/actions/build-git-cache/action.yml create mode 100644 .github/actions/set-chromium-cookie/action.yml delete mode 100644 .github/actions/set-chromium-git-helper/action.yml create mode 100644 .github/workflows/build-git-cache.yml delete mode 100644 patches/v8/cherry-pick-45eb42cd398e.patch delete mode 100644 patches/v8/cherry-pick-7bc0a67ebfbf.patch diff --git a/.github/actions/build-git-cache/action.yml b/.github/actions/build-git-cache/action.yml new file mode 100644 index 0000000000000..6a50666a50fbd --- /dev/null +++ b/.github/actions/build-git-cache/action.yml @@ -0,0 +1,83 @@ +name: 'Build Git Cache' +description: 'Runs a gclient sync to build the git cache for Electron' +inputs: + target-platform: + description: 'Target platform, should be linux, win, macos' +runs: + using: "composite" + steps: + - name: Set GIT_CACHE_PATH to make gclient to use the cache + shell: bash + run: | + echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie + - name: Install Build Tools + uses: ./src/electron/.github/actions/install-build-tools + - name: Set up cache drive + shell: bash + run: | + if [ "${{ inputs.target-platform }}" = "win" ]; then + echo "CACHE_DRIVE=/mnt/win-cache" >> $GITHUB_ENV + else + echo "CACHE_DRIVE=/mnt/cross-instance-cache" >> $GITHUB_ENV + fi + - name: Check cross instance cache disk space + shell: bash + run: | + # if there is less than 35 GB free space then creating the cache might fail so exit early + freespace=`df -m $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + freespace_human=`df -h $CACHE_DRIVE | grep -w $CACHE_DRIVE | awk '{print $4}'` + if [ $freespace -le 35000 ]; then + echo "The cross mount cache has $freespace_human free space which is not enough - exiting" + exit 1 + else + echo "The cross mount cache has $freespace_human free space - continuing" + fi + - name: Restore gitcache + shell: bash + run: | + GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar" + if [ ! -f "$GIT_CACHE_TAR" ]; then + echo "Git cache tar file does not exist, skipping restore" + exit 0 + fi + echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH" + mkdir -p $GIT_CACHE_PATH + tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH + - name: Gclient Sync + shell: bash + run: | + e d gclient config \ + --name "src/electron" \ + --unmanaged \ + ${GCLIENT_EXTRA_ARGS} \ + "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" + + if [ "$TARGET_OS" != "" ]; then + echo "target_os=['$TARGET_OS']" >> ./.gclient + fi + + ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags --nohooks -vv + - name: Compress Git Cache Directory + shell: bash + run: | + echo "Uncompressed gitcache size: $(du -sh $GIT_CACHE_PATH | cut -f1 -d' ')" + cd $GIT_CACHE_PATH + tar -cf ../gitcache.tar . + cd .. + echo "Compressed gitcache to $(du -sh gitcache.tar | cut -f1 -d' ')" + # remove the old cache file if it exists + if [ -f $CACHE_DRIVE/gitcache.tar ]; then + echo "Removing old gitcache.tar from $CACHE_DRIVE" + rm $CACHE_DRIVE/gitcache.tar + fi + cp ./gitcache.tar $CACHE_DRIVE/ + - name: Wait for active SSH sessions + shell: bash + if: always() && !cancelled() + run: | + while [ -f /var/.ssh-lock ] + do + sleep 60 + done diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 7cfbd542c1b60..49a08eead649d 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -20,8 +20,8 @@ runs: echo "GIT_CACHE_PATH=$(pwd)/git-cache" >> $GITHUB_ENV - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash @@ -83,6 +83,18 @@ runs: - name: Add patch conflict problem matcher shell: bash run: echo "::add-matcher::src/electron/.github/problem-matchers/patch-conflict.json" + - name: Restore gitcache + if: steps.check-cache.outputs.cache_exists == 'false' + shell: bash + run: | + GIT_CACHE_TAR="$CACHE_DRIVE/gitcache.tar" + if [ ! -f "$GIT_CACHE_TAR" ]; then + echo "Git cache tar file does not exist, skipping restore" + exit 0 + fi + echo "Restoring git cache from $GIT_CACHE_TAR to $GIT_CACHE_PATH" + mkdir -p $GIT_CACHE_PATH + tar -xf $GIT_CACHE_TAR -C $GIT_CACHE_PATH - name: Gclient Sync if: steps.check-cache.outputs.cache_exists == 'false' shell: bash diff --git a/.github/actions/set-chromium-cookie/action.yml b/.github/actions/set-chromium-cookie/action.yml new file mode 100644 index 0000000000000..2011655e29b59 --- /dev/null +++ b/.github/actions/set-chromium-cookie/action.yml @@ -0,0 +1,58 @@ +name: 'Set Chromium Git Cookie' +description: 'Sets an authenticated cookie from Chromium to allow for a higher request limit' +runs: + using: "composite" + steps: + - name: Set the git cookie from chromium.googlesource.com (Unix) + if: ${{ runner.os != 'Windows' }} + shell: bash + run: | + if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then + echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate." + exit 0 + fi + + eval 'set +o history' 2>/dev/null || setopt HIST_IGNORE_SPACE 2>/dev/null + touch ~/.gitcookies + chmod 0600 ~/.gitcookies + + git config --global http.cookiefile ~/.gitcookies + + tr , \\t <<\__END__ >>~/.gitcookies + ${{ env.CHROMIUM_GIT_COOKIE }} + __END__ + eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null + + RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self) + if [[ $RESPONSE == ")]}'"* ]]; then + # Extract account email for verification + EMAIL=$(echo "$RESPONSE" | tail -c +5 | jq -r '.email // "No email found"') + echo "Cookie authentication successful - authenticated as: $EMAIL" + else + echo "Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE is set correctly" + echo $RESPONSE + fi + - name: Set the git cookie from chromium.googlesource.com (Windows) + if: ${{ runner.os == 'Windows' }} + shell: cmd + run: | + if "%CHROMIUM_GIT_COOKIE_WINDOWS_STRING%"=="" ( + echo CHROMIUM_GIT_COOKIE_WINDOWS_STRING is not set - cannot authenticate. + exit /b 0 + ) + + git config --global http.cookiefile "%USERPROFILE%\.gitcookies" + powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies" + + curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt + + findstr /B /C:")]}'" response.txt > nul + if %ERRORLEVEL% EQU 0 ( + echo Cookie authentication successful + powershell -NoProfile -Command "& {$content = Get-Content -Raw response.txt; $content = $content.Substring(4); try { $json = ConvertFrom-Json $content; if($json.email) { Write-Host 'Authenticated as:' $json.email } else { Write-Host 'No email found in response' } } catch { Write-Host 'Error parsing JSON:' $_ }}" + ) else ( + echo Cookie authentication failed - ensure CHROMIUM_GIT_COOKIE_WINDOWS_STRING is set correctly + type response.txt + ) + + del response.txt diff --git a/.github/actions/set-chromium-git-helper/action.yml b/.github/actions/set-chromium-git-helper/action.yml deleted file mode 100644 index bdc0ceb303d25..0000000000000 --- a/.github/actions/set-chromium-git-helper/action.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: 'Set Chromium Git Helper' -description: 'Sets Chromium Git Helper to allow for a higher request limit' -runs: - using: "composite" - steps: - - name: Save the chromium git credentials to a file - shell: bash - run: | - if [[ -z "${{ env.CHROMIUM_GIT_AUTH }}" ]]; then - echo "CHROMIUM_GIT_AUTH is not set - cannot authenticate." - exit 0 - fi - if [[ "${{ runner.os }}" != "Windows" ]]; then - cd $HOME - fi - echo "${{ env.CHROMIUM_GIT_AUTH }}" > .chromium_git_auth - - - name: Set the chromium git helper to use auth from a file - shell: bash - run: | - if [[ "${{ runner.os }}" == "Windows" ]]; then - if [[ ! -f "/c/actions-runner/_work/electron/electron/.chromium_git_auth" ]]; then - echo "File /c/actions-runner/_work/electron/electron/.chromium_git_auth does not exist - cannot authenticate." - exit 0 - fi - else - if [[ ! -f "$HOME/.chromium_git_auth" ]]; then - echo "File $HOME/.chromium_git_auth does not exist - cannot authenticate." - exit 0 - fi - fi - if [[ -z "${{ env.CHROMIUM_GIT_USER }}" ]]; then - echo "CHROMIUM_GIT_USER is not set - cannot authenticate." - exit 0 - fi - git config --global credential.https://chromium.googlesource.com.username "${{ env.CHROMIUM_GIT_USER }}" - if [[ "${{ runner.os }}" == "Windows" ]]; then - git config --global credential.https://chromium.googlesource.com.helper '!f() { test "$1" = get && echo "password=$(cat /c/actions-runner/_work/electron/electron/.chromium_git_auth)"; }; f' - else - git config --global credential.https://chromium.googlesource.com.helper '!f() { test "$1" = get && echo "password=$(cat $HOME/.chromium_git_auth)"; }; f' - fi diff --git a/.github/workflows/build-git-cache.yml b/.github/workflows/build-git-cache.yml new file mode 100644 index 0000000000000..0fddbd4522a58 --- /dev/null +++ b/.github/workflows/build-git-cache.yml @@ -0,0 +1,74 @@ +name: Build Git Cache +# This workflow updates git cache on the cross-instance cache volumes +# It runs daily at midnight. + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + build-git-cache-linux: + runs-on: electron-arc-linux-amd64-32core + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root + volumes: + - /mnt/cross-instance-cache:/mnt/cross-instance-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: linux + + build-git-cache-windows: + runs-on: electron-arc-linux-amd64-32core + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root --device /dev/fuse --cap-add SYS_ADMIN + volumes: + - /mnt/win-cache:/mnt/win-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' + TARGET_OS: 'win' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: win + + build-git-cache-macos: + runs-on: electron-arc-linux-amd64-32core + # This job updates the same git cache as linux, so it needs to run after the linux one. + needs: build-git-cache-linux + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root + volumes: + - /mnt/cross-instance-cache:/mnt/cross-instance-cache + env: + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - name: Checkout Electron + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: src/electron + fetch-depth: 0 + - name: Build Git Cache + uses: ./src/electron/.github/actions/build-git-cache + with: + target-platform: macos \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ace3bae71f5a4..0eaa4c7d87295 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,8 +100,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' outputs: build-image-sha: ${{ needs.setup.outputs.build-image-sha }} @@ -129,8 +128,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' PATCH_UP_APP_CREDS: ${{ secrets.PATCH_UP_APP_CREDS }} outputs: @@ -156,8 +154,8 @@ jobs: - /mnt/win-cache:/mnt/win-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index a003a15fc1184..8cadd26d23bcc 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -27,8 +27,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - name: Checkout Electron diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index 3e4654445092a..c7241b6a3bb00 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -28,8 +28,7 @@ jobs: - /mnt/cross-instance-cache:/mnt/cross-instance-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' steps: - name: Checkout Electron diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index a49ce67b62f85..6cdbff0259952 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -13,8 +13,7 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} jobs: lint: @@ -31,8 +30,8 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Setup third_party Depot Tools shell: bash run: | diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index 297b146be635b..8d2821a61a923 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -65,8 +65,8 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }} ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }} @@ -127,8 +127,8 @@ jobs: GN_EXTRA_ARGS='is_asan=true' fi echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Generate DEPS Hash diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml index 9e74404070b13..48fe703078145 100644 --- a/.github/workflows/pipeline-segment-electron-gn-check.yml +++ b/.github/workflows/pipeline-segment-electron-gn-check.yml @@ -66,8 +66,8 @@ jobs: - name: Check disk space after freeing up space if: ${{ inputs.target-platform == 'macos' }} run: df -h - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Enable windows toolchain diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 90e3d6c207e1a..369bb86104703 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -36,8 +36,8 @@ permissions: pull-requests: read env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} ELECTRON_OUT_DIR: Default ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} @@ -126,8 +126,8 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install Dependencies uses: ./src/electron/.github/actions/install-dependencies - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Get Depot Tools timeout-minutes: 5 run: | diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index 6af170aaba43a..ee381add0dbb4 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -31,8 +31,7 @@ concurrency: cancel-in-progress: ${{ github.ref_protected != true }} env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }} ELECTRON_OUT_DIR: Default ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }} @@ -52,8 +51,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools @@ -106,8 +105,8 @@ jobs: path: src/electron fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Set Chromium Git Helper - uses: ./src/electron/.github/actions/set-chromium-git-helper + - name: Set Chromium Git Cookie + uses: ./src/electron/.github/actions/set-chromium-cookie - name: Install Build Tools uses: ./src/electron/.github/actions/install-build-tools - name: Init Build Tools diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index c0acf202f6d30..e8b7c6172fdd8 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -28,8 +28,7 @@ jobs: - /mnt/win-cache:/mnt/win-cache - /var/run/sas:/var/run/sas env: - CHROMIUM_GIT_AUTH: ${{ secrets.CHROMIUM_GIT_AUTH }} - CHROMIUM_GIT_USER: ${{ secrets.CHROMIUM_GIT_USER }} + CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }} GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True' TARGET_OS: 'win' ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1' diff --git a/patches/chromium/cherry-pick-f1e6422a355c.patch b/patches/chromium/cherry-pick-f1e6422a355c.patch index 82f87c24146fa..a95ef07b925a8 100644 --- a/patches/chromium/cherry-pick-f1e6422a355c.patch +++ b/patches/chromium/cherry-pick-f1e6422a355c.patch @@ -52,7 +52,7 @@ Commit-Queue: Yoshisato Yanagisawa Cr-Commit-Position: refs/heads/main@{#1463892} diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc -index 720c38313bd132a12b8465a4a2ca1bc32cf6f569..c2c8ee137a4b3a500954dcc9eaf3dea003613876 100644 +index b6f2e57224c8a50ffa65b70b30cf0eda77691613..68ac835c14901a22cd8ae1b55470e4b0a3d9dea8 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc @@ -2828,6 +2828,13 @@ BASE_FEATURE(kWebviewAccelerateSmallCanvases, diff --git a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch index 3fbb7b9faf5c4..dec3592cbdc61 100644 --- a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch +++ b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch @@ -64,7 +64,7 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..09a332c0999086b30fd952d9456f7889 } } diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js -index ae03073aff8140b11c63b6c05d831ba573568dba..b70c7cfe40e2eaaeea7b5ad6fcf0aaee87276aa1 100644 +index aff686577df3c366f06f90666e23a03fc376cf53..e8857a151428acd6f8ece74d92774a18accc1e13 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -492,7 +492,7 @@ class ModuleLoader { diff --git a/patches/v8/.patches b/patches/v8/.patches index eaac2faf9f98c..199780cede8b0 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -1,4 +1,2 @@ chore_allow_customizing_microtask_policy_per_context.patch enable_--perf-prof_flag_on_macos.patch -cherry-pick-7bc0a67ebfbf.patch -cherry-pick-45eb42cd398e.patch diff --git a/patches/v8/cherry-pick-45eb42cd398e.patch b/patches/v8/cherry-pick-45eb42cd398e.patch deleted file mode 100644 index ad85ca62646f0..0000000000000 --- a/patches/v8/cherry-pick-45eb42cd398e.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Igor Sheludko -Date: Tue, 27 May 2025 21:34:45 +0200 -Subject: Convert Smi to Word64 using zero extension - -... when a known type range contains only positive values. - -Bug: 420637585 -Change-Id: I8d9bb3f2fe2e5268e1659bb4ea7bbf97bfb52288 -Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594731 -Reviewed-by: Nico Hartmann -Commit-Queue: Igor Sheludko -Cr-Commit-Position: refs/heads/main@{#100538} - -diff --git a/src/compiler/representation-change.cc b/src/compiler/representation-change.cc -index f0d4f59c7625103a3fed789bfc1d08c407e00c08..8ce36e6a0f4a4a119a35c61dedcb22d2f55fa982 100644 ---- a/src/compiler/representation-change.cc -+++ b/src/compiler/representation-change.cc -@@ -1323,7 +1323,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor( - } - } else if (output_rep == MachineRepresentation::kTaggedSigned) { - if (output_type.Is(Type::SignedSmall())) { -- op = simplified()->ChangeTaggedSignedToInt64(); -+ if (output_type.IsRange() && output_type.AsRange()->Min() >= 0) { -+ node = InsertChangeTaggedSignedToInt32(node); -+ op = machine()->ChangeUint32ToUint64(); -+ } else { -+ op = simplified()->ChangeTaggedSignedToInt64(); -+ } - } else { - return TypeError(node, output_rep, output_type, - MachineRepresentation::kWord64); diff --git a/patches/v8/cherry-pick-7bc0a67ebfbf.patch b/patches/v8/cherry-pick-7bc0a67ebfbf.patch deleted file mode 100644 index ff5e0b4f29805..0000000000000 --- a/patches/v8/cherry-pick-7bc0a67ebfbf.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Leszek Swirski -Date: Tue, 27 May 2025 20:33:19 +0200 -Subject: Weaken alias analysis in store-store elimination - -Bug: 420636529 -Change-Id: I7c5a8f47960708cecbb27d811eedc7f754933deb -Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594051 -Reviewed-by: Shu-yu Guo -Auto-Submit: Leszek Swirski -Commit-Queue: Leszek Swirski -Cr-Commit-Position: refs/heads/main@{#100530} - -diff --git a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h -index 45654a022fbaa67634d68d7d6e9dab7a5a50cb28..e058a41f23e29bbe4f5098608b340ccfef50bf98 100644 ---- a/src/compiler/turboshaft/store-store-elimination-reducer-inl.h -+++ b/src/compiler/turboshaft/store-store-elimination-reducer-inl.h -@@ -325,10 +325,11 @@ class RedundantStoreAnalysis { - // TODO(nicohartmann@): Use the new effect flags to distinguish heap - // access once available. - const bool is_on_heap_store = store.kind.tagged_base; -- const bool is_field_store = !store.index().valid(); -+ const bool is_fixed_offset_store = !store.index().valid(); - const uint8_t size = store.stored_rep.SizeInBytes(); -- // For now we consider only stores of fields of objects on the heap. -- if (is_on_heap_store && is_field_store) { -+ // For now we consider only stores of fixed offsets of objects on the -+ // heap. -+ if (is_on_heap_store && is_fixed_offset_store) { - bool is_eliminable_store = false; - switch (table_.GetObservability(store.base(), store.offset, size)) { - case StoreObservability::kUnobservable: -@@ -415,11 +416,16 @@ class RedundantStoreAnalysis { - // TODO(nicohartmann@): Use the new effect flags to distinguish heap - // access once available. - const bool is_on_heap_load = load.kind.tagged_base; -- const bool is_field_load = !load.index().valid(); -+ const bool is_fixed_offset_load = !load.index().valid(); - // For now we consider only loads of fields of objects on the heap. -- if (is_on_heap_load && is_field_load) { -- table_.MarkPotentiallyAliasingStoresAsObservable(load.base(), -- load.offset); -+ if (is_on_heap_load) { -+ if (is_fixed_offset_load) { -+ table_.MarkPotentiallyAliasingStoresAsObservable(load.base(), -+ load.offset); -+ } else { -+ // A dynamically indexed load might alias any fixed offset. -+ table_.MarkAllStoresAsObservable(); -+ } - } - break; - } From 47caba64abe70eb6c19d7eb8bd17b36a1930a5fb Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:14:23 +0200 Subject: [PATCH 269/339] docs: no class inheritance (#47432) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Erick Zhao --- docs/api/base-window.md | 4 ++++ docs/api/browser-view.md | 4 ++++ docs/api/browser-window.md | 4 ++++ docs/api/image-view.md | 4 ++++ docs/api/ipc-main-service-worker.md | 4 ++++ docs/api/menu-item.md | 4 ++++ docs/api/menu.md | 4 ++++ docs/api/message-channel-main.md | 4 ++++ docs/api/notification.md | 13 +++++++------ docs/api/share-menu.md | 4 ++++ docs/api/touch-bar.md | 4 ++++ docs/api/tray.md | 4 ++++ docs/api/view.md | 4 ++++ docs/api/web-contents-view.md | 4 ++++ docs/faq.md | 8 ++++++++ 15 files changed, 67 insertions(+), 6 deletions(-) diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 98909de846e22..29461967d942f 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -99,6 +99,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `BaseWindow` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new BaseWindow([options])` * `options` [BaseWindowConstructorOptions](structures/base-window-options.md?inline) (optional) diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index 693363ec4b2eb..87f6ead865960 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -38,6 +38,10 @@ Process: [Main](../glossary.md#main-process) This module cannot be used until the `ready` event of the `app` module is emitted. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Example ```js diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 78997aabd44fe..83bea39c5c1cd 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -150,6 +150,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `BrowserWindow` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new BrowserWindow([options])` * `options` [BrowserWindowConstructorOptions](structures/browser-window-options.md?inline) (optional) diff --git a/docs/api/image-view.md b/docs/api/image-view.md index 854de3054601a..fcc7135ecfa7a 100644 --- a/docs/api/image-view.md +++ b/docs/api/image-view.md @@ -42,6 +42,10 @@ Process: [Main](../glossary.md#main-process) `ImageView` is an [EventEmitter][event-emitter]. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new ImageView()` _Experimental_ Creates an ImageView. diff --git a/docs/api/ipc-main-service-worker.md b/docs/api/ipc-main-service-worker.md index 8995d66ba7a7c..08a9d63a98174 100644 --- a/docs/api/ipc-main-service-worker.md +++ b/docs/api/ipc-main-service-worker.md @@ -11,6 +11,10 @@ Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Instance Methods #### `ipcMainServiceWorker.on(channel, listener)` diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 3c53afc222bc6..8ee760d1cb44a 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -6,6 +6,10 @@ Process: [Main](../glossary.md#main-process) See [`Menu`](menu.md) for examples. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new MenuItem(options)` * `options` Object diff --git a/docs/api/menu.md b/docs/api/menu.md index c94d8fe70d794..9ba6ce00242c0 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -6,6 +6,10 @@ Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new Menu()` Creates a new menu. diff --git a/docs/api/message-channel-main.md b/docs/api/message-channel-main.md index 18339848db6ac..3dff6a6269629 100644 --- a/docs/api/message-channel-main.md +++ b/docs/api/message-channel-main.md @@ -33,6 +33,10 @@ ipcRenderer.on('port', (e) => { }) ``` +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Instance Properties #### `channel.port1` diff --git a/docs/api/notification.md b/docs/api/notification.md index efaa93b39e217..1c5a6e9c98162 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -4,12 +4,9 @@ Process: [Main](../glossary.md#main-process) -:::info Renderer process notifications - -If you want to show notifications from a renderer process you should use the -[web Notifications API](../tutorial/notifications.md) - -::: +> [!NOTE] +> If you want to show notifications from a renderer process you should use the +> [web Notifications API](../tutorial/notifications.md) ## Class: Notification @@ -21,6 +18,10 @@ Process: [Main](../glossary.md#main-process) It creates a new `Notification` with native properties as set by the `options`. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### Static Methods The `Notification` class has the following static methods: diff --git a/docs/api/share-menu.md b/docs/api/share-menu.md index a886ea52682af..c1ffd455743b0 100644 --- a/docs/api/share-menu.md +++ b/docs/api/share-menu.md @@ -13,6 +13,10 @@ For including the share menu as a submenu of other menus, please use the Process: [Main](../glossary.md#main-process) +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new ShareMenu(sharingItem)` * `sharingItem` SharingItem - The item to share. diff --git a/docs/api/touch-bar.md b/docs/api/touch-bar.md index c304f57430832..35f3105416ba3 100644 --- a/docs/api/touch-bar.md +++ b/docs/api/touch-bar.md @@ -1,5 +1,9 @@ # TouchBar +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ## Class: TouchBar > Create TouchBar layouts for native macOS applications diff --git a/docs/api/tray.md b/docs/api/tray.md index 6b112def35918..2ffc4dc671847 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -25,6 +25,10 @@ app.whenReady().then(() => { }) ``` +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + **Platform Considerations** **Linux** diff --git a/docs/api/view.md b/docs/api/view.md index 8e0a23a5d8def..5858d08c0abb8 100644 --- a/docs/api/view.md +++ b/docs/api/view.md @@ -25,6 +25,10 @@ Process: [Main](../glossary.md#main-process) `View` is an [EventEmitter][event-emitter]. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new View()` Creates a new `View`. diff --git a/docs/api/web-contents-view.md b/docs/api/web-contents-view.md index 66bb257cf0edb..802580e26b9bd 100644 --- a/docs/api/web-contents-view.md +++ b/docs/api/web-contents-view.md @@ -32,6 +32,10 @@ Process: [Main](../glossary.md#main-process) `WebContentsView` is an [EventEmitter][event-emitter]. +> [!WARNING] +> Electron's built-in classes cannot be subclassed in user code. +> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules). + ### `new WebContentsView([options])` * `options` Object (optional) diff --git a/docs/faq.md b/docs/faq.md index 114731d69d689..9b5e656d81659 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -152,6 +152,14 @@ The effect is visible only on (some?) LCD screens. Even if you don't see a diffe Notice that just setting the background in the CSS does not have the desired effect. +## Class inheritance does not work with Electron built-in modules + +Electron classes cannot be subclassed with the [`extends`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) +keyword (also known as class inheritance). This feature was never implemented in Electron due +to the added complexity it would add to C++/JavaScript interop in Electron's internals. + +For more information, see [electron/electron#23](https://github.com/electron/electron/issues/23). + [memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management [closures]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures [storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage From cac475579efae633ba740058a1b4f8353feeb7da Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:10:44 -0400 Subject: [PATCH 270/339] fix: printing PDF via `webContents.print()` (#47400) fix: printing PDF via webContents.print() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_web_contents.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index b47e9c5766079..ee0329c83d100 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2955,12 +2955,15 @@ void OnGetDeviceNameToUse(base::WeakPtr web_contents, print_settings.Set(printing::kSettingDpiVertical, dpi.height()); } - auto* print_view_manager = - PrintViewManagerElectron::FromWebContents(web_contents.get()); + content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get()); + if (!rfh) + return; + + auto* print_view_manager = PrintViewManagerElectron::FromWebContents( + content::WebContents::FromRenderFrameHost(rfh)); if (!print_view_manager) return; - content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get()); print_view_manager->PrintNow(rfh, std::move(print_settings), std::move(print_callback)); } @@ -3006,12 +3009,15 @@ void WebContents::Print(gin::Arguments* args) { } if (options.IsEmptyObject()) { - auto* print_view_manager = - PrintViewManagerElectron::FromWebContents(web_contents()); + content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents()); + if (!rfh) + return; + + auto* print_view_manager = PrintViewManagerElectron::FromWebContents( + content::WebContents::FromRenderFrameHost(rfh)); if (!print_view_manager) return; - content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents()); print_view_manager->PrintNow(rfh, std::move(settings), std::move(callback)); return; } From e3939f2e608b1b4d05a78b88beece30cc4f718a8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 18:35:49 -0500 Subject: [PATCH 271/339] docs: mention `kwallet6` command line option (#47438) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- docs/api/safe-storage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api/safe-storage.md b/docs/api/safe-storage.md index d9c7dc3feab47..4f985db5e1951 100644 --- a/docs/api/safe-storage.md +++ b/docs/api/safe-storage.md @@ -73,5 +73,6 @@ command line flag is provided `--password-store="basic"`. is provided `--password-store="kwallet"`. * `kwallet5` - When the desktop session is `kde5` or if the following command line flag is provided `--password-store="kwallet5"`. -* `kwallet6` - When the desktop session is `kde6`. +* `kwallet6` - When the desktop session is `kde6` or if the following command line flag +is provided `--password-store="kwallet6"`. * `unknown` - When the function is called before app has emitted the `ready` event. From 5e9f4eaa3c9a3920c09732986683786974f63ae3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:23:44 +0200 Subject: [PATCH 272/339] docs: remove `electron-quick-start` from README (#47388) Refs https://github.com/electron/.permissions/pull/293 The repo was renamed to `minimal-repro` and is no longer intended to be used as a way to start new projects (see PR above). Since we really want bugs in `electron/electron` to be reported with a Fiddle gist instead of a standalone repo (makes reproducing and bisecting sooo much easier and safer!), I removed the repo from the README completely instead of mentioning as an issue reproduction starting point. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index 07edb95ea80da..2ab98ce41009b 100644 --- a/README.md +++ b/README.md @@ -44,29 +44,17 @@ Each Electron release provides binaries for macOS, Windows, and Linux. * Fedora 32 and newer * Debian 10 and newer -## Quick start & Electron Fiddle +## Electron Fiddle Use [`Electron Fiddle`](https://github.com/electron/fiddle) to build, run, and package small Electron experiments, to see code examples for all of Electron's APIs, and to try out different versions of Electron. It's designed to make the start of your journey with Electron easier. -Alternatively, clone and run the -[electron/electron-quick-start](https://github.com/electron/electron-quick-start) -repository to see a minimal Electron app in action: - -```sh -git clone https://github.com/electron/electron-quick-start -cd electron-quick-start -npm install -npm start -``` - ## Resources for learning Electron * [electronjs.org/docs](https://electronjs.org/docs) - All of Electron's documentation * [electron/fiddle](https://github.com/electron/fiddle) - A tool to build, run, and package small Electron experiments -* [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - A very basic starter Electron app * [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - Sample starter apps created by the community ## Programmatic usage From 55557dd3a6cc2c24daf907450c4de63fd766fac0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:28:41 -0500 Subject: [PATCH 273/339] fix: crash calling `Fetch.continueResponse` with `WebContentsView` (#47442) fix: crash calling Fetch.continueResponse with WebContentsView Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_debugger.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_debugger.cc b/shell/browser/api/electron_api_debugger.cc index 61440400e7528..740e87dd2cb26 100644 --- a/shell/browser/api/electron_api_debugger.cc +++ b/shell/browser/api/electron_api_debugger.cc @@ -81,7 +81,11 @@ void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host, void Debugger::RenderFrameHostChanged(content::RenderFrameHost* old_rfh, content::RenderFrameHost* new_rfh) { - if (agent_host_) { + // ConnectWebContents uses the primary main frame of the webContents, + // so if the new_rfh is not the primary main frame, we don't want to + // reconnect otherwise we'll end up trying to reconnect to a RenderFrameHost + // that already has a DevToolsAgentHost associated with it. + if (agent_host_ && new_rfh->IsInPrimaryMainFrame()) { agent_host_->DisconnectWebContents(); auto* web_contents = content::WebContents::FromRenderFrameHost(new_rfh); agent_host_->ConnectWebContents(web_contents); From 351e6e36f497c806569d1f252af390bd4073a3f1 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:24:50 +0200 Subject: [PATCH 274/339] fix: rework lifetime mgmt of `ClearDataTask`/`ClearDataOperation` (#47411) * fix: rework lifetime mgmt of ClearDataTask/ClearDataOperation Co-authored-by: Shelley Vohr * Update shell/browser/api/electron_api_session.cc Co-authored-by: Robo Co-authored-by: Shelley Vohr * Update shell/browser/api/electron_api_session.cc Co-authored-by: Robo Co-authored-by: Shelley Vohr * Update shell/browser/api/electron_api_session.cc Co-authored-by: Robo Co-authored-by: Shelley Vohr * Update shell/browser/api/electron_api_session.cc Co-authored-by: Robo Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_session.cc | 64 ++++++++++++++--------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index dcb89142e73ff..563d63aa95e7d 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -84,6 +84,7 @@ #include "shell/common/gin_converters/time_converter.h" #include "shell/common/gin_converters/usb_protected_classes_converter.h" #include "shell/common/gin_converters/value_converter.h" +#include "shell/common/gin_helper/cleaned_up_at_exit.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/error_thrower.h" #include "shell/common/gin_helper/object_template_builder.h" @@ -198,9 +199,11 @@ std::vector GetDataTypesFromMask( // Represents a task to clear browsing data for the `clearData` API method. // -// This type manages its own lifetime, deleting itself once the task finishes -// completely. -class ClearDataTask { +// This type manages its own lifetime, +// 1) deleting itself once all the operations created by this task are +// completed. 2) through gin_helper::CleanedUpAtExit, ensuring it's destroyed +// before the node environment shuts down. +class ClearDataTask : public gin_helper::CleanedUpAtExit { public: // Starts running a task. This function will return before the task is // finished, but will resolve or reject the |promise| when it finishes. @@ -211,7 +214,7 @@ class ClearDataTask { std::vector origins, BrowsingDataFilterBuilder::Mode filter_mode, BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode) { - std::shared_ptr task(new ClearDataTask(std::move(promise))); + auto* task = new ClearDataTask(std::move(promise)); // This method counts as an operation. This is important so we can call // `OnOperationFinished` at the end of this method as a fallback if all the @@ -255,42 +258,36 @@ class ClearDataTask { } // This static method counts as an operation. - task->OnOperationFinished(std::nullopt); + task->OnOperationFinished(nullptr, std::nullopt); } private: // An individual |content::BrowsingDataRemover::Remove...| operation as part - // of a full |ClearDataTask|. This class manages its own lifetime, cleaning - // itself up after the operation completes and notifies the task of the - // result. + // of a full |ClearDataTask|. This class is owned by ClearDataTask and cleaned + // up either when the operation completes or when ClearDataTask is destroyed. class ClearDataOperation : private BrowsingDataRemover::Observer { public: - static void Run(std::shared_ptr task, - BrowsingDataRemover* remover, - BrowsingDataRemover::DataType data_type_mask, - std::unique_ptr filter_builder) { - auto* operation = new ClearDataOperation(task, remover); + ClearDataOperation(ClearDataTask* task, BrowsingDataRemover* remover) + : task_(task) { + observation_.Observe(remover); + } + void Start(BrowsingDataRemover* remover, + BrowsingDataRemover::DataType data_type_mask, + std::unique_ptr filter_builder) { remover->RemoveWithFilterAndReply(base::Time::Min(), base::Time::Max(), data_type_mask, kClearOriginTypeAll, - std::move(filter_builder), operation); + std::move(filter_builder), this); } // BrowsingDataRemover::Observer: void OnBrowsingDataRemoverDone( BrowsingDataRemover::DataType failed_data_types) override { - task_->OnOperationFinished(failed_data_types); - delete this; + task_->OnOperationFinished(this, failed_data_types); } private: - ClearDataOperation(std::shared_ptr task, - BrowsingDataRemover* remover) - : task_(task) { - observation_.Observe(remover); - } - - std::shared_ptr task_; + raw_ptr task_; base::ScopedObservation observation_{this}; }; @@ -299,18 +296,20 @@ class ClearDataTask { : promise_(std::move(promise)) {} static void StartOperation( - std::shared_ptr task, + ClearDataTask* task, BrowsingDataRemover* remover, BrowsingDataRemover::DataType data_type_mask, std::unique_ptr filter_builder) { // Track this operation task->operations_running_ += 1; - ClearDataOperation::Run(task, remover, data_type_mask, - std::move(filter_builder)); + auto& operation = task->operations_.emplace_back( + std::make_unique(task, remover)); + operation->Start(remover, data_type_mask, std::move(filter_builder)); } void OnOperationFinished( + ClearDataOperation* operation, std::optional failed_data_types) { DCHECK_GT(operations_running_, 0); operations_running_ -= 1; @@ -319,6 +318,16 @@ class ClearDataTask { failed_data_types_ |= failed_data_types.value(); } + if (operation) { + operations_.erase( + std::remove_if( + operations_.begin(), operations_.end(), + [operation](const std::unique_ptr& op) { + return op.get() == operation; + }), + operations_.end()); + } + // If this is the last operation, then the task is finished if (operations_running_ == 0) { OnTaskFinished(); @@ -346,11 +355,14 @@ class ClearDataTask { promise_.Reject(error); } + + delete this; } int operations_running_ = 0; BrowsingDataRemover::DataType failed_data_types_ = 0ULL; gin_helper::Promise promise_; + std::vector> operations_; }; base::Value::Dict createProxyConfig(ProxyPrefs::ProxyMode proxy_mode, From ddee51eb9e5a4bd1306f950755f7b9363d3464f6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:25:13 +0200 Subject: [PATCH 275/339] docs: update remaining references to `electron-quick-start` (#47449) * chore: udpate remaining references to electron-quick-start Co-authored-by: Anny Yang * chore: Update docs/tutorial/tutorial-1-prerequisites.md Co-authored-by: Niklas Wenzel Co-authored-by: Anny Yang * chore: Update docs/tutorial/tutorial-3-preload.md Co-authored-by: Niklas Wenzel Co-authored-by: Anny Yang * chore: Update docs/tutorial/tutorial-2-first-app.md Co-authored-by: Niklas Wenzel Co-authored-by: Anny Yang * chore: linebreak Co-authored-by: Anny Yang * chore: swap minimal-repro for npx create-electron-app Co-authored-by: Anny Yang * chore: add back code commands Co-authored-by: Anny Yang * chore: add whitespace Co-authored-by: Anny Yang * chore: remove reference to repo containing old quick start Co-authored-by: Anny Yang --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Anny Yang Co-authored-by: Anny Yang --- docs/tutorial/debugging-vscode.md | 43 +++++++++++++++-------- docs/tutorial/snapcraft.md | 33 +++++++++-------- docs/tutorial/tutorial-1-prerequisites.md | 2 +- docs/tutorial/tutorial-2-first-app.md | 2 +- docs/tutorial/tutorial-3-preload.md | 2 +- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/docs/tutorial/debugging-vscode.md b/docs/tutorial/debugging-vscode.md index fa5ccd8f76879..9f8d29d25c22c 100644 --- a/docs/tutorial/debugging-vscode.md +++ b/docs/tutorial/debugging-vscode.md @@ -1,6 +1,7 @@ # Debugging in VSCode -This guide goes over how to set up VSCode debugging for both your own Electron project as well as the native Electron codebase. +This guide goes over how to set up VSCode debugging for both your own Electron +project as well as the native Electron codebase. ## Debugging your Electron app @@ -9,8 +10,8 @@ This guide goes over how to set up VSCode debugging for both your own Electron p #### 1. Open an Electron project in VSCode. ```sh -$ git clone git@github.com:electron/electron-quick-start.git -$ code electron-quick-start +$ npx create-electron-app@latest my-app +$ code my-app ``` #### 2. Add a file `.vscode/launch.json` with the following configuration: @@ -37,23 +38,27 @@ $ code electron-quick-start #### 3. Debugging -Set some breakpoints in `main.js`, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). You should be able to hit the breakpoints. - -Here is a pre-configured project that you can download and directly debug in VSCode: https://github.com/octref/vscode-electron-debug/tree/master/electron-quick-start +Set some breakpoints in `main.js`, and start debugging in the +[Debug View](https://code.visualstudio.com/docs/editor/debugging). You should +be able to hit the breakpoints. ## Debugging the Electron codebase -If you want to build Electron from source and modify the native Electron codebase, this section will help you in testing your modifications. +If you want to build Electron from source and modify the native Electron codebase, +this section will help you in testing your modifications. -For those unsure where to acquire this code or how to build it, [Electron's Build Tools](https://github.com/electron/build-tools) automates and explains most of this process. If you wish to manually set up the environment, you can instead use these [build instructions](../development/build-instructions-gn.md). +For those unsure where to acquire this code or how to build it, +[Electron's Build Tools](https://github.com/electron/build-tools) automates and +explains most of this process. If you wish to manually set up the environment, +you can instead use these [build instructions](../development/build-instructions-gn.md). ### Windows (C++) #### 1. Open an Electron project in VSCode. ```sh -$ git clone git@github.com:electron/electron-quick-start.git -$ code electron-quick-start +$ npx create-electron-app@latest my-app +$ code my-app ``` #### 2. Add a file `.vscode/launch.json` with the following configuration: @@ -86,14 +91,22 @@ $ code electron-quick-start **Configuration Notes** -* `cppvsdbg` requires the [built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) be enabled. +* `cppvsdbg` requires the +[built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) +be enabled. * `${workspaceFolder}` is the full path to Chromium's `src` directory. * `your-executable-location` will be one of the following depending on a few items: - * `Testing`: If you are using the default settings of [Electron's Build-Tools](https://github.com/electron/build-tools) or the default instructions when [building from source](../development/build-instructions-gn.md#building). + * `Testing`: If you are using the default settings of + [Electron's Build-Tools](https://github.com/electron/build-tools) or the default + instructions when [building from source](../development/build-instructions-gn.md#building). * `Release`: If you built a Release build rather than a Testing build. - * `your-directory-name`: If you modified this during your build process from the default, this will be whatever you specified. -* The `args` array string `"your-electron-project-path"` should be the absolute path to either the directory or `main.js` file of the Electron project you are using for testing. In this example, it should be your path to `electron-quick-start`. + * `your-directory-name`: If you modified this during your build process from + the default, this will be whatever you specified. +* The `args` array string `"your-electron-project-path"` should be the absolute +path to either the directory or `main.js` file of the Electron project you are +using for testing. In this example, it should be your path to `my-app`. #### 3. Debugging -Set some breakpoints in the .cc files of your choosing in the native Electron C++ code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). +Set some breakpoints in the .cc files of your choosing in the native Electron C++ +code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). diff --git a/docs/tutorial/snapcraft.md b/docs/tutorial/snapcraft.md index dd4e84495077f..9e6094c28ca9b 100644 --- a/docs/tutorial/snapcraft.md +++ b/docs/tutorial/snapcraft.md @@ -5,8 +5,8 @@ for any Snapcraft environment, including the Ubuntu Software Center. ## Background and Requirements -Together with the broader Linux community, Canonical aims to fix many of the -common software installation problems with the [`snapcraft`](https://snapcraft.io/) +Together with the broader Linux community, Canonical aims to address common +software installation issues through the [`snapcraft`](https://snapcraft.io/) project. Snaps are containerized software packages that include required dependencies, auto-update, and work on all major Linux distributions without system modification. @@ -83,7 +83,14 @@ snap(options) ### Step 1: Create Sample Snapcraft Project -Create your project directory and add the following to `snap/snapcraft.yaml`: +```sh +$ npx create-electron-app@latest my-app +``` + +### Step 2: Create Sample Snapcraft Project + +Create a `snap` directory in your project root and add the following to +`snap/snapcraft.yaml`: ```yaml name: electron-packager-hello-world @@ -97,7 +104,7 @@ grade: stable apps: electron-packager-hello-world: - command: electron-quick-start/electron-quick-start --no-sandbox + command: my-app/my-app --no-sandbox extensions: [gnome] plugs: - browser-support @@ -109,13 +116,13 @@ apps: TMPDIR: $XDG_RUNTIME_DIR parts: - electron-quick-start: + my-app: plugin: nil - source: https://github.com/electron/electron-quick-start.git + source: . override-build: | npm install electron @electron/packager npx electron-packager . --overwrite --platform=linux --output=release-build --prune=true - cp -rv ./electron-quick-start-linux-* $SNAPCRAFT_PART_INSTALL/electron-quick-start + cp -rv ./my-app-linux-* $SNAPCRAFT_PART_INSTALL/my-app build-snaps: - node/14/stable build-packages: @@ -125,12 +132,10 @@ parts: - libnspr4 ``` -If you want to apply this example to an existing project: - -- Replace `source: https://github.com/electron/electron-quick-start.git` with `source: .`. -- Replace all instances of `electron-quick-start` with your project's name. +If you want to apply this example to an existing project, replace all instances +of `my-app` with your project's name. -### Step 2: Build the snap +### Step 3: Build the snap ```sh $ snapcraft @@ -139,13 +144,13 @@ $ snapcraft Snapped electron-packager-hello-world_0.1_amd64.snap ``` -### Step 3: Install the snap +### Step 4: Install the snap ```sh sudo snap install electron-packager-hello-world_0.1_amd64.snap --dangerous ``` -### Step 4: Run the snap +### Step 5: Run the snap ```sh electron-packager-hello-world diff --git a/docs/tutorial/tutorial-1-prerequisites.md b/docs/tutorial/tutorial-1-prerequisites.md index c0990dfbe2c05..e47938bdf81e4 100644 --- a/docs/tutorial/tutorial-1-prerequisites.md +++ b/docs/tutorial/tutorial-1-prerequisites.md @@ -1,6 +1,6 @@ --- title: 'Prerequisites' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-prerequisites hide_title: false --- diff --git a/docs/tutorial/tutorial-2-first-app.md b/docs/tutorial/tutorial-2-first-app.md index 7364d7faf84ac..465c98c0bf26e 100644 --- a/docs/tutorial/tutorial-2-first-app.md +++ b/docs/tutorial/tutorial-2-first-app.md @@ -1,6 +1,6 @@ --- title: 'Building your First App' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-first-app hide_title: false --- diff --git a/docs/tutorial/tutorial-3-preload.md b/docs/tutorial/tutorial-3-preload.md index 0854de8305092..377293d3fe06c 100644 --- a/docs/tutorial/tutorial-3-preload.md +++ b/docs/tutorial/tutorial-3-preload.md @@ -1,6 +1,6 @@ --- title: 'Using Preload Scripts' -description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.' +description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.' slug: tutorial-preload hide_title: false --- From 573c8aa6562727be173ed48ac81e5bdba48be941 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 10:03:05 +0200 Subject: [PATCH 276/339] refactor: have ShowSaveDialogSync() return a std::optional (#47452) * refactor: have ShowSaveDialogSync() return a std::optional Co-authored-by: Charles Kerr * fixup! refactor: have ShowSaveDialogSync() return a std::optional Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_dialog.cc | 5 ++- .../browser/api/electron_api_web_contents.cc | 32 +++++++++++-------- shell/browser/ui/file_dialog.h | 4 ++- shell/browser/ui/file_dialog_linux.cc | 27 +++++++++------- shell/browser/ui/file_dialog_mac.mm | 11 +++---- shell/browser/ui/file_dialog_win.cc | 22 ++++++------- 6 files changed, 56 insertions(+), 45 deletions(-) diff --git a/shell/browser/api/electron_api_dialog.cc b/shell/browser/api/electron_api_dialog.cc index 66c7bf2937539..b5feef3eed004 100644 --- a/shell/browser/api/electron_api_dialog.cc +++ b/shell/browser/api/electron_api_dialog.cc @@ -71,9 +71,8 @@ v8::Local ShowOpenDialog( void ShowSaveDialogSync(const file_dialog::DialogSettings& settings, gin::Arguments* args) { - base::FilePath path; - if (file_dialog::ShowSaveDialogSync(settings, &path)) - args->Return(path); + if (const auto path = file_dialog::ShowSaveDialogSync(settings)) + args->Return(*path); } v8::Local ShowSaveDialog( diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index ee0329c83d100..6d22baefc8bb6 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -18,6 +18,7 @@ #include "base/containers/fixed_flat_map.h" #include "base/containers/flat_set.h" #include "base/containers/id_map.h" +#include "base/containers/map_util.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" #include "base/no_destructor.h" @@ -4024,30 +4025,35 @@ void WebContents::DevToolsSaveToFile(const std::string& url, const std::string& content, bool save_as, bool is_base64) { - base::FilePath path; - auto it = saved_files_.find(url); - if (it != saved_files_.end() && !save_as) { - path = it->second; - } else { + const base::FilePath* path = nullptr; + + if (!save_as) + base::FindOrNull(saved_files_, url); + + if (path == nullptr) { file_dialog::DialogSettings settings; settings.parent_window = owner_window(); settings.force_detached = offscreen_; settings.title = url; settings.default_path = base::FilePath::FromUTF8Unsafe(url); - if (!file_dialog::ShowSaveDialogSync(settings, &path)) { - inspectable_web_contents_->CallClientFunction( - "DevToolsAPI", "canceledSaveURL", base::Value(url)); - return; + if (auto new_path = file_dialog::ShowSaveDialogSync(settings)) { + auto [iter, _] = saved_files_.try_emplace(url, std::move(*new_path)); + path = &iter->second; } } - saved_files_[url] = path; + if (path == nullptr) { + inspectable_web_contents_->CallClientFunction( + "DevToolsAPI", "canceledSaveURL", base::Value{url}); + return; + } + // Notify DevTools. inspectable_web_contents_->CallClientFunction( - "DevToolsAPI", "savedURL", base::Value(url), - base::Value(path.AsUTF8Unsafe())); + "DevToolsAPI", "savedURL", base::Value{url}, + base::Value{path->AsUTF8Unsafe()}); file_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&WriteToFile, path, content, is_base64)); + FROM_HERE, base::BindOnce(&WriteToFile, *path, content, is_base64)); } void WebContents::DevToolsAppendToFile(const std::string& url, diff --git a/shell/browser/ui/file_dialog.h b/shell/browser/ui/file_dialog.h index b8858c06ecb3b..f7757da491c65 100644 --- a/shell/browser/ui/file_dialog.h +++ b/shell/browser/ui/file_dialog.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_ #define ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_ +#include #include #include #include @@ -72,7 +73,8 @@ bool ShowOpenDialogSync(const DialogSettings& settings, void ShowOpenDialog(const DialogSettings& settings, gin_helper::Promise promise); -bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path); +std::optional ShowSaveDialogSync( + const DialogSettings& settings); void ShowSaveDialog(const DialogSettings& settings, gin_helper::Promise promise); diff --git a/shell/browser/ui/file_dialog_linux.cc b/shell/browser/ui/file_dialog_linux.cc index 732820aa193a9..56251d9717629 100644 --- a/shell/browser/ui/file_dialog_linux.cc +++ b/shell/browser/ui/file_dialog_linux.cc @@ -233,20 +233,25 @@ void ShowOpenDialog(const DialogSettings& settings, dialog->RunOpenDialog(std::move(promise), settings); } -bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { - base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - auto cb = base::BindOnce( - [](base::RepeatingClosure cb, base::FilePath* file_path, - gin_helper::Dictionary result) { - result.Get("filePath", file_path); - std::move(cb).Run(); +std::optional ShowSaveDialogSync( + const DialogSettings& settings) { + std::optional path; + + base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed}; + auto on_chooser_dialog_done = base::BindOnce( + [](base::RepeatingClosure run_loop_closure, + std::optional* path, gin_helper::Dictionary result) { + if (base::FilePath val; result.Get("filePath", &val)) + *path = std::move(val); + std::move(run_loop_closure).Run(); }, - run_loop.QuitClosure(), path); + run_loop.QuitClosure(), &path); - FileChooserDialog* dialog = new FileChooserDialog(); - dialog->RunSaveDialog(std::move(cb), settings); + auto* const dialog = new FileChooserDialog{}; + dialog->RunSaveDialog(std::move(on_chooser_dialog_done), settings); run_loop.Run(); - return !path->empty(); + + return path; } void ShowSaveDialog(const DialogSettings& settings, diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index 936a907b39f3b..db361b848942b 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -436,19 +436,18 @@ void ShowOpenDialog(const DialogSettings& settings, } } -bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { - DCHECK(path); +std::optional ShowSaveDialogSync( + const DialogSettings& settings) { NSSavePanel* dialog = [NSSavePanel savePanel]; SetupDialog(dialog, settings); SetupSaveDialogForProperties(dialog, settings.properties); - int chosen = RunModalDialog(dialog, settings); + const int chosen = RunModalDialog(dialog, settings); if (chosen == NSModalResponseCancel || ![[dialog URL] isFileURL]) - return false; + return {}; - *path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path])); - return true; + return base::FilePath{base::SysNSStringToUTF8([[dialog URL] path])}; } void SaveDialogCompletion(int chosen, diff --git a/shell/browser/ui/file_dialog_win.cc b/shell/browser/ui/file_dialog_win.cc index e142477ad0820..f1d19a0b53310 100644 --- a/shell/browser/ui/file_dialog_win.cc +++ b/shell/browser/ui/file_dialog_win.cc @@ -233,11 +233,12 @@ void ShowOpenDialog(const DialogSettings& settings, base::BindOnce(done, std::move(promise))); } -bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { +std::optional ShowSaveDialogSync( + const DialogSettings& settings) { ATL::CComPtr file_save_dialog; HRESULT hr = file_save_dialog.CoCreateInstance(CLSID_FileSaveDialog); if (FAILED(hr)) - return false; + return {}; DWORD options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT; if (settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES) @@ -250,32 +251,31 @@ bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { hr = ShowFileDialog(file_save_dialog, settings); if (FAILED(hr)) - return false; + return {}; CComPtr pItem; hr = file_save_dialog->GetResult(&pItem); if (FAILED(hr)) - return false; + return {}; PWSTR result_path = nullptr; hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &result_path); if (!SUCCEEDED(hr)) - return false; + return {}; - *path = base::FilePath(result_path); + auto path = base::FilePath{result_path}; CoTaskMemFree(result_path); - - return true; + return path; } void ShowSaveDialog(const DialogSettings& settings, gin_helper::Promise promise) { auto done = [](gin_helper::Promise promise, - bool success, base::FilePath result) { + std::optional result) { v8::HandleScope handle_scope(promise.isolate()); auto dict = gin::Dictionary::CreateEmpty(promise.isolate()); - dict.Set("canceled", !success); - dict.Set("filePath", result); + dict.Set("canceled", !result.has_value()); + dict.Set("filePath", result.value_or(base::FilePath{})); promise.Resolve(dict); }; dialog_thread::Run(base::BindOnce(ShowSaveDialogSync, settings), From e98530ab7da0ad3b491cc6ac82817e679ba23044 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:55:01 -0400 Subject: [PATCH 277/339] refactor: move `gin::Converter` impl to a .cc file (#47467) refactor: move gin::Converter impl to a .cc file Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- filenames.gni | 1 + shell/browser/preload_script.cc | 82 +++++++++++++++++++++++++++++++++ shell/browser/preload_script.h | 63 +++---------------------- 3 files changed, 89 insertions(+), 57 deletions(-) create mode 100644 shell/browser/preload_script.cc diff --git a/filenames.gni b/filenames.gni index 50242a7e7a1c1..499e8dc95ce93 100644 --- a/filenames.gni +++ b/filenames.gni @@ -479,6 +479,7 @@ filenames = { "shell/browser/osr/osr_web_contents_view.h", "shell/browser/plugins/plugin_utils.cc", "shell/browser/plugins/plugin_utils.h", + "shell/browser/preload_script.cc", "shell/browser/preload_script.h", "shell/browser/protocol_registry.cc", "shell/browser/protocol_registry.h", diff --git a/shell/browser/preload_script.cc b/shell/browser/preload_script.cc new file mode 100644 index 0000000000000..0f60687eba06b --- /dev/null +++ b/shell/browser/preload_script.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2025 Salesforce, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/preload_script.h" + +#include "base/containers/fixed_flat_map.h" +#include "base/files/file_path.h" +#include "base/uuid.h" +#include "shell/common/gin_converters/file_path_converter.h" +#include "shell/common/gin_helper/dictionary.h" + +namespace gin { + +using electron::PreloadScript; + +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const PreloadScript::ScriptType& in) { + using Val = PreloadScript::ScriptType; + static constexpr auto Lookup = base::MakeFixedFlatMap({ + {Val::kWebFrame, "frame"}, + {Val::kServiceWorker, "service-worker"}, + }); + return StringToV8(isolate, Lookup.at(in)); +} + +// static +bool Converter::FromV8( + v8::Isolate* isolate, + v8::Local val, + PreloadScript::ScriptType* out) { + using Val = PreloadScript::ScriptType; + static constexpr auto Lookup = base::MakeFixedFlatMap({ + {"frame", Val::kWebFrame}, + {"service-worker", Val::kServiceWorker}, + }); + return FromV8WithLookup(isolate, val, Lookup, out); +} + +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const PreloadScript& script) { + gin::Dictionary dict(isolate, v8::Object::New(isolate)); + dict.Set("filePath", script.file_path.AsUTF8Unsafe()); + dict.Set("id", script.id); + dict.Set("type", script.script_type); + return ConvertToV8(isolate, dict).As(); +} + +// static +bool Converter::FromV8(v8::Isolate* isolate, + v8::Local val, + PreloadScript* out) { + gin_helper::Dictionary options; + if (!ConvertFromV8(isolate, val, &options)) + return false; + if (PreloadScript::ScriptType script_type; + options.Get("type", &script_type)) { + out->script_type = script_type; + } else { + return false; + } + if (base::FilePath file_path; options.Get("filePath", &file_path)) { + out->file_path = file_path; + } else { + return false; + } + if (std::string id; options.Get("id", &id)) { + out->id = id; + } else { + out->id = base::Uuid::GenerateRandomV4().AsLowercaseString(); + } + if (bool deprecated; options.Get("_deprecated", &deprecated)) { + out->deprecated = deprecated; + } + return true; +} + +} // namespace gin diff --git a/shell/browser/preload_script.h b/shell/browser/preload_script.h index bae243c7cafc2..62e945a5698c1 100644 --- a/shell/browser/preload_script.h +++ b/shell/browser/preload_script.h @@ -5,14 +5,11 @@ #ifndef ELECTRON_SHELL_BROWSER_PRELOAD_SCRIPT_H_ #define ELECTRON_SHELL_BROWSER_PRELOAD_SCRIPT_H_ -#include +#include -#include "base/containers/fixed_flat_map.h" #include "base/files/file_path.h" -#include "base/uuid.h" #include "gin/converter.h" -#include "shell/common/gin_converters/file_path_converter.h" -#include "shell/common/gin_helper/dictionary.h" +#include "v8/include/v8-forward.h" namespace electron { @@ -36,67 +33,19 @@ using electron::PreloadScript; template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, - const PreloadScript::ScriptType& in) { - using Val = PreloadScript::ScriptType; - static constexpr auto Lookup = - base::MakeFixedFlatMap({ - {Val::kWebFrame, "frame"}, - {Val::kServiceWorker, "service-worker"}, - }); - return StringToV8(isolate, Lookup.at(in)); - } - + const PreloadScript::ScriptType& in); static bool FromV8(v8::Isolate* isolate, v8::Local val, - PreloadScript::ScriptType* out) { - using Val = PreloadScript::ScriptType; - static constexpr auto Lookup = - base::MakeFixedFlatMap({ - {"frame", Val::kWebFrame}, - {"service-worker", Val::kServiceWorker}, - }); - return FromV8WithLookup(isolate, val, Lookup, out); - } + PreloadScript::ScriptType* out); }; template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, - const PreloadScript& script) { - gin::Dictionary dict(isolate, v8::Object::New(isolate)); - dict.Set("filePath", script.file_path.AsUTF8Unsafe()); - dict.Set("id", script.id); - dict.Set("type", script.script_type); - return ConvertToV8(isolate, dict).As(); - } - + const PreloadScript& script); static bool FromV8(v8::Isolate* isolate, v8::Local val, - PreloadScript* out) { - gin_helper::Dictionary options; - if (!ConvertFromV8(isolate, val, &options)) - return false; - if (PreloadScript::ScriptType script_type; - options.Get("type", &script_type)) { - out->script_type = script_type; - } else { - return false; - } - if (base::FilePath file_path; options.Get("filePath", &file_path)) { - out->file_path = file_path; - } else { - return false; - } - if (std::string id; options.Get("id", &id)) { - out->id = id; - } else { - out->id = base::Uuid::GenerateRandomV4().AsLowercaseString(); - } - if (bool deprecated; options.Get("_deprecated", &deprecated)) { - out->deprecated = deprecated; - } - return true; - } + PreloadScript* out); }; } // namespace gin From 8d2654c47e23237ef60a9837735111ffa1d76554 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:48:02 -0400 Subject: [PATCH 278/339] chore: bump chromium to 136.0.7103.168 (36-x-y) (#47441) * chore: bump chromium in DEPS to 136.0.7103.168 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- DEPS | 2 +- patches/v8/enable_--perf-prof_flag_on_macos.patch | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index afcb492647b79..0ca7aa07ac4cd 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.156', + '136.0.7103.168', 'node_version': 'v22.16.0', 'nan_version': diff --git a/patches/v8/enable_--perf-prof_flag_on_macos.patch b/patches/v8/enable_--perf-prof_flag_on_macos.patch index 34ee2009b623e..b1491332c9768 100644 --- a/patches/v8/enable_--perf-prof_flag_on_macos.patch +++ b/patches/v8/enable_--perf-prof_flag_on_macos.patch @@ -274,10 +274,10 @@ index 84f669ca3f98690264e1ee05bfe6842c47bbcbd0..e95c3bb6fe0f445904df9dff3e6fe357 #endif // V8_DIAGNOSTICS_PERF_JIT_H_ diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h -index dd713090c674585c23f9b9574df7ed258c0cd23e..8c12e97c4e256fb8bc6ef51c7abd6a926df7b008 100644 +index 3d10e605750752f54e6947de05ffd5c8d02cb492..d61124b328296668a1ee2e9d8ddb2c97b70eae48 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h -@@ -3262,7 +3262,7 @@ DEFINE_IMPLICATION(prof, log_code) +@@ -3264,7 +3264,7 @@ DEFINE_IMPLICATION(prof, log_code) DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.") @@ -286,7 +286,7 @@ index dd713090c674585c23f9b9574df7ed258c0cd23e..8c12e97c4e256fb8bc6ef51c7abd6a92 #define DEFINE_PERF_PROF_BOOL(nam, cmt) DEFINE_BOOL(nam, false, cmt) #define DEFINE_PERF_PROF_IMPLICATION DEFINE_IMPLICATION #else -@@ -3279,7 +3279,7 @@ DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.") +@@ -3281,7 +3281,7 @@ DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.") #endif DEFINE_PERF_PROF_BOOL(perf_basic_prof, @@ -295,7 +295,7 @@ index dd713090c674585c23f9b9574df7ed258c0cd23e..8c12e97c4e256fb8bc6ef51c7abd6a92 DEFINE_NEG_IMPLICATION(perf_basic_prof, compact_code_space) DEFINE_STRING(perf_basic_prof_path, DEFAULT_PERF_BASIC_PROF_PATH, "directory to write perf-.map symbol file to") -@@ -3288,8 +3288,8 @@ DEFINE_PERF_PROF_BOOL( +@@ -3290,8 +3290,8 @@ DEFINE_PERF_PROF_BOOL( "Only report function code ranges to perf (i.e. no stubs).") DEFINE_PERF_PROF_IMPLICATION(perf_basic_prof_only_functions, perf_basic_prof) From f9cba35a250ae38e2861c1cb353617ac996994d2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 20:46:59 +0200 Subject: [PATCH 279/339] feat: support `HIDDevice.collections` (#47484) * feat: support HIDDevice.collections Co-authored-by: Shelley Vohr * Update docs/api/structures/hid-device.md Co-authored-by: Erick Zhao Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/structures/hid-device.md | 8 + shell/browser/hid/hid_chooser_context.cc | 178 +++++++++++++++++++++++ 2 files changed, 186 insertions(+) diff --git a/docs/api/structures/hid-device.md b/docs/api/structures/hid-device.md index a6a097061dc22..7c8712cd8605b 100644 --- a/docs/api/structures/hid-device.md +++ b/docs/api/structures/hid-device.md @@ -6,3 +6,11 @@ * `productId` Integer - The USB product ID. * `serialNumber` string (optional) - The USB device serial number. * `guid` string (optional) - Unique identifier for the HID interface. A device may have multiple HID interfaces. +* `collections` Object[] - an array of report formats. See [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/HIDDevice/collections) for more. + * `usage` Integer - An integer representing the usage ID component of the HID usage associated with this collection. + * `usagePage` Integer - An integer representing the usage page component of the HID usage associated with this collection. + * `type` Integer - An 8-bit value representing the collection type, which describes a different relationship between the grouped items. + * `children` Object[] - An array of sub-collections which takes the same format as a top-level collection. + * `inputReports` Object[] - An array of inputReport items which represent individual input reports described in this collection. + * `outputReports` Object[] - An array of outputReport items which represent individual output reports described in this collection. + * `featureReports` Object[] - An array of featureReport items which represent individual feature reports described in this collection. diff --git a/shell/browser/hid/hid_chooser_context.cc b/shell/browser/hid/hid_chooser_context.cc index e1bec890070da..968a487fa1e06 100644 --- a/shell/browser/hid/hid_chooser_context.cc +++ b/shell/browser/hid/hid_chooser_context.cc @@ -36,6 +36,175 @@ #include "extensions/common/constants.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +namespace { + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +enum HidUnitSystem { + // none: No unit system + kUnitSystemNone = 0x00, + // si-linear: Centimeter, Gram, Seconds, Kelvin, Ampere, Candela + kUnitSystemSILinear = 0x01, + // si-rotation: Radians, Gram, Seconds, Kelvin, Ampere, Candela + kUnitSystemSIRotation = 0x02, + // english-linear: Inch, Slug, Seconds, Fahrenheit, Ampere, Candela + kUnitSystemEnglishLinear = 0x03, + // english-linear: Degrees, Slug, Seconds, Fahrenheit, Ampere, Candela + kUnitSystemEnglishRotation = 0x04, + // vendor-defined unit system + kUnitSystemVendorDefined = 0x0f, +}; + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +int ConvertHidUsageAndPageToInt(const device::mojom::HidUsageAndPage& usage) { + return static_cast((usage.usage_page) << 16 | usage.usage); +} + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +int8_t UnitFactorExponentToInt(uint8_t unit_factor_exponent) { + DCHECK_LE(unit_factor_exponent, 0x0f); + // Values from 0x08 to 0x0f encode negative exponents. + if (unit_factor_exponent > 0x08) + return static_cast(unit_factor_exponent) - 16; + return unit_factor_exponent; +} + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +std::string UnitSystemToString(uint8_t unit) { + DCHECK_LE(unit, 0x0f); + switch (unit) { + case kUnitSystemNone: + return "none"; + case kUnitSystemSILinear: + return "si-linear"; + case kUnitSystemSIRotation: + return "si-rotation"; + case kUnitSystemEnglishLinear: + return "english-linear"; + case kUnitSystemEnglishRotation: + return "english-rotation"; + case kUnitSystemVendorDefined: + return "vendor-defined"; + default: + break; + } + // Values other than those defined in HidUnitSystem are reserved by the spec. + return "reserved"; +} + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +base::Value::Dict HidReportItemToValue( + const device::mojom::HidReportItem& item) { + base::Value::Dict dict; + + dict.Set("hasNull", item.has_null_position); + dict.Set("hasPreferredState", !item.no_preferred_state); + dict.Set("isAbsolute", !item.is_relative); + dict.Set("isArray", !item.is_variable); + dict.Set("isBufferedBytes", item.is_buffered_bytes); + dict.Set("isConstant", item.is_constant); + dict.Set("isLinear", !item.is_non_linear); + dict.Set("isRange", item.is_range); + dict.Set("isVolatile", item.is_volatile); + dict.Set("logicalMinimum", item.logical_minimum); + dict.Set("logicalMaximum", item.logical_maximum); + dict.Set("physicalMinimum", item.physical_minimum); + dict.Set("physicalMaximum", item.physical_maximum); + dict.Set("reportCount", static_cast(item.report_count)); + dict.Set("reportSize", static_cast(item.report_size)); + + dict.Set("unitExponent", UnitFactorExponentToInt(item.unit_exponent & 0x0f)); + dict.Set("unitFactorCurrentExponent", + UnitFactorExponentToInt((item.unit >> 20) & 0x0f)); + dict.Set("unitFactorLengthExponent", + UnitFactorExponentToInt((item.unit >> 4) & 0x0f)); + dict.Set("unitFactorLuminousIntensityExponent", + UnitFactorExponentToInt((item.unit >> 24) & 0x0f)); + dict.Set("unitFactorMassExponent", + UnitFactorExponentToInt((item.unit >> 8) & 0x0f)); + dict.Set("unitFactorTemperatureExponent", + UnitFactorExponentToInt((item.unit >> 16) & 0x0f)); + dict.Set("unitFactorTimeExponent", + UnitFactorExponentToInt((item.unit >> 12) & 0x0f)); + dict.Set("unitSystem", UnitSystemToString(item.unit & 0x0f)); + + if (item.is_range) { + dict.Set("usageMinimum", ConvertHidUsageAndPageToInt(*item.usage_minimum)); + dict.Set("usageMaximum", ConvertHidUsageAndPageToInt(*item.usage_maximum)); + } else { + base::Value::List usages_list; + for (const auto& usage : item.usages) { + usages_list.Append(ConvertHidUsageAndPageToInt(*usage)); + } + dict.Set("usages", std::move(usages_list)); + } + + dict.Set("wrap", item.wrap); + + return dict; +} + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +base::Value::Dict HidReportDescriptionToValue( + const device::mojom::HidReportDescription& report) { + base::Value::Dict dict; + dict.Set("reportId", static_cast(report.report_id)); + + base::Value::List items_list; + for (const auto& item : report.items) { + items_list.Append(base::Value(HidReportItemToValue(*item))); + } + dict.Set("items", std::move(items_list)); + + return dict; +} + +// Adapted from third_party/blink/renderer/modules/hid/hid_device.cc. +base::Value::Dict HidCollectionInfoToValue( + const device::mojom::HidCollectionInfo& collection) { + base::Value::Dict dict; + + // Usage information + dict.Set("usage", collection.usage->usage); + dict.Set("usagePage", collection.usage->usage_page); + + // Collection type + dict.Set("collectionType", static_cast(collection.collection_type)); + + // Input reports + base::Value::List input_reports_list; + for (const auto& report : collection.input_reports) { + input_reports_list.Append( + base::Value(HidReportDescriptionToValue(*report))); + } + dict.Set("inputReports", std::move(input_reports_list)); + + // Output reports + base::Value::List output_reports_list; + for (const auto& report : collection.output_reports) { + output_reports_list.Append( + base::Value(HidReportDescriptionToValue(*report))); + } + dict.Set("outputReports", std::move(output_reports_list)); + + // Feature reports + base::Value::List feature_reports_list; + for (const auto& report : collection.feature_reports) { + feature_reports_list.Append( + base::Value(HidReportDescriptionToValue(*report))); + } + dict.Set("featureReports", std::move(feature_reports_list)); + + // Child collections (recursive) + base::Value::List children_list; + for (const auto& child : collection.children) { + children_list.Append(base::Value(HidCollectionInfoToValue(*child))); + } + dict.Set("children", std::move(children_list)); + + return dict; +} +} // namespace + namespace electron { HidChooserContext::HidChooserContext(ElectronBrowserContext* context) @@ -77,6 +246,7 @@ base::Value HidChooserContext::DeviceInfoToValue( base::UTF16ToUTF8(HidChooserContext::DisplayNameFromDeviceInfo(device))); value.Set(kDeviceVendorIdKey, device.vendor_id); value.Set(kDeviceProductIdKey, device.product_id); + if (HidChooserContext::CanStorePersistentEntry(device)) { // Use the USB serial number as a persistent identifier. If it is // unavailable, only ephemeral permissions may be granted. @@ -87,6 +257,14 @@ base::Value HidChooserContext::DeviceInfoToValue( // and must be granted again each time the device is connected. value.Set(kHidGuidKey, device.guid); } + + // Convert collections array + base::Value::List collections_list; + for (const auto& collection : device.collections) { + collections_list.Append(base::Value(HidCollectionInfoToValue(*collection))); + } + value.Set("collections", std::move(collections_list)); + return base::Value(std::move(value)); } From 20dc3dbc3af9bc96932b11b84bb70274944cb166 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:27:38 -0400 Subject: [PATCH 280/339] chore: bump chromium to 136.0.7103.177 (36-x-y) (#47500) chore: bump chromium in DEPS to 136.0.7103.177 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 0ca7aa07ac4cd..f168341b431bc 100644 --- a/DEPS +++ b/DEPS @@ -2,7 +2,7 @@ gclient_gn_args_from = 'src' vars = { 'chromium_version': - '136.0.7103.168', + '136.0.7103.177', 'node_version': 'v22.16.0', 'nan_version': From abda6bfda59929a80c200447298f87122a3aee4c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:38:05 +0200 Subject: [PATCH 281/339] build: update cache action to latest (#47517) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/checkout/action.yml | 2 +- .github/actions/install-dependencies/action.yml | 2 +- .github/actions/restore-cache-azcopy/action.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 49a08eead649d..9edf46b877fa1 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -43,7 +43,7 @@ runs: curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}" > sas-token - name: Save SAS Key if: ${{ inputs.generate-sas-token == 'true' }} - uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: sas-token key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 25f288c2a7fa1..ff0f5581472db 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -7,7 +7,7 @@ runs: shell: bash id: yarn-cache-dir-path run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT - - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 + - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index 4c34ba496340b..22e82e0f97843 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -8,14 +8,14 @@ runs: steps: - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: sas-token key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1 enableCrossOsArchive: true - name: Obtain SAS Key continue-on-error: true - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: sas-token key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }} @@ -32,7 +32,7 @@ runs: shell: bash command: | sas_token=$(cat sas-token) - if [ -z $sas-token ]; then + if [ -z "$sas_token" ]; then echo "SAS Token not found; exiting src cache download early..." exit 1 else From 2d9cb53a86e3aa5ce484001d2d13a93322204121 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 21:56:52 +0200 Subject: [PATCH 282/339] refactor: simplify titlebar overlay initialization (#47523) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 8f87141bf9940..d1d5e8b7689f2 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -105,20 +105,12 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options, options.Get(options::kVibrancyType, &vibrancy_); #endif - v8::Local titlebar_overlay; - if (options.Get(options::ktitleBarOverlay, &titlebar_overlay)) { - if (titlebar_overlay->IsBoolean()) { - options.Get(options::ktitleBarOverlay, &titlebar_overlay_); - } else if (titlebar_overlay->IsObject()) { - titlebar_overlay_ = true; - - auto titlebar_overlay_dict = - gin_helper::Dictionary::CreateEmpty(options.isolate()); - options.Get(options::ktitleBarOverlay, &titlebar_overlay_dict); - int height; - if (titlebar_overlay_dict.Get(options::kOverlayHeight, &height)) - titlebar_overlay_height_ = height; - } + if (gin_helper::Dictionary dict; + options.Get(options::ktitleBarOverlay, &dict)) { + titlebar_overlay_ = true; + titlebar_overlay_height_ = dict.ValueOrDefault(options::kOverlayHeight, 0); + } else if (bool flag; options.Get(options::ktitleBarOverlay, &flag)) { + titlebar_overlay_ = flag; } if (parent) From c15c6d91187cceba3096048c6d2e72ea361b88c3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:40:15 +0200 Subject: [PATCH 283/339] build: rewrite push-patch to use the github API instead of local git commits to ensure commits are signed (#47531) * build: rewrite push-patch to use the github API instead of local git commits to ensure commits are signed Co-authored-by: Samuel Attard * again (cherry picked from commit a21afc3e45d15f88c1f754d5990908f248909b41) Co-authored-by: Samuel Attard * use pr head ref (cherry picked from commit 0edcc985fadcce64f01fb77b1c15653d5e66e864) Co-authored-by: Samuel Attard --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard Co-authored-by: Samuel Attard --- .github/actions/checkout/action.yml | 12 +-- script/patch-up.js | 128 ++++++++++++++++++++++++++++ script/push-patch.js | 38 --------- 3 files changed, 134 insertions(+), 44 deletions(-) create mode 100644 script/patch-up.js delete mode 100644 script/push-patch.js diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 9edf46b877fa1..98fc18bb566b5 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -117,12 +117,7 @@ runs: git update-index --refresh || true if ! git diff-index --quiet HEAD --; then # There are changes to the patches. Make a git commit with the updated patches - git add patches - GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>" - # Export it - mkdir -p ../../patches - git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch - if node ./script/push-patch.js; then + if node ./script/patch-up.js; then echo echo "======================================================================" echo "Changes to the patches when applying, we have auto-pushed the diff to the current branch" @@ -130,6 +125,11 @@ runs: echo "======================================================================" exit 1 else + git add patches + GIT_COMMITTER_NAME="PatchUp" GIT_COMMITTER_EMAIL="73610968+patchup[bot]@users.noreply.github.com" git commit -m "chore: update patches" --author="PatchUp <73610968+patchup[bot]@users.noreply.github.com>" + # Export it + mkdir -p ../../patches + git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch echo echo "======================================================================" echo "There were changes to the patches when applying." diff --git a/script/patch-up.js b/script/patch-up.js new file mode 100644 index 0000000000000..0b6d91a824c12 --- /dev/null +++ b/script/patch-up.js @@ -0,0 +1,128 @@ +const { appCredentialsFromString, getAuthOptionsForRepo } = require('@electron/github-app-auth'); + +const { Octokit } = require('@octokit/rest'); + +const fs = require('node:fs'); +const path = require('node:path'); + +const { PATCH_UP_APP_CREDS } = process.env; + +const REPO_OWNER = 'electron'; +const REPO_NAME = 'electron'; + +async function getAllPatchFiles (dir) { + const files = []; + + async function walkDir (currentDir) { + const entries = await fs.promises.readdir(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(currentDir, entry.name); + + if (entry.isDirectory()) { + await walkDir(fullPath); + } else if (entry.isFile() && (entry.name.endsWith('.patch') || entry.name === 'README.md' || entry.name === 'config.json')) { + const relativePath = path.relative(process.cwd(), fullPath); + const content = await fs.promises.readFile(fullPath, 'utf8'); + files.push({ + path: relativePath, + content + }); + } + } + } + + await walkDir(dir); + return files; +} + +function getCurrentCommitSha () { + return process.env.GITHUB_SHA; +} + +function getCurrentBranch () { + return process.env.GITHUB_HEAD_REF; +} + +async function main () { + if (!PATCH_UP_APP_CREDS) { + throw new Error('PATCH_UP_APP_CREDS environment variable not set'); + } + + const currentBranch = getCurrentBranch(); + + if (!currentBranch) { + throw new Error('GITHUB_HEAD_REF environment variable not set. Patch Up only works in PR workflows currently.'); + } + + const octokit = new Octokit({ + ...await getAuthOptionsForRepo({ + name: REPO_OWNER, + owner: REPO_NAME + }, appCredentialsFromString(PATCH_UP_APP_CREDS)) + }); + + const patchesDir = path.join(process.cwd(), 'patches'); + + // Get current git state + const currentCommitSha = getCurrentCommitSha(); + + // Get the tree SHA from the current commit + const currentCommit = await octokit.git.getCommit({ + owner: REPO_OWNER, + repo: REPO_NAME, + commit_sha: currentCommitSha + }); + const baseTreeSha = currentCommit.data.tree.sha; + + // Find all patch files + const patchFiles = await getAllPatchFiles(patchesDir); + + if (patchFiles.length === 0) { + throw new Error('No patch files found'); + } + + console.log(`Found ${patchFiles.length} patch files`); + + // Create a new tree with the patch files + const tree = patchFiles.map(file => ({ + path: file.path, + mode: '100644', + type: 'blob', + content: file.content + })); + + const treeResponse = await octokit.git.createTree({ + owner: REPO_OWNER, + repo: REPO_NAME, + base_tree: baseTreeSha, + tree + }); + + // Create a new commit + const commitMessage = 'chore: update patches'; + const commitResponse = await octokit.git.createCommit({ + owner: REPO_OWNER, + repo: REPO_NAME, + message: commitMessage, + tree: treeResponse.data.sha, + parents: [currentCommitSha] + }); + + // Update the branch reference + await octokit.git.updateRef({ + owner: REPO_OWNER, + repo: REPO_NAME, + ref: `heads/${currentBranch}`, + sha: commitResponse.data.sha + }); + + console.log(`Successfully pushed commit ${commitResponse.data.sha} to ${currentBranch}`); +} + +if (require.main === module) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/script/push-patch.js b/script/push-patch.js deleted file mode 100644 index 7265a10f59618..0000000000000 --- a/script/push-patch.js +++ /dev/null @@ -1,38 +0,0 @@ -const { appCredentialsFromString, getTokenForRepo } = require('@electron/github-app-auth'); - -const cp = require('node:child_process'); - -const { PATCH_UP_APP_CREDS } = process.env; - -async function main () { - if (!PATCH_UP_APP_CREDS) { - throw new Error('PATCH_UP_APP_CREDS environment variable not set'); - } - - const token = await getTokenForRepo( - { - name: 'electron', - owner: 'electron' - }, - appCredentialsFromString(PATCH_UP_APP_CREDS) - ); - - const remoteURL = `https://x-access-token:${token}@github.com/electron/electron.git`; - - // NEVER LOG THE OUTPUT OF THIS COMMAND - // GIT LEAKS THE ACCESS CREDENTIALS IN CONSOLE LOGS - const { status } = cp.spawnSync('git', ['push', '--set-upstream', remoteURL], { - stdio: 'ignore' - }); - - if (status !== 0) { - throw new Error('Failed to push to target branch'); - } -} - -if (require.main === module) { - main().catch((err) => { - console.error(err); - process.exit(1); - }); -} From cd42e9ba057029340c5699d86014dc11ad9d09cb Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:42:46 +0200 Subject: [PATCH 284/339] feat: add support for --no-experimental-global-navigator (#47417) chore: add support for --no-experimental-global-navigator Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: deepak1556 --- docs/api/command-line-switches.md | 5 ++++ shell/common/node_bindings.cc | 1 + spec/api-utility-process-spec.ts | 28 +++++++++++++++++++ .../fixtures/api/utility-process/navigator.js | 1 + 4 files changed, 35 insertions(+) create mode 100644 spec/fixtures/api/utility-process/navigator.js diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index 8026d9351d375..e5ab9dcbe94d7 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -325,6 +325,10 @@ Set the directory to which all Node.js diagnostic output files are written. Defa Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https://nodejs.org/docs/latest/api/v8.html#v8setheapsnapshotnearheaplimitlimit). +### `--no-experimental-global-navigator` + +Disable exposition of [Navigator API][] on the global scope from Node.js. + [app]: app.md [append-switch]: command-line.md#commandlineappendswitchswitch-value [debugging-main-process]: ../tutorial/debugging-main-process.md @@ -333,3 +337,4 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https: [play-silent-audio]: https://github.com/atom/atom/pull/9485/files [ready]: app.md#event-ready [severities]: https://source.chromium.org/chromium/chromium/src/+/main:base/logging.h?q=logging::LogSeverity&ss=chromium +[Navigator API]: https://github.com/nodejs/node/blob/main/doc/api/globals.md#navigator diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 10d8287fc1ea6..11b34075e7535 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -359,6 +359,7 @@ bool IsAllowedOption(const std::string_view option) { "--throw-deprecation", "--trace-deprecation", "--trace-warnings", + "--no-experimental-global-navigator", }); if (debug_options.contains(option)) diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts index aa75aedda8671..e365a8fd78fd9 100644 --- a/spec/api-utility-process-spec.ts +++ b/spec/api-utility-process-spec.ts @@ -780,5 +780,33 @@ describe('utilityProcess module', () => { expect(stat.size).to.be.greaterThan(0); await fs.rm(tmpDir, { recursive: true }); }); + + it('supports --no-experimental-global-navigator flag', async () => { + { + const child = utilityProcess.fork(path.join(fixturesPath, 'navigator.js'), [], { + stdio: 'ignore' + }); + await once(child, 'spawn'); + const [data] = await once(child, 'message'); + expect(data).to.be.true(); + const exit = once(child, 'exit'); + expect(child.kill()).to.be.true(); + await exit; + } + { + const child = utilityProcess.fork(path.join(fixturesPath, 'navigator.js'), [], { + stdio: 'ignore', + execArgv: [ + '--no-experimental-global-navigator' + ] + }); + await once(child, 'spawn'); + const [data] = await once(child, 'message'); + expect(data).to.be.false(); + const exit = once(child, 'exit'); + expect(child.kill()).to.be.true(); + await exit; + } + }); }); }); diff --git a/spec/fixtures/api/utility-process/navigator.js b/spec/fixtures/api/utility-process/navigator.js new file mode 100644 index 0000000000000..c7bfafb07c54c --- /dev/null +++ b/spec/fixtures/api/utility-process/navigator.js @@ -0,0 +1 @@ +process.parentPort.postMessage(typeof navigator === 'object'); From d4a93c896d1df4ad1f43a909729b241471de939b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:22:56 -0400 Subject: [PATCH 285/339] fix: ensure `/dev/null` fd is closed on failure (#47541) * fix: ensure /dev/null fd is closed on failure Co-authored-by: Shelley Vohr * chore: ignore closehandle for windows Co-authored-by: Robo --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr Co-authored-by: Robo --- shell/browser/api/electron_api_utility_process.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/browser/api/electron_api_utility_process.cc b/shell/browser/api/electron_api_utility_process.cc index d9ff0996d91d8..b2eb975dacef5 100644 --- a/shell/browser/api/electron_api_utility_process.cc +++ b/shell/browser/api/electron_api_utility_process.cc @@ -78,6 +78,9 @@ UtilityProcessWrapper::UtilityProcessWrapper( base::FileHandleMappingVector fds_to_remap; #endif for (const auto& [io_handle, io_type] : stdio) { + if (io_handle == IOHandle::STDIN) + continue; + if (io_type == IOType::IO_PIPE) { #if BUILDFLAG(IS_WIN) HANDLE read = nullptr; From c2a3d240c3150a5eda0c60d6f28a9d3d190b7c82 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:34:36 -0400 Subject: [PATCH 286/339] docs: Add C++/Linux tutorial (#47549) * docs: Add C++/Linux tutorial Co-authored-by: Felix Rieseberg * Update docs/tutorial/native-code-and-electron-cpp-linux.md Co-authored-by: Kilian Valkhof Co-authored-by: Felix Rieseberg * Apply suggestions from code review Co-authored-by: Kilian Valkhof Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Apply suggestions from code review Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Apply suggestions from code review Co-authored-by: Erick Zhao Co-authored-by: Felix Rieseberg * Implement more feedback, lint Co-authored-by: Felix Rieseberg --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Felix Rieseberg Co-authored-by: Felix Rieseberg --- .../native-code-and-electron-cpp-linux.md | 1638 +++++++++++++++++ 1 file changed, 1638 insertions(+) create mode 100644 docs/tutorial/native-code-and-electron-cpp-linux.md diff --git a/docs/tutorial/native-code-and-electron-cpp-linux.md b/docs/tutorial/native-code-and-electron-cpp-linux.md new file mode 100644 index 0000000000000..b40fa8f6b4afc --- /dev/null +++ b/docs/tutorial/native-code-and-electron-cpp-linux.md @@ -0,0 +1,1638 @@ +# Native Code and Electron: C++ (Linux) + +This tutorial builds on the [general introduction to Native Code and Electron](./native-code-and-electron.md) and focuses on creating a native addon for Linux using C++ and GTK3. To illustrate how you can embed native Linux code in your Electron app, we'll be building a basic native GTK3 GUI that communicates with Electron's JavaScript. + +Specifically, we'll be using GTK3 for our GUI interface, which provides: + +* A comprehensive set of UI widgets like buttons, entry fields, and lists +* Cross-desktop compatibility across various Linux distributions +* Integration with the native theming and accessibility features of Linux desktops + +> [!NOTE] +> We specifically use GTK3 because that's what Chromium (and by extension, Electron) uses internally. Using GTK4 would cause runtime conflicts since both GTK3 and GTK4 would be loaded in the same process. If and when Chromium upgrades to GTK4, you will likely be able to easily upgrade your native code to GTK4, too. + +This tutorial will be most useful to those who already have some familiarity with GTK development on Linux. You should have experience with basic GTK concepts like widgets, signals, and the main event loop. In the interest of brevity, we're not spending too much time explaining the individual GTK elements we're using or the code we're writing for them. This allows this tutorial to be really helpful for those who already know GTK development and want to use their skills with Electron - without having to also be an entire GTK documentation. + +> [!NOTE] +> If you're not already familiar with these concepts, the [GTK3 documentation](https://docs.gtk.org/gtk3/) and [GTK3 tutorials](https://docs.gtk.org/gtk3/getting_started.html) are excellent resources to get started. The [GNOME Developer Documentation](https://developer.gnome.org/) also provides comprehensive guides for GTK development. + +## Requirements + +Just like our general introduction to Native Code and Electron, this tutorial assumes you have Node.js and npm installed, as well as the basic tools necessary for compiling native code. Since this tutorial discusses writing native code that interacts with GTK3, you'll need: + +* A Linux distribution with GTK3 development files installed +* The [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) tool +* G++ compiler and build tools + +On Ubuntu/Debian, you can install these with: + +```sh +sudo apt-get install build-essential pkg-config libgtk-3-dev +``` + +On Fedora/RHEL/CentOS: + +```sh +sudo dnf install gcc-c++ pkgconfig gtk3-devel +``` + +## 1) Creating a package + +You can re-use the package we created in our [Native Code and Electron](./native-code-and-electron.md) tutorial. This tutorial will not be repeating the steps described there. Let's first setup our basic addon folder structure: + +```txt +cpp-linux/ +├── binding.gyp # Configuration file for node-gyp to build the native addon +├── include/ +│ └── cpp_code.h # Header file with declarations for our C++ native code +├── js/ +│ └── index.js # JavaScript interface that loads and exposes our native addon +├── package.json # Node.js package configuration and dependencies +└── src/ + ├── cpp_addon.cc # C++ code that bridges Node.js/Electron with our native code + └── cpp_code.cc # Implementation of our native C++ functionality using GTK3 +``` + +Our package.json should look like this: + +```json title='package.json' +{ + "name": "cpp-linux", + "version": "1.0.0", + "description": "A demo module that exposes C++ code to Electron", + "main": "js/index.js", + "scripts": { + "clean": "rm -rf build", + "build-electron": "electron-rebuild", + "build": "node-gyp configure && node-gyp build" + }, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "bindings": "^1.5.0" + } +} +``` + +## 2) Setting up the build configuration + +For a Linux-specific addon using GTK3, we need to configure our `binding.gyp` file correctly to ensure our addon is only compiled on Linux systems - doing ideally nothing on other platforms. This involves using conditional compilation flags, leveraging `pkg-config` to automatically locate and include the GTK3 libraries and header paths on the user's system, and setting appropriate compiler flags to enable features like exception handling and threading support. The configuration will ensure that our native code can properly interface with both the Node.js/Electron runtime and the GTK3 libraries that provide the native GUI capabilities. + +```json title='binding.gyp' +{ + "targets": [ + { + "target_name": "cpp_addon", + "conditions": [ + ['OS=="linux"', { + "sources": [ + "src/cpp_addon.cc", + "src/cpp_code.cc" + ], + "include_dirs": [ + " +#include + +namespace cpp_code { + +std::string hello_world(const std::string& input); +void hello_gui(); + +// Callback function types +using TodoCallback = std::function; + +// Callback setters +void setTodoAddedCallback(TodoCallback callback); +void setTodoUpdatedCallback(TodoCallback callback); +void setTodoDeletedCallback(TodoCallback callback); + +} // namespace cpp_code +``` + +This header defines: + +* A basic `hello_world` function +* A `hello_gui` function to create a GTK3 GUI +* Callback types for Todo operations (add, update, delete) +* Setter functions for the callback + +## 4) Implementing GTK3 GUI Code + +Now, let's implement our GTK3 GUI in `src/cpp_code.cc`. We'll break this into manageable sections. We'll start with a number of includes as well as the basic setup. + +### Basic Setup and Data Structures + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using TodoCallback = std::function; + +namespace cpp_code +{ + // Basic functions + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + // Data structures + struct TodoItem + { + uuid_t id; + std::string text; + int64_t date; + + std::string toJson() const + { + char uuid_str[37]; + uuid_unparse(id, uuid_str); + return "{" + "\"id\":\"" + + std::string(uuid_str) + "\"," + "\"text\":\"" + + text + "\"," + "\"date\":" + + std::to_string(date) + + "}"; + } + + static std::string formatDate(int64_t timestamp) + { + char date_str[64]; + time_t unix_time = timestamp / 1000; + strftime(date_str, sizeof(date_str), "%Y-%m-%d", localtime(&unix_time)); + return date_str; + } + }; +``` + +In this section: + +* We include necessary headers for GTK3, standard library components, and UUID generation. +* Define a `TodoCallback` type to handle communication back to JavaScript. +* Create a `TodoItem` struct to store our todo data with: + * A UUID for unique identification + * Text content and a timestamp + * A method to convert to JSON for sending to JavaScript + * A static helper to format dates for display + +The `toJson()` method is particularly important as it's what allows our C++ objects to be serialized for transmission to JavaScript. There are probably better ways to do that, but this tutorial is about combining C++ for native Linux UI development with Electron, so we'll give ourselves a pass for not writing better JSON serialization code here. There are many libraries to work with JSON in C++ with different trade-offs. See https://www.json.org/json-en.html for a list. + +Notably, we haven't actually added any user interface yet - which we'll do in the next step. GTK code tends to be verbose, so bear with us - despite the length. + +### Global state and forward declarations + +Below the code already in your `src/cpp_code.cc`, add the following: + +```cpp title='src/cpp_code.cc' + // Forward declarations + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo); + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo); + + // Global state + namespace + { + TodoCallback g_todoAddedCallback; + TodoCallback g_todoUpdatedCallback; + TodoCallback g_todoDeletedCallback; + GMainContext *g_gtk_main_context = nullptr; + GMainLoop *g_main_loop = nullptr; + std::thread *g_gtk_thread = nullptr; + std::vector g_todos; + } +``` + +Here we: + +* Forward-declare helper functions we'll use later +* Set up global state in an anonymous namespace, including: + * Callbacks for the `add`, `update`, and `delete` todo operations + * GTK main context and loop pointers for thread management + * A pointer to the GTK thread itself + * A vector to store our todos + +These global variables keep track of application state and allow different parts of our code to interact with each other. The thread management variables (`g_gtk_main_context`, `g_main_loop`, and `g_gtk_thread`) are particularly important because GTK requires running in its own event loop. Since our code will be called from Node.js/Electron's main thread, we need to run GTK in a separate thread to avoid blocking the JavaScript event loop. This separation ensures that our native UI remains responsive while still allowing bidirectional communication with the Electron application. The callbacks enable us to send events back to JavaScript when the user interacts with our native GTK interface. + +### Helper Functions + +Moving on, we're adding more code below the code we've already written. In this section, we're adding three static helper methods - and also start setting up some actual native user interface. We'll add a helper function that'll notify a callback in a thread-safe way, a function to update a row label, and a function to create the whole "Add Todo" dialog. + +```cpp title='src/cpp_code.cc' + // Helper functions + static void notify_callback(const TodoCallback &callback, const std::string &json) + { + if (callback && g_gtk_main_context) + { + g_main_context_invoke(g_gtk_main_context, [](gpointer data) -> gboolean + { + auto* cb_data = static_cast*>(data); + cb_data->first(cb_data->second); + delete cb_data; + return G_SOURCE_REMOVE; }, new std::pair(callback, json)); + } + } + + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo) + { + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + auto *old_label = GTK_WIDGET(gtk_container_get_children(GTK_CONTAINER(row))->data); + gtk_container_remove(GTK_CONTAINER(row), old_label); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_widget_show_all(GTK_WIDGET(row)); + } + + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo = nullptr) + { + auto *dialog = gtk_dialog_new_with_buttons( + existing_todo ? "Edit Todo" : "Add Todo", + parent, + GTK_DIALOG_MODAL, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + nullptr); + + auto *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_set_border_width(GTK_CONTAINER(content_area), 10); + + auto *entry = gtk_entry_new(); + if (existing_todo) + { + gtk_entry_set_text(GTK_ENTRY(entry), existing_todo->text.c_str()); + } + gtk_container_add(GTK_CONTAINER(content_area), entry); + + auto *calendar = gtk_calendar_new(); + if (existing_todo) + { + time_t unix_time = existing_todo->date / 1000; + struct tm *timeinfo = localtime(&unix_time); + gtk_calendar_select_month(GTK_CALENDAR(calendar), timeinfo->tm_mon, timeinfo->tm_year + 1900); + gtk_calendar_select_day(GTK_CALENDAR(calendar), timeinfo->tm_mday); + } + gtk_container_add(GTK_CONTAINER(content_area), calendar); + + gtk_widget_show_all(dialog); + return dialog; + } +``` + +These helper functions are crucial for our application: + +* `notify_callback`: Safely invokes JavaScript callbacks from the GTK thread using `g_main_context_invoke`, which schedules function execution in the GTK main context. As a reminder, the GTK main context is the environment where GTK operations must be performed to ensure thread safety, as GTK is not thread-safe and all UI operations must happen on the main thread. +* `update_todo_row_label`: Updates a row in the todo list with new text and formatted date. +* `create_todo_dialog`: Creates a dialog for adding or editing todos with: + * A text entry field for the todo text + * A calendar widget for selecting the date + * Appropriate buttons for saving or canceling + +### Event handlers + +Our native user interface has events - and those events must be handled. The only Electron-specific thing in this code is that we're notifying our JS callbacks. + +```cpp title='src/cpp_code.cc' + static void edit_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + auto *dialog = create_todo_dialog( + GTK_WINDOW(gtk_builder_get_object(builder, "window")), + &g_todos[index]); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + auto *entry = GTK_ENTRY(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->data); + auto *calendar = GTK_CALENDAR(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->next->data); + + const char *new_text = gtk_entry_get_text(entry); + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + gint64 new_date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos[index].text = new_text; + g_todos[index].date = new_date; + + update_todo_row_label(row, g_todos[index]); + notify_callback(g_todoUpdatedCallback, g_todos[index].toJson()); + } + + gtk_widget_destroy(dialog); + } + + static void delete_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + std::string json = g_todos[index].toJson(); + gtk_container_remove(GTK_CONTAINER(list), GTK_WIDGET(row)); + g_todos.erase(g_todos.begin() + index); + notify_callback(g_todoDeletedCallback, json); + } + + static void on_add_clicked(GtkButton *button, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *entry = GTK_ENTRY(gtk_builder_get_object(builder, "todo_entry")); + auto *calendar = GTK_CALENDAR(gtk_builder_get_object(builder, "todo_calendar")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + const char *text = gtk_entry_get_text(entry); + if (strlen(text) > 0) + { + TodoItem todo; + uuid_generate(todo.id); + todo.text = text; + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + todo.date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos.push_back(todo); + + auto *row = gtk_list_box_row_new(); + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_container_add(GTK_CONTAINER(list), row); + gtk_widget_show_all(row); + + gtk_entry_set_text(entry, ""); + + notify_callback(g_todoAddedCallback, todo.toJson()); + } + } + + static void on_row_activated(GtkListBox *list_box, GtkListBoxRow *row, gpointer user_data) + { + GMenu *menu = g_menu_new(); + g_menu_append(menu, "Edit", "app.edit"); + g_menu_append(menu, "Delete", "app.delete"); + + auto *popover = gtk_popover_new_from_model(GTK_WIDGET(row), G_MENU_MODEL(menu)); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_RIGHT); + gtk_popover_popup(GTK_POPOVER(popover)); + + g_object_unref(menu); + } +``` + +These event handlers manage user interactions: + +`edit_action`: Handles editing a todo by: + +* Getting the selected row +* Creating a dialog with the current todo data +* Updating the todo if the user confirms +* Notifying JavaScript via callback + +`delete_action`: Removes a todo and notifies JavaScript. + +`on_add_clicked`: Adds a new todo when the user clicks the Add button: + +* Gets text and date from input fields +* Creates a new TodoItem with a unique ID +* Adds it to the list and the underlying data store +* Notifies JavaScript + +`on_row_activated`: Shows a popup menu when a todo is clicked, with options to edit or delete. + +### GTK application setup + +Now, we'll need to setup our GTK application. This might be counter-intuitive, given that we already have a GTK application running. The activation code here is necessary because this is native C++ code running alongside Electron, not within it. While Electron does have its own main process and renderer processes, this GTK application operates as a native OS window that's launched from the Electron application but runs in its own process or thread. The `hello_gui()` function specifically starts the GTK application with its own thread (`g_gtk_thread`), application loop, and UI context. + +```cpp title='src/cpp_code.cc' + static gboolean init_gtk_app(gpointer user_data) + { + auto *app = static_cast(user_data); + g_application_run(G_APPLICATION(app), 0, nullptr); + g_object_unref(app); + if (g_main_loop) + { + g_main_loop_quit(g_main_loop); + } + return G_SOURCE_REMOVE; + } + + static void activate_handler(GtkApplication *app, gpointer user_data) + { + auto *builder = gtk_builder_new(); + + const GActionEntry app_actions[] = { + {"edit", edit_action, nullptr, nullptr, nullptr, {0, 0, 0}}, + {"delete", delete_action, nullptr, nullptr, nullptr, {0, 0, 0}}}; + g_action_map_add_action_entries(G_ACTION_MAP(app), app_actions, + G_N_ELEMENTS(app_actions), builder); + + gtk_builder_add_from_string(builder, + "" + "" + " " + " Todo List" + " 400" + " 500" + " " + " " + " true" + " vertical" + " 6" + " 12" + " " + " " + " true" + " 6" + " " + " " + " true" + " true" + " Enter todo item..." + " " + " " + " " + " " + " true" + " " + " " + " " + " " + " true" + " Add" + " " + " " + " " + " " + " " + " " + " true" + " true" + " " + " " + " true" + " single" + " " + " " + " " + " " + " " + " " + " " + "", + -1, nullptr); + + auto *window = GTK_WINDOW(gtk_builder_get_object(builder, "window")); + auto *button = GTK_BUTTON(gtk_builder_get_object(builder, "add_button")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + gtk_window_set_application(window, app); + + g_signal_connect(button, "clicked", G_CALLBACK(on_add_clicked), builder); + g_signal_connect(list, "row-activated", G_CALLBACK(on_row_activated), nullptr); + + gtk_widget_show_all(GTK_WIDGET(window)); + } +``` + +Let's take a closer look at the code above: + +* `init_gtk_app`: Runs the GTK application main loop. +* `activate_handler`: Sets up the application UI when activated: + * Creates a GtkBuilder for loading the UI + * Registers edit and delete actions + * Defines the UI layout using GTK's XML markup language + * Connects signals to our event handlers + +The UI layout is defined inline using XML, which is a common pattern in GTK applications. It creates a main window, input controls (text entry, calendar, and add button), a list box for displaying todos, and proper layout containers and scrolling. + +### Main GUI function and thread management + +Now that we have everything wired, up, we can add our two core GUI functions: `hello_gui()` (which we'll call from JavaScript) and `cleanup_gui()` to get rid of everything. You'll be hopefully delighted to hear that our careful setup of GTK app, context, and threads makes this straightforward: + +```cpp title='src/cpp_code.cc' + void hello_gui() + { + if (g_gtk_thread != nullptr) + { + g_print("GTK application is already running.\n"); + return; + } + + if (!gtk_init_check(0, nullptr)) + { + g_print("Failed to initialize GTK.\n"); + return; + } + + g_gtk_main_context = g_main_context_new(); + g_main_loop = g_main_loop_new(g_gtk_main_context, FALSE); + + g_gtk_thread = new std::thread([]() + { + GtkApplication* app = gtk_application_new("com.example.todo", G_APPLICATION_NON_UNIQUE); + g_signal_connect(app, "activate", G_CALLBACK(activate_handler), nullptr); + + g_idle_add_full(G_PRIORITY_DEFAULT, init_gtk_app, app, nullptr); + + if (g_main_loop) { + g_main_loop_run(g_main_loop); + } }); + + g_gtk_thread->detach(); + } + + void cleanup_gui() + { + if (g_main_loop && g_main_loop_is_running(g_main_loop)) + { + g_main_loop_quit(g_main_loop); + } + + if (g_main_loop) + { + g_main_loop_unref(g_main_loop); + g_main_loop = nullptr; + } + + if (g_gtk_main_context) + { + g_main_context_unref(g_gtk_main_context); + g_gtk_main_context = nullptr; + } + + g_gtk_thread = nullptr; + } +``` + +These functions manage the GTK application lifecycle: + +* `hello_gui`: The entry point exposed to JavaScript that checks if GTK is already running, initializes GTK, creates a new main context and loop, launches a thread to run the GTK application, and detaches the thread so it runs independently. +* `cleanup_gui`: Properly cleans up GTK resources when the application closes. + +Running GTK in a separate thread is crucial for Electron integration, as it prevents the GTK main loop from blocking Node.js's event loop. + +### Callback management + +Previously, we setup global variables to hold our callbacks. Now, we'll add functions that assign those callbacks. These callbacks form the bridge between our native GTK code and JavaScript, allowing bidirectional communication. + +```cpp title='src/cpp_code.cc' + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } +``` + +### Putting `cpp_code.cc` together + +We've now finished the GTK and native part of our addon - that is, the code that's most concerned with interacting with the operating system (and by contrast, less so with bridging the native C++ and JavaScript worlds). After adding all the sections above, your `src/cpp_code.cc` should look like this: + +```cpp title='src/cpp_code.cc' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using TodoCallback = std::function; + +namespace cpp_code +{ + + // Basic functions + std::string hello_world(const std::string &input) + { + return "Hello from C++! You said: " + input; + } + + // Data structures + struct TodoItem + { + uuid_t id; + std::string text; + int64_t date; + + std::string toJson() const + { + char uuid_str[37]; + uuid_unparse(id, uuid_str); + return "{" + "\"id\":\"" + + std::string(uuid_str) + "\"," + "\"text\":\"" + + text + "\"," + "\"date\":" + + std::to_string(date) + + "}"; + } + + static std::string formatDate(int64_t timestamp) + { + char date_str[64]; + time_t unix_time = timestamp / 1000; + strftime(date_str, sizeof(date_str), "%Y-%m-%d", localtime(&unix_time)); + return date_str; + } + }; + + // Forward declarations + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo); + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo); + + // Global state + namespace + { + TodoCallback g_todoAddedCallback; + TodoCallback g_todoUpdatedCallback; + TodoCallback g_todoDeletedCallback; + GMainContext *g_gtk_main_context = nullptr; + GMainLoop *g_main_loop = nullptr; + std::thread *g_gtk_thread = nullptr; + std::vector g_todos; + } + + // Helper functions + static void notify_callback(const TodoCallback &callback, const std::string &json) + { + if (callback && g_gtk_main_context) + { + g_main_context_invoke(g_gtk_main_context, [](gpointer data) -> gboolean + { + auto* cb_data = static_cast*>(data); + cb_data->first(cb_data->second); + delete cb_data; + return G_SOURCE_REMOVE; }, new std::pair(callback, json)); + } + } + + static void update_todo_row_label(GtkListBoxRow *row, const TodoItem &todo) + { + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + auto *old_label = GTK_WIDGET(gtk_container_get_children(GTK_CONTAINER(row))->data); + gtk_container_remove(GTK_CONTAINER(row), old_label); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_widget_show_all(GTK_WIDGET(row)); + } + + static GtkWidget *create_todo_dialog(GtkWindow *parent, const TodoItem *existing_todo = nullptr) + { + auto *dialog = gtk_dialog_new_with_buttons( + existing_todo ? "Edit Todo" : "Add Todo", + parent, + GTK_DIALOG_MODAL, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + nullptr); + + auto *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_set_border_width(GTK_CONTAINER(content_area), 10); + + auto *entry = gtk_entry_new(); + if (existing_todo) + { + gtk_entry_set_text(GTK_ENTRY(entry), existing_todo->text.c_str()); + } + gtk_container_add(GTK_CONTAINER(content_area), entry); + + auto *calendar = gtk_calendar_new(); + if (existing_todo) + { + time_t unix_time = existing_todo->date / 1000; + struct tm *timeinfo = localtime(&unix_time); + gtk_calendar_select_month(GTK_CALENDAR(calendar), timeinfo->tm_mon, timeinfo->tm_year + 1900); + gtk_calendar_select_day(GTK_CALENDAR(calendar), timeinfo->tm_mday); + } + gtk_container_add(GTK_CONTAINER(content_area), calendar); + + gtk_widget_show_all(dialog); + return dialog; + } + + static void edit_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + auto *dialog = create_todo_dialog( + GTK_WINDOW(gtk_builder_get_object(builder, "window")), + &g_todos[index]); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + auto *entry = GTK_ENTRY(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->data); + auto *calendar = GTK_CALENDAR(gtk_container_get_children( + GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)))) + ->next->data); + + const char *new_text = gtk_entry_get_text(entry); + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + gint64 new_date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos[index].text = new_text; + g_todos[index].date = new_date; + + update_todo_row_label(row, g_todos[index]); + notify_callback(g_todoUpdatedCallback, g_todos[index].toJson()); + } + + gtk_widget_destroy(dialog); + } + + static void delete_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + auto *row = gtk_list_box_get_selected_row(list); + if (!row) + return; + + gint index = gtk_list_box_row_get_index(row); + auto size = static_cast(g_todos.size()); + if (index < 0 || index >= size) + return; + + std::string json = g_todos[index].toJson(); + gtk_container_remove(GTK_CONTAINER(list), GTK_WIDGET(row)); + g_todos.erase(g_todos.begin() + index); + notify_callback(g_todoDeletedCallback, json); + } + + static void on_add_clicked(GtkButton *button, gpointer user_data) + { + auto *builder = static_cast(user_data); + auto *entry = GTK_ENTRY(gtk_builder_get_object(builder, "todo_entry")); + auto *calendar = GTK_CALENDAR(gtk_builder_get_object(builder, "todo_calendar")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + const char *text = gtk_entry_get_text(entry); + if (strlen(text) > 0) + { + TodoItem todo; + uuid_generate(todo.id); + todo.text = text; + + guint year, month, day; + gtk_calendar_get_date(calendar, &year, &month, &day); + GDateTime *datetime = g_date_time_new_local(year, month + 1, day, 0, 0, 0); + todo.date = g_date_time_to_unix(datetime) * 1000; + g_date_time_unref(datetime); + + g_todos.push_back(todo); + + auto *row = gtk_list_box_row_new(); + auto *label = gtk_label_new((todo.text + " - " + TodoItem::formatDate(todo.date)).c_str()); + gtk_container_add(GTK_CONTAINER(row), label); + gtk_container_add(GTK_CONTAINER(list), row); + gtk_widget_show_all(row); + + gtk_entry_set_text(entry, ""); + + notify_callback(g_todoAddedCallback, todo.toJson()); + } + } + + static void on_row_activated(GtkListBox *list_box, GtkListBoxRow *row, gpointer user_data) + { + GMenu *menu = g_menu_new(); + g_menu_append(menu, "Edit", "app.edit"); + g_menu_append(menu, "Delete", "app.delete"); + + auto *popover = gtk_popover_new_from_model(GTK_WIDGET(row), G_MENU_MODEL(menu)); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_RIGHT); + gtk_popover_popup(GTK_POPOVER(popover)); + + g_object_unref(menu); + } + + static gboolean init_gtk_app(gpointer user_data) + { + auto *app = static_cast(user_data); + g_application_run(G_APPLICATION(app), 0, nullptr); + g_object_unref(app); + if (g_main_loop) + { + g_main_loop_quit(g_main_loop); + } + return G_SOURCE_REMOVE; + } + + static void activate_handler(GtkApplication *app, gpointer user_data) + { + auto *builder = gtk_builder_new(); + + const GActionEntry app_actions[] = { + {"edit", edit_action, nullptr, nullptr, nullptr, {0, 0, 0}}, + {"delete", delete_action, nullptr, nullptr, nullptr, {0, 0, 0}}}; + g_action_map_add_action_entries(G_ACTION_MAP(app), app_actions, + G_N_ELEMENTS(app_actions), builder); + + gtk_builder_add_from_string(builder, + "" + "" + " " + " Todo List" + " 400" + " 500" + " " + " " + " true" + " vertical" + " 6" + " 12" + " " + " " + " true" + " 6" + " " + " " + " true" + " true" + " Enter todo item..." + " " + " " + " " + " " + " true" + " " + " " + " " + " " + " true" + " Add" + " " + " " + " " + " " + " " + " " + " true" + " true" + " " + " " + " true" + " single" + " " + " " + " " + " " + " " + " " + " " + "", + -1, nullptr); + + auto *window = GTK_WINDOW(gtk_builder_get_object(builder, "window")); + auto *button = GTK_BUTTON(gtk_builder_get_object(builder, "add_button")); + auto *list = GTK_LIST_BOX(gtk_builder_get_object(builder, "todo_list")); + + gtk_window_set_application(window, app); + + g_signal_connect(button, "clicked", G_CALLBACK(on_add_clicked), builder); + g_signal_connect(list, "row-activated", G_CALLBACK(on_row_activated), nullptr); + + gtk_widget_show_all(GTK_WIDGET(window)); + } + + void hello_gui() + { + if (g_gtk_thread != nullptr) + { + g_print("GTK application is already running.\n"); + return; + } + + if (!gtk_init_check(0, nullptr)) + { + g_print("Failed to initialize GTK.\n"); + return; + } + + g_gtk_main_context = g_main_context_new(); + g_main_loop = g_main_loop_new(g_gtk_main_context, FALSE); + + g_gtk_thread = new std::thread([]() + { + GtkApplication* app = gtk_application_new("com.example.todo", G_APPLICATION_NON_UNIQUE); + g_signal_connect(app, "activate", G_CALLBACK(activate_handler), nullptr); + + g_idle_add_full(G_PRIORITY_DEFAULT, init_gtk_app, app, nullptr); + + if (g_main_loop) { + g_main_loop_run(g_main_loop); + } }); + + g_gtk_thread->detach(); + } + + void cleanup_gui() + { + if (g_main_loop && g_main_loop_is_running(g_main_loop)) + { + g_main_loop_quit(g_main_loop); + } + + if (g_main_loop) + { + g_main_loop_unref(g_main_loop); + g_main_loop = nullptr; + } + + if (g_gtk_main_context) + { + g_main_context_unref(g_gtk_main_context); + g_gtk_main_context = nullptr; + } + + g_gtk_thread = nullptr; + } + + void setTodoAddedCallback(TodoCallback callback) + { + g_todoAddedCallback = callback; + } + + void setTodoUpdatedCallback(TodoCallback callback) + { + g_todoUpdatedCallback = callback; + } + + void setTodoDeletedCallback(TodoCallback callback) + { + g_todoDeletedCallback = callback; + } + +} // namespace cpp_code +``` + +## 5) Creating the Node.js addon bridge + +Now let's implement the bridge between our C++ code and Node.js in `src/cpp_addon.cc`. Let's start by creating a basic skeleton for our addon: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +// Class to wrap our C++ code will go here + +Napi::Object Init(Napi::Env env, Napi::Object exports) { + // We'll add code here later + return exports; +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +This is the minimal structure required for a Node.js addon using `node-addon-api`. The `Init` function is called when the addon is loaded, and the `NODE_API_MODULE` macro registers our initializer. This basic skeleton doesn't do anything yet, but it provides the entry point for Node.js to load our native code. + +### Create a class to wrap our C++ code + +Let's create a class that will wrap our C++ code and expose it to JavaScript. In our previous step, we've added a comment reading "Class to wrap our C++ code will go here" - replace it with the code below. + +```cpp title='src/cpp_addon.cc' +class CppAddon : public Napi::ObjectWrap +{ +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) + { + Napi::Function func = DefineClass(env, "CppLinuxAddon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference *constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppLinuxAddon", func); + return exports; + } + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + // We'll implement the constructor together with a callback struct later + } + + ~CppAddon() + { + if (tsfn_ != nullptr) + { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + // Method implementations will go here +}; +``` + +Here, we create a C++ class that inherits from `Napi::ObjectWrap`: + +`static Napi::Object Init` defines our JavaScript interface with three methods: + +* `helloWorld`: A simple function to test the bridge +* `helloGui`: The function to launch our GTK3 UI +* `on`: A method to register event callbacks + +The constructor initializes: + +* `emitter`: An object that will emit events to JavaScript +* `callbacks`: A map of registered JavaScript callback functions +* `tsfn_`: A thread-safe function handle (crucial for GTK3 thread communication) + +The destructor properly cleans up the thread-safe function when the object is garbage collected. + +### Implement basic functionality - HelloWorld + +Next, we'll add our two main methods, `HelloWorld()` and `HelloGui()`. We'll add these to our `private` scope, right where we have a comment reading "Method implementations will go here". + +```cpp title='src/cpp_addon.cc' +Napi::Value HelloWorld(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); +} + +void HelloGui(const Napi::CallbackInfo &info) +{ + cpp_code::hello_gui(); +} + +// On() method implementation will go here +``` + +`HelloWorld()`: + +* Validates the input argument (must be a string) +* Calls our C++ hello_world function +* Returns the result as a JavaScript string + +`HelloGui()`: + +* Simply calls our C++ hello_gui function without arguments +* Returns nothing (void) as the function just launches the UI +* These methods form the direct bridge between JavaScript calls and our native C++ functions. + +You might be wondering what `Napi::CallbackInfo` is or where it comes from. This is a class provided by the Node-API (N-API) C++ wrapper, specifically from the [`node-addon-api`](https://github.com/nodejs/node-addon-api) package. It encapsulates all the information about a JavaScript function call, including: + +* The arguments passed from JavaScript +* The JavaScript execution environment (via `info.Env()`) +* The `this` value of the function call +* The number of arguments (via `info.Length()`) + +This class is fundamental to the Node.js native addon development as it serves as the bridge between JavaScript function calls and C++ method implementations. Every native method that can be called from JavaScript receives a `CallbackInfo` object as its parameter, allowing the C++ code to access and validate the JavaScript arguments before processing them. You can see us using it in `HelloWorld()` to get function parameters and other information about the function call. Our `HelloGui()` function doesn't use it, but if it did, it'd follow the same pattern. + +### Setting up the event system + +Now we'll tackle the tricky part of native development: setting up the event system. Previously, we added native callbacks to our `cpp_code.cc` code - and in our bridge code in `cpp_addon.cc`, we'll need to find a way to have those callbacks ultimately trigger a JavaScript method. + +Let's start with the `On()` method, which we'll call from JavaScript. In our previously written code, you'll find a comment reading `On() method implementation will go here`. Replace it with the following method: + +```cpp title='src/cpp_addon.cc' +Napi::Value On(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) + { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); +} +``` + +This method allows JavaScript to register callbacks for different event types and stores the JavaScript function in our `callbacks` map for later use. So far, so good - but now we need to let `cpp_code.cc` know about these callbacks. We also need to figure out a way to coordinate our threads, because the actual `cpp_code.cc` will be doing most of its work on its own thread. + +In our code, find the section where we're declaring the constructor `CppAddon(const Napi::CallbackInfo &info)`, which you'll find in the `public` section. It should have a comment reading `We'll implement the constructor together with a callback struct later`. Then, replace that part with the following code: + +```cpp title='src/cpp_addon.cc' + struct CallbackData + { + std::string eventType; + std::string payload; + CppAddon *addon; + }; + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void *context, void *data) + { + auto *callbackData = static_cast(data); + if (!callbackData) + return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) + { + delete callbackData; + return; + } + + try + { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) + { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } + catch (...) + { + } + + delete callbackData; + }, + &tsfn_); + + if (status != napi_ok) + { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string &eventType) + { + return [this, eventType](const std::string &payload) + { + if (tsfn_ != nullptr) + { + auto *data = new CallbackData{ + eventType, + payload, + this}; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + cpp_code::setTodoUpdatedCallback(makeCallback("todoUpdated")); + cpp_code::setTodoDeletedCallback(makeCallback("todoDeleted")); + } +``` + +This is the most complex part of our bridge: implementing bidirectional communication. There are a few things worth noting going on here, so let's take them step by step: + +`CallbackData` struct: + +* Holds the event type, JSON payload, and a reference to our addon. + +In the constructor: + +* We create a thread-safe function (`napi_create_threadsafe_function`) which is crucial for calling into JavaScript from the GTK3 thread +* The thread-safe function callback unpacks the data and calls the appropriate JavaScript callback +* We create a lambda `makeCallback` that produces callback functions for different event types +* We register these callbacks with our C++ code using the setter functions + +Let's talk about `napi_create_threadsafe_function`. The orchestration of different threads is maybe the most difficult part about native addon development - and in our experience, the place where developers are most likely to give up. `napi_create_threadsafe_function` is provided by the N-API and allows you to safely call JavaScript functions from any thread. This is essential when working with GUI frameworks like GTK3 that run on their own thread. Here's why it's important: + +1. **Thread Safety**: JavaScript in Electron runs on a single thread (exceptions apply, but this is a generally useful rule). Without thread-safe functions, calling JavaScript from another thread would cause crashes or race conditions. +1. **Queue Management**: It automatically queues function calls and executes them on the JavaScript thread. +1. **Resource Management**: It handles proper reference counting to ensure objects aren't garbage collected while still needed. + +In our code, we're using it to bridge the gap between GTK3's event loop and Node.js's event loop, allowing events from our GUI to safely trigger JavaScript callbacks. + +For developers wanting to learn more, you can refer to the [official N-API documentation](https://nodejs.org/api/n-api.html#n_api_napi_create_threadsafe_function) for detailed information about thread-safe functions, the [node-addon-api wrapper documentation](https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md) for the C++ wrapper implementation, and the [Node.js Threading Model article](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) to understand how Node.js handles concurrency and why thread-safe functions are necessary. + +### Putting `cpp_addon.cc` together + +We've now finished the bridge part our addon - that is, the code that's most concerned with being the bridge between your JavaScript and C++ code (and by contrast, less so actually interacting with the operating system or GTK). After adding all the sections above, your `src/cpp_addon.cc` should look like this: + +```cpp title='src/cpp_addon.cc' +#include +#include +#include "cpp_code.h" + +class CppAddon : public Napi::ObjectWrap +{ +public: + static Napi::Object Init(Napi::Env env, Napi::Object exports) + { + Napi::Function func = DefineClass(env, "CppLinuxAddon", { + InstanceMethod("helloWorld", &CppAddon::HelloWorld), + InstanceMethod("helloGui", &CppAddon::HelloGui), + InstanceMethod("on", &CppAddon::On) + }); + + Napi::FunctionReference *constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.SetInstanceData(constructor); + + exports.Set("CppLinuxAddon", func); + return exports; + } + + struct CallbackData + { + std::string eventType; + std::string payload; + CppAddon *addon; + }; + + CppAddon(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info), + env_(info.Env()), + emitter(Napi::Persistent(Napi::Object::New(info.Env()))), + callbacks(Napi::Persistent(Napi::Object::New(info.Env()))), + tsfn_(nullptr) + { + napi_status status = napi_create_threadsafe_function( + env_, + nullptr, + nullptr, + Napi::String::New(env_, "CppCallback"), + 0, + 1, + nullptr, + nullptr, + this, + [](napi_env env, napi_value js_callback, void *context, void *data) + { + auto *callbackData = static_cast(data); + if (!callbackData) + return; + + Napi::Env napi_env(env); + Napi::HandleScope scope(napi_env); + + auto addon = static_cast(context); + if (!addon) + { + delete callbackData; + return; + } + + try + { + auto callback = addon->callbacks.Value().Get(callbackData->eventType).As(); + if (callback.IsFunction()) + { + callback.Call(addon->emitter.Value(), {Napi::String::New(napi_env, callbackData->payload)}); + } + } + catch (...) + { + } + + delete callbackData; + }, + &tsfn_); + + if (status != napi_ok) + { + Napi::Error::New(env_, "Failed to create threadsafe function").ThrowAsJavaScriptException(); + return; + } + + // Set up the callbacks here + auto makeCallback = [this](const std::string &eventType) + { + return [this, eventType](const std::string &payload) + { + if (tsfn_ != nullptr) + { + auto *data = new CallbackData{ + eventType, + payload, + this}; + napi_call_threadsafe_function(tsfn_, data, napi_tsfn_blocking); + } + }; + }; + + cpp_code::setTodoAddedCallback(makeCallback("todoAdded")); + cpp_code::setTodoUpdatedCallback(makeCallback("todoUpdated")); + cpp_code::setTodoDeletedCallback(makeCallback("todoDeleted")); + } + + ~CppAddon() + { + if (tsfn_ != nullptr) + { + napi_release_threadsafe_function(tsfn_, napi_tsfn_release); + tsfn_ = nullptr; + } + } + +private: + Napi::Env env_; + Napi::ObjectReference emitter; + Napi::ObjectReference callbacks; + napi_threadsafe_function tsfn_; + + Napi::Value HelloWorld(const Napi::CallbackInfo &info) + { + Napi::Env env = info.Env(); + + if (info.Length() < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "Expected string argument").ThrowAsJavaScriptException(); + return env.Null(); + } + + std::string input = info[0].As(); + std::string result = cpp_code::hello_world(input); + + return Napi::String::New(env, result); + } + + void HelloGui(const Napi::CallbackInfo &info) + { + cpp_code::hello_gui(); + } + + Napi::Value On(const Napi::CallbackInfo &info) + { + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) + { + Napi::TypeError::New(env, "Expected (string, function) arguments").ThrowAsJavaScriptException(); + return env.Undefined(); + } + + callbacks.Value().Set(info[0].As(), info[1].As()); + return env.Undefined(); + } +}; + +Napi::Object Init(Napi::Env env, Napi::Object exports) +{ + return CppAddon::Init(env, exports); +} + +NODE_API_MODULE(cpp_addon, Init) +``` + +## 6) Creating a JavaScript wrapper + +Let's finish things off by adding a JavaScript wrapper in `js/index.js`. As we could all see, C++ requires a lot of boilerplate code that might be easier or faster to write in JavaScript - and you will find that many production applications end up transforming data or requests in JavaScript before invoking native code. We, for instance, turn our timestamp into a proper JavaScript date. + +```cpp title='js/index.js' +const EventEmitter = require('events'); + +class CppLinuxAddon extends EventEmitter { + constructor() { + super() + + if (process.platform !== 'linux') { + throw new Error('This module is only available on Linux'); + } + + const native = require('bindings')('cpp_addon') + this.addon = new native.CppLinuxAddon() + + // Set up event forwarding + this.addon.on('todoAdded', (payload) => { + this.emit('todoAdded', this.parse(payload)) + }); + + this.addon.on('todoUpdated', (payload) => { + this.emit('todoUpdated', this.parse(payload)) + }) + + this.addon.on('todoDeleted', (payload) => { + this.emit('todoDeleted', this.parse(payload)) + }) + } + + helloWorld(input = "") { + return this.addon.helloWorld(input) + } + + helloGui() { + return this.addon.helloGui() + } + + // Parse JSON and convert date to JavaScript Date object + parse(payload) { + const parsed = JSON.parse(payload) + + return { ...parsed, date: new Date(parsed.date) } + } +} + +if (process.platform === 'linux') { + module.exports = new CppLinuxAddon() +} else { + // Return empty object on non-Linux platforms + module.exports = {} +} +``` + +This wrapper: + +* Extends EventEmitter for native event handling +* Only loads on Linux platforms +* Forwards events from C++ to JavaScript +* Provides clean methods to call into C++ +* Converts JSON data into proper JavaScript objects + +## 7) Building and testing the addon + +With all files in place, you can build the addon: + +```sh +npm run build +``` + +If the build completes, you can now add the addon to your Electron app and `import` or `require` it there. + +## Usage Example + +Once you've built the addon, you can use it in your Electron application. Here's a complete example: + +```js @ts-expect-error=[2] +// In your Electron main process or renderer process +import cppLinux from 'cpp-linux' + +// Test the basic functionality +console.log(cppLinux.helloWorld('Hi!')) +// Output: "Hello from C++! You said: Hi!" + +// Set up event listeners for GTK GUI interactions +cppLinux.on('todoAdded', (todo) => { + console.log('New todo added:', todo) + // todo: { id: "uuid-string", text: "Todo text", date: Date object } +}) + +cppLinux.on('todoUpdated', (todo) => { + console.log('Todo updated:', todo) +}) + +cppLinux.on('todoDeleted', (todo) => { + console.log('Todo deleted:', todo) +}) + +// Launch the native GTK GUI +cppLinux.helloGui() +``` + +When you run this code: + +1. The `helloWorld()` call will return a greeting from C++ +2. The event listeners will be triggered when users interact with the GTK3 GUI +3. The `helloGui()` call will open a native GTK3 window with: + * A text entry field for todo items + * A calendar widget for selecting dates + * An "Add" button to create new todos + * A scrollable list showing all todos + * Right-click context menus for editing and deleting todos + +All interactions with the native GTK3 interface will trigger the corresponding JavaScript events, allowing your Electron application to respond to native GUI actions in real-time. + +## Conclusion + +You've now built a complete native Node.js addon for Linux using C++ and GTK3. This addon: + +1. Provides a bidirectional bridge between JavaScript and C++ +1. Creates a native GTK3 GUI that runs in its own thread +1. Implements a simple Todo application with add functionality +1. Uses GTK3, which is compatible with Electron's Chromium runtime +1. Handles callbacks from C++ to JavaScript safely + +This foundation can be extended to implement more complex Linux-specific features in your Electron applications. You can access system features, integrate with Linux-specific libraries, or create performant native UIs while maintaining the flexibility and ease of development that Electron provides. +For more information on GTK3 development, refer to the [GTK3 Documentation](https://docs.gtk.org/gtk3/) and the [GLib/GObject documentation](https://docs.gtk.org/gobject/). You may also find the [Node.js N-API documentation](https://nodejs.org/api/n-api.html) and [node-addon-api](https://github.com/nodejs/node-addon-api) helpful for extending your native addons. From 13ceef30b7db5517ec54425f3711c4ef86987fe3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:51:42 +0200 Subject: [PATCH 287/339] feat: support customizing window accent color on Windows (#47538) * fix: support window accent color in frameless windows Co-authored-by: Shelley Vohr * refactor: allow customization Co-authored-by: Shelley Vohr * Update docs/api/structures/base-window-options.md Co-authored-by: Will Anderson Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/structures/base-window-options.md | 1 + shell/browser/native_window_views.cc | 11 +++ shell/browser/native_window_views.h | 3 + shell/browser/native_window_views_win.cc | 90 ++++++++++++++++++++++ shell/common/options_switches.h | 2 + 5 files changed, 107 insertions(+) diff --git a/docs/api/structures/base-window-options.md b/docs/api/structures/base-window-options.md index 0b451c7733a0e..375c6adc3b59b 100644 --- a/docs/api/structures/base-window-options.md +++ b/docs/api/structures/base-window-options.md @@ -95,6 +95,7 @@ * `color` String (optional) _Windows_ _Linux_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color. * `symbolColor` String (optional) _Windows_ _Linux_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color. * `height` Integer (optional) - The height of the title bar and Window Controls Overlay in pixels. Default is system height. +* `accentColor` boolean | string (optional) _Windows_ - The accent color for the window. By default, follows user preference in System Settings. Set to `false` to explicitly disable, or set the color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha values will be ignored. * `trafficLightPosition` [Point](point.md) (optional) _macOS_ - Set a custom position for the traffic light buttons in frameless windows. * `roundedCorners` boolean (optional) _macOS_ _Windows_ - Whether frameless window diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 6ac9ee7a5b521..f81918bd8c6c9 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -79,6 +79,7 @@ #include "base/win/windows_version.h" #include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/ui/win/electron_desktop_native_widget_aura.h" +#include "shell/common/color_util.h" #include "skia/ext/skia_utils_win.h" #include "ui/display/win/screen_win.h" #include "ui/gfx/color_utils.h" @@ -213,6 +214,14 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE); overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT); + + bool accent_color = true; + std::string accent_color_string; + if (options.Get(options::kAccentColor, &accent_color_string)) { + accent_color_ = ParseCSSColor(accent_color_string); + } else if (options.Get(options::kAccentColor, &accent_color)) { + accent_color_ = accent_color; + } #endif v8::Local titlebar_overlay; @@ -429,6 +438,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, last_window_state_ = ui::mojom::WindowShowState::kFullscreen; else last_window_state_ = ui::mojom::WindowShowState::kNormal; + + UpdateWindowAccentColor(); #endif // Listen to mouse events. diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 8ef7d2b783a2f..7f6c44793ea7e 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -208,6 +208,7 @@ class NativeWindowViews : public NativeWindow, void ResetWindowControls(); void SetRoundedCorners(bool rounded); void SetForwardMouseMessages(bool forward); + void UpdateWindowAccentColor(); static LRESULT CALLBACK SubclassProc(HWND hwnd, UINT msg, WPARAM w_param, @@ -304,6 +305,8 @@ class NativeWindowViews : public NativeWindow, // Whether the window is currently being moved. bool is_moving_ = false; + std::variant accent_color_ = true; + std::optional pending_bounds_change_; // The message ID of the "TaskbarCreated" message, sent to us when we need to diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index c5d72bc5bb325..219fdaca646a6 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -7,6 +7,7 @@ #include #include "base/win/atl.h" // Must be before UIAutomationCore.h +#include "base/win/registry.h" #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" #include "content/public/browser/browser_accessibility_state.h" @@ -28,6 +29,53 @@ namespace electron { namespace { +void SetWindowBorderAndCaptionColor(HWND hwnd, COLORREF color) { + if (base::win::GetVersion() < base::win::Version::WIN11) + return; + + HRESULT result = + DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &color, sizeof(color)); + + if (FAILED(result)) + LOG(WARNING) << "Failed to set caption color"; + + result = + DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color)); + + if (FAILED(result)) + LOG(WARNING) << "Failed to set border color"; +} + +std::optional GetAccentColor() { + base::win::RegKey key; + if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM", + KEY_READ) != ERROR_SUCCESS) { + return std::nullopt; + } + + DWORD accent_color = 0; + if (key.ReadValueDW(L"AccentColor", &accent_color) != ERROR_SUCCESS) { + return std::nullopt; + } + + return accent_color; +} + +bool IsAccentColorOnTitleBarsEnabled() { + base::win::RegKey key; + if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM", + KEY_READ) != ERROR_SUCCESS) { + return false; + } + + DWORD enabled = 0; + if (key.ReadValueDW(L"ColorPrevalence", &enabled) != ERROR_SUCCESS) { + return false; + } + + return enabled != 0; +} + // Convert Win32 WM_QUERYENDSESSIONS to strings. const std::vector EndSessionToStringVec(LPARAM end_session_id) { std::vector params; @@ -448,6 +496,19 @@ bool NativeWindowViews::PreHandleMSG(UINT message, } return false; } + case WM_DWMCOLORIZATIONCOLORCHANGED: { + UpdateWindowAccentColor(); + return false; + } + case WM_SETTINGCHANGE: { + if (l_param) { + const wchar_t* setting_name = reinterpret_cast(l_param); + std::wstring setting_str(setting_name); + if (setting_str == L"ImmersiveColorSet") + UpdateWindowAccentColor(); + } + return false; + } default: { return false; } @@ -507,6 +568,35 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) { } } +void NativeWindowViews::UpdateWindowAccentColor() { + if (base::win::GetVersion() < base::win::Version::WIN11) + return; + + if (!IsAccentColorOnTitleBarsEnabled()) + return; + + COLORREF border_color; + if (std::holds_alternative(accent_color_)) { + // Don't set accent color if the user has disabled it. + if (!std::get(accent_color_)) + return; + + std::optional accent_color = GetAccentColor(); + if (!accent_color.has_value()) + return; + + border_color = + RGB(GetRValue(accent_color.value()), GetGValue(accent_color.value()), + GetBValue(accent_color.value())); + } else { + SkColor color = std::get(accent_color_); + border_color = + RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + } + + SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), border_color); +} + void NativeWindowViews::ResetWindowControls() { // If a given window was minimized and has since been // unminimized (restored/maximized), ensure the WCO buttons diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 7b86b734e5a21..1e2aad82727f3 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -123,6 +123,8 @@ inline constexpr std::string_view kRoundedCorners = "roundedCorners"; inline constexpr std::string_view ktitleBarOverlay = "titleBarOverlay"; +inline constexpr std::string_view kAccentColor = "accentColor"; + // The color to use as the theme and symbol colors respectively for Window // Controls Overlay if enabled on Windows. inline constexpr std::string_view kOverlayButtonColor = "color"; From e90282d57aa5bd8ad1fdda0ccfaa91bfda6249d9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:07:23 -0500 Subject: [PATCH 288/339] docs: update asar integrity fuse availability (#47566) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Niklas Wenzel --- docs/tutorial/fuses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/fuses.md b/docs/tutorial/fuses.md index 1afa53d6a7df3..c810d1af2a660 100644 --- a/docs/tutorial/fuses.md +++ b/docs/tutorial/fuses.md @@ -48,7 +48,7 @@ The nodeCliInspect fuse toggles whether the `--inspect`, `--inspect-brk`, etc. f **@electron/fuses:** `FuseV1Options.EnableEmbeddedAsarIntegrityValidation` -The embeddedAsarIntegrityValidation fuse toggles an experimental feature on macOS that validates the content of the `app.asar` file when it is loaded. This feature is designed to have a minimal performance impact but may marginally slow down file reads from inside the `app.asar` archive. +The embeddedAsarIntegrityValidation fuse toggles an experimental feature on macOS and Windows that validates the content of the `app.asar` file when it is loaded. This feature is designed to have a minimal performance impact but may marginally slow down file reads from inside the `app.asar` archive. For more information on how to use asar integrity validation please read the [Asar Integrity](asar-integrity.md) documentation. From 33d23f5e00422c338571ce2be9b259d12f3ae215 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 08:18:10 -0500 Subject: [PATCH 289/339] refactor: remove stray `.c_str()` calls for `absl::StrFormat()` (#47576) refactor: remove stray .c_str() calls for absl::StrFormat() StrFormat() understands std::string, std::string_view Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/api/electron_api_session.cc | 5 ++--- .../electron_management_api_delegate.cc | 5 ++--- .../extensions/api/scripting/scripting_api.cc | 8 +++---- shell/browser/hid/hid_chooser_controller.cc | 7 +++---- .../net/proxying_url_loader_factory.cc | 2 +- shell/browser/net/proxying_websocket.cc | 3 +-- shell/browser/ui/inspectable_web_contents.cc | 21 ++++++++----------- shell/browser/ui/views/global_menu_bar_x11.cc | 4 ++-- shell/common/application_info.cc | 2 +- shell/renderer/renderer_client_base.cc | 4 ++-- 10 files changed, 27 insertions(+), 34 deletions(-) diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 563d63aa95e7d..08d4861b93e24 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -1499,9 +1499,8 @@ v8::Local Session::ClearData(gin_helper::ErrorThrower thrower, // Opaque origins cannot be used with this API if (origin.opaque()) { - thrower.ThrowError( - absl::StrFormat("Invalid origin: '%s'", - origin_url.possibly_invalid_spec().c_str())); + thrower.ThrowError(absl::StrFormat( + "Invalid origin: '%s'", origin_url.possibly_invalid_spec())); return v8::Undefined(isolate); } diff --git a/shell/browser/extensions/api/management/electron_management_api_delegate.cc b/shell/browser/extensions/api/management/electron_management_api_delegate.cc index 258dc7851c92e..f555460588f8b 100644 --- a/shell/browser/extensions/api/management/electron_management_api_delegate.cc +++ b/shell/browser/extensions/api/management/electron_management_api_delegate.cc @@ -199,9 +199,8 @@ GURL ElectronManagementAPIDelegate::GetIconURL( ExtensionIconSet::Match match, bool grayscale) const { GURL icon_url(absl::StrFormat( - "%s%s/%d/%d%s", chrome::kChromeUIExtensionIconURL, - extension->id().c_str(), icon_size, static_cast(match), - grayscale ? "?grayscale=true" : "")); + "%s%s/%d/%d%s", chrome::kChromeUIExtensionIconURL, extension->id(), + icon_size, static_cast(match), grayscale ? "?grayscale=true" : "")); CHECK(icon_url.is_valid()); return icon_url; } diff --git a/shell/browser/extensions/api/scripting/scripting_api.cc b/shell/browser/extensions/api/scripting/scripting_api.cc index b140bd69b6064..33e2591bbde9a 100644 --- a/shell/browser/extensions/api/scripting/scripting_api.cc +++ b/shell/browser/extensions/api/scripting/scripting_api.cc @@ -215,7 +215,7 @@ bool CollectFramesForInjection(const api::scripting::InjectionTarget& target, ExtensionApiFrameIdMap::DocumentIdFromString(id); if (!document_id) { - *error_out = absl::StrFormat("Invalid document id %s", id.c_str()); + *error_out = absl::StrFormat("Invalid document id %s", id); return false; } @@ -227,7 +227,7 @@ bool CollectFramesForInjection(const api::scripting::InjectionTarget& target, // request. if (!frame || content::WebContents::FromRenderFrameHost(frame) != tab) { *error_out = absl::StrFormat("No document with id %s in tab with id %d", - id.c_str(), target.tab_id); + id, target.tab_id); return false; } @@ -499,8 +499,8 @@ ExtensionFunction::ResponseAction ScriptingExecuteScriptFunction::Run() { args_expression = base::JoinString(string_args, ","); } - std::string code_to_execute = absl::StrFormat( - "(%s)(%s)", injection_.func->c_str(), args_expression.c_str()); + std::string code_to_execute = + absl::StrFormat("(%s)(%s)", *injection_.func, args_expression); std::vector sources; sources.push_back(mojom::JSSource::New(std::move(code_to_execute), GURL())); diff --git a/shell/browser/hid/hid_chooser_controller.cc b/shell/browser/hid/hid_chooser_controller.cc index e1d6ed06b7a70..6cf1b0a9107f1 100644 --- a/shell/browser/hid/hid_chooser_controller.cc +++ b/shell/browser/hid/hid_chooser_controller.cc @@ -280,8 +280,8 @@ bool HidChooserController::DisplayDevice( absl::StrFormat( "Chooser dialog is not displaying a FIDO HID device: vendorId=%d, " "productId=%d, name='%s', serial='%s'", - device.vendor_id, device.product_id, device.product_name.c_str(), - device.serial_number.c_str())); + device.vendor_id, device.product_id, device.product_name, + device.serial_number)); return false; } @@ -292,8 +292,7 @@ bool HidChooserController::DisplayDevice( "the HID blocklist: vendorId=%d, " "productId=%d, name='%s', serial='%s'", device.vendor_id, device.product_id, - device.product_name.c_str(), - device.serial_number.c_str())); + device.product_name, device.serial_number)); return false; } diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index 572e5e4ae6ded..1bbb15b669cfc 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -394,7 +394,7 @@ void ProxyingURLLoaderFactory::InProgressRequest:: "HTTP/1.1 %i Internal Redirect\n" "Location: %s\n" "Non-Authoritative-Reason: WebRequest API\n\n", - kInternalRedirectStatusCode, redirect_url_.spec().c_str()); + kInternalRedirectStatusCode, redirect_url_.spec()); // Cross-origin requests need to modify the Origin header to 'null'. Since // CorsURLLoader sets |request_initiator| to the Origin request header in diff --git a/shell/browser/net/proxying_websocket.cc b/shell/browser/net/proxying_websocket.cc index 75f922fa5e40f..02ae781d0bb74 100644 --- a/shell/browser/net/proxying_websocket.cc +++ b/shell/browser/net/proxying_websocket.cc @@ -142,8 +142,7 @@ void ProxyingWebSocket::OnConnectionEstablished( base::MakeRefCounted(absl::StrFormat( "HTTP/%d.%d %d %s", handshake_response_->http_version.major_value(), handshake_response_->http_version.minor_value(), - handshake_response_->status_code, - handshake_response_->status_text.c_str())); + handshake_response_->status_code, handshake_response_->status_text)); for (const auto& header : handshake_response_->headers) response_->headers->AddHeader(header->name, header->value); diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc index e80d6a6d7662d..f05476527bd73 100644 --- a/shell/browser/ui/inspectable_web_contents.cc +++ b/shell/browser/ui/inspectable_web_contents.cc @@ -139,16 +139,14 @@ double GetNextZoomLevel(double level, bool out) { } GURL GetRemoteBaseURL() { - return GURL( - absl::StrFormat("%s%s/%s/", kChromeUIDevToolsRemoteFrontendBase, - kChromeUIDevToolsRemoteFrontendPath, - embedder_support::GetChromiumGitRevision().c_str())); + return GURL(absl::StrFormat("%s%s/%s/", kChromeUIDevToolsRemoteFrontendBase, + kChromeUIDevToolsRemoteFrontendPath, + embedder_support::GetChromiumGitRevision())); } GURL GetDevToolsURL(bool can_dock) { - auto url_string = - absl::StrFormat(kChromeUIDevToolsURL, GetRemoteBaseURL().spec().c_str(), - can_dock ? "true" : ""); + auto url_string = absl::StrFormat( + kChromeUIDevToolsURL, GetRemoteBaseURL().spec(), can_dock ? "true" : ""); return GURL(url_string); } @@ -630,8 +628,7 @@ void InspectableWebContents::SetInspectedPageBounds(const gfx::Rect& rect) { void InspectableWebContents::InspectedURLChanged(const std::string& url) { if (managed_devtools_web_contents_) { if (devtools_title_.empty()) { - view_->SetTitle( - base::UTF8ToUTF16(absl::StrFormat(kTitleFormat, url.c_str()))); + view_->SetTitle(base::UTF8ToUTF16(absl::StrFormat(kTitleFormat, url))); } } } @@ -1023,9 +1020,9 @@ void InspectableWebContents::DidFinishNavigation( // most likely bug in chromium. base::ReplaceFirstSubstringAfterOffset(&it->second, 0, "var chrome", "var chrome = window.chrome "); - auto script = absl::StrFormat( - "%s(\"%s\")", it->second.c_str(), - base::Uuid::GenerateRandomV4().AsLowercaseString().c_str()); + auto script = + absl::StrFormat("%s(\"%s\")", it->second, + base::Uuid::GenerateRandomV4().AsLowercaseString()); // Invoking content::DevToolsFrontendHost::SetupExtensionsAPI(frame, script); // should be enough, but it seems to be a noop currently. frame->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script), diff --git a/shell/browser/ui/views/global_menu_bar_x11.cc b/shell/browser/ui/views/global_menu_bar_x11.cc index 53e51db0857e1..feb1d9472770a 100644 --- a/shell/browser/ui/views/global_menu_bar_x11.cc +++ b/shell/browser/ui/views/global_menu_bar_x11.cc @@ -164,8 +164,8 @@ std::string GetMenuModelStatus(ElectronMenuModel* model) { int status = model->GetTypeAt(i) | (model->IsVisibleAt(i) << 3) | (model->IsEnabledAt(i) << 4) | (model->IsItemCheckedAt(i) << 5); - ret += absl::StrFormat( - "%s-%X\n", base::UTF16ToUTF8(model->GetLabelAt(i)).c_str(), status); + ret += absl::StrFormat("%s-%X\n", base::UTF16ToUTF8(model->GetLabelAt(i)), + status); } return ret; } diff --git a/shell/common/application_info.cc b/shell/common/application_info.cc index f7b04a2d04ac2..acba56d544ad0 100644 --- a/shell/common/application_info.cc +++ b/shell/common/application_info.cc @@ -44,7 +44,7 @@ std::string GetApplicationUserAgent() { } else { user_agent = absl::StrFormat( "%s/%s Chrome/%s " ELECTRON_PRODUCT_NAME "/" ELECTRON_VERSION_STRING, - name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING); + name, browser->GetVersion(), CHROME_VERSION_STRING); } return embedder_support::BuildUserAgentFromProduct(user_agent); } diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index 5eb0fe04fc6e1..cc971f7b65a55 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -185,8 +185,8 @@ RendererClientBase* RendererClientBase::Get() { void RendererClientBase::BindProcess(v8::Isolate* isolate, gin_helper::Dictionary* process, content::RenderFrame* render_frame) { - auto context_id = absl::StrFormat("%s-%" PRId64, renderer_client_id_.c_str(), - ++next_context_id_); + auto context_id = + absl::StrFormat("%s-%" PRId64, renderer_client_id_, ++next_context_id_); process->SetReadOnly("isMainFrame", render_frame->IsMainFrame()); process->SetReadOnly("contextIsolated", From bddc661684dde3c87ec7ba98f76390db4e64623b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:42:36 -0500 Subject: [PATCH 290/339] refactor: make context bridge's private keys hidden, constexpr string_views (#47587) * refactor: local functions GetPrivate(), SetPrivate() now take std::string_views Co-authored-by: Charles Kerr * refactor: make local keys std::string_views instead of C-style char arrays Co-authored-by: Charles Kerr * refactor: make local keys constexpr Co-authored-by: Charles Kerr * refactor: move local keys into local anonymous namespace Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- .../api/electron_api_context_bridge.cc | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index ff91fee761af8..da6bf279544b2 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -42,18 +42,16 @@ content::RenderFrame* GetRenderFrame(v8::Local value); namespace api { -namespace context_bridge { +namespace { -const char kProxyFunctionPrivateKey[] = "electron_contextBridge_proxy_fn"; -const char kProxyFunctionReceiverPrivateKey[] = +constexpr std::string_view kProxyFunctionPrivateKey = + "electron_contextBridge_proxy_fn"; +constexpr std::string_view kProxyFunctionReceiverPrivateKey = "electron_contextBridge_proxy_fn_receiver"; -const char kSupportsDynamicPropertiesPrivateKey[] = +constexpr std::string_view kSupportsDynamicPropertiesPrivateKey = "electron_contextBridge_supportsDynamicProperties"; -const char kOriginalFunctionPrivateKey[] = "electron_contextBridge_original_fn"; - -} // namespace context_bridge - -namespace { +constexpr std::string_view kOriginalFunctionPrivateKey = + "electron_contextBridge_original_fn"; static int kMaxRecursion = 1000; @@ -115,7 +113,7 @@ bool IsPlainArray(const v8::Local& arr) { void SetPrivate(v8::Local context, v8::Local target, - const std::string& key, + const std::string_view key, v8::Local value) { target ->SetPrivate( @@ -128,7 +126,7 @@ void SetPrivate(v8::Local context, v8::MaybeLocal GetPrivate(v8::Local context, v8::Local target, - const std::string& key) { + const std::string_view key) { return target->GetPrivate( context, v8::Private::ForApi(context->GetIsolate(), @@ -192,8 +190,8 @@ v8::MaybeLocal PassValueToOtherContextInner( // the global handle at the right time. if (value->IsFunction()) { auto func = value.As(); - v8::MaybeLocal maybe_original_fn = GetPrivate( - source_context, func, context_bridge::kOriginalFunctionPrivateKey); + v8::MaybeLocal maybe_original_fn = + GetPrivate(source_context, func, kOriginalFunctionPrivateKey); { v8::Context::Scope destination_scope(destination_context); @@ -214,13 +212,11 @@ v8::MaybeLocal PassValueToOtherContextInner( v8::Local state = v8::Object::New(destination_context->GetIsolate()); - SetPrivate(destination_context, state, - context_bridge::kProxyFunctionPrivateKey, func); - SetPrivate(destination_context, state, - context_bridge::kProxyFunctionReceiverPrivateKey, + SetPrivate(destination_context, state, kProxyFunctionPrivateKey, func); + SetPrivate(destination_context, state, kProxyFunctionReceiverPrivateKey, parent_value); SetPrivate(destination_context, state, - context_bridge::kSupportsDynamicPropertiesPrivateKey, + kSupportsDynamicPropertiesPrivateKey, gin::ConvertToV8(destination_context->GetIsolate(), support_dynamic_properties)); @@ -228,7 +224,7 @@ v8::MaybeLocal PassValueToOtherContextInner( .ToLocal(&proxy_func)) return {}; SetPrivate(destination_context, proxy_func.As(), - context_bridge::kOriginalFunctionPrivateKey, func); + kOriginalFunctionPrivateKey, func); object_cache->CacheProxiedObject(value, proxy_func); return v8::MaybeLocal(proxy_func); } @@ -486,12 +482,11 @@ void ProxyFunctionWrapper(const v8::FunctionCallbackInfo& info) { // Pull the original function and its context off of the data private key v8::MaybeLocal sdp_value = - GetPrivate(calling_context, data, - context_bridge::kSupportsDynamicPropertiesPrivateKey); - v8::MaybeLocal maybe_func = GetPrivate( - calling_context, data, context_bridge::kProxyFunctionPrivateKey); - v8::MaybeLocal maybe_recv = GetPrivate( - calling_context, data, context_bridge::kProxyFunctionReceiverPrivateKey); + GetPrivate(calling_context, data, kSupportsDynamicPropertiesPrivateKey); + v8::MaybeLocal maybe_func = + GetPrivate(calling_context, data, kProxyFunctionPrivateKey); + v8::MaybeLocal maybe_recv = + GetPrivate(calling_context, data, kProxyFunctionReceiverPrivateKey); v8::Local func_value; if (sdp_value.IsEmpty() || maybe_func.IsEmpty() || maybe_recv.IsEmpty() || !gin::ConvertFromV8(args.isolate(), sdp_value.ToLocalChecked(), From 5eb6cec6e9670bf9289aab11fe67df0a771eeee8 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:07:48 -0500 Subject: [PATCH 291/339] refactor: sync IsKillURL() with upstream impl in extension_tab_util.cc (#47594) Use base::MakeFixedFlatSet() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/extensions/api/tabs/tabs_api.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/shell/browser/extensions/api/tabs/tabs_api.cc b/shell/browser/extensions/api/tabs/tabs_api.cc index 50a8e2c371e97..29dd10e761eba 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.cc +++ b/shell/browser/extensions/api/tabs/tabs_api.cc @@ -11,7 +11,7 @@ #include #include "base/command_line.h" -#include "base/containers/contains.h" +#include "base/containers/fixed_flat_set.h" #include "base/strings/pattern.h" #include "base/types/expected_macros.h" #include "chrome/common/url_constants.h" @@ -497,13 +497,16 @@ bool IsKillURL(const GURL& url) { } // Also disallow a few more hosts which are not covered by the check above. - static const char* const kKillHosts[] = { - chrome::kChromeUIDelayedHangUIHost, chrome::kChromeUIHangUIHost, - chrome::kChromeUIQuitHost, chrome::kChromeUIRestartHost, - content::kChromeUIBrowserCrashHost, content::kChromeUIMemoryExhaustHost, - }; + constexpr auto kKillHosts = base::MakeFixedFlatSet({ + chrome::kChromeUIDelayedHangUIHost, + chrome::kChromeUIHangUIHost, + chrome::kChromeUIQuitHost, + chrome::kChromeUIRestartHost, + content::kChromeUIBrowserCrashHost, + content::kChromeUIMemoryExhaustHost, + }); - return base::Contains(kKillHosts, url.host_piece()); + return kKillHosts.contains(url.host_piece()); } GURL ResolvePossiblyRelativeURL(const std::string& url_string, From 78f78bf8be09f3d833923dae0243c0087bc6f507 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:29:16 +0200 Subject: [PATCH 292/339] perf: avoid copying a vector when calling ConvertToWeakPtrVector() (#47602) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/window_list.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/shell/browser/window_list.cc b/shell/browser/window_list.cc index 188402d0e6bb1..f945dfa2270bf 100644 --- a/shell/browser/window_list.cc +++ b/shell/browser/window_list.cc @@ -6,20 +6,18 @@ #include +#include "base/containers/to_vector.h" #include "base/no_destructor.h" #include "shell/browser/native_window.h" #include "shell/browser/window_list_observer.h" namespace { + template -std::vector> ConvertToWeakPtrVector(std::vector raw_ptrs) { - std::vector> converted_to_weak; - converted_to_weak.reserve(raw_ptrs.size()); - for (auto* raw_ptr : raw_ptrs) { - converted_to_weak.push_back(raw_ptr->GetWeakPtr()); - } - return converted_to_weak; +auto ConvertToWeakPtrVector(const std::vector& raw_ptrs) { + return base::ToVector(raw_ptrs, [](T* t) { return t->GetWeakPtr(); }); } + } // namespace namespace electron { From 668ded5ea328ff7ccf3054f38f014543d6ff2592 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:59:00 +0200 Subject: [PATCH 293/339] test: fix nan tests on macOS (#47608) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- script/nan-spec-runner.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/script/nan-spec-runner.js b/script/nan-spec-runner.js index 031062f4b3552..2abdfe85e91f4 100644 --- a/script/nan-spec-runner.js +++ b/script/nan-spec-runner.js @@ -46,20 +46,23 @@ async function main () { const platformFlags = []; if (process.platform === 'darwin') { const sdkPath = path.resolve(BASE, 'out', outDir, 'sdk', 'xcode_links'); - const sdks = (await fs.promises.readdir(sdkPath)).filter(fileName => fileName.endsWith('.sdk')); - const sdkToUse = sdks[0]; - if (!sdkToUse) { + const sdks = (await fs.promises.readdir(sdkPath)).filter(f => f.endsWith('.sdk')); + + if (!sdks.length) { console.error('Could not find an SDK to use for the NAN tests'); process.exit(1); } - if (sdks.length) { - console.warn(`Multiple SDKs found in the xcode_links directory - using ${sdkToUse}`); + const sdkToUse = sdks.sort((a, b) => { + const getVer = s => s.match(/(\d+)\.?(\d*)/)?.[0] || '0'; + return getVer(b).localeCompare(getVer(a), undefined, { numeric: true }); + })[0]; + + if (sdks.length > 1) { + console.warn(`Multiple SDKs found - using ${sdkToUse}`); } - platformFlags.push( - `-isysroot ${path.resolve(sdkPath, sdkToUse)}` - ); + platformFlags.push(`-isysroot ${path.resolve(sdkPath, sdkToUse)}`); } // TODO(ckerr) this is cribbed from read obj/electron/electron_app.ninja. From 387394e08439e75d7e81f393be07cb8a3f6892e5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 10:59:59 +0200 Subject: [PATCH 294/339] refactor: avoid copies of large objects in range based for loops (#47604) * Avoid copies of large objects in range-based for-loops. Xref: https://chromium-review.googlesource.com/c/chromium/src/+/6527689 Co-authored-by: Charles Kerr * Avoid copies of large objects in range-based for-loops in Browser::ShowAboutPanel() Co-authored-by: Charles Kerr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/browser_win.cc | 2 +- shell/browser/ui/webui/accessibility_ui.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/browser_win.cc b/shell/browser/browser_win.cc index da035a79099fe..fb75895404eb2 100644 --- a/shell/browser/browser_win.cc +++ b/shell/browser/browser_win.cc @@ -754,7 +754,7 @@ void Browser::ShowAboutPanel() { "applicationName", "applicationVersion", "copyright", "credits"}; const std::string* str; - for (std::string opt : stringOptions) { + for (const std::string& opt : stringOptions) { if ((str = dict.FindString(opt))) { aboutMessage.append(*str).append("\r\n"); } diff --git a/shell/browser/ui/webui/accessibility_ui.cc b/shell/browser/ui/webui/accessibility_ui.cc index 38a401dba0716..85fb55979db4a 100644 --- a/shell/browser/ui/webui/accessibility_ui.cc +++ b/shell/browser/ui/webui/accessibility_ui.cc @@ -273,7 +273,7 @@ std::string RecursiveDumpAXPlatformNodeAsString( std::string line = node->GetDelegate()->GetData().ToString(); std::vector attributes = base::SplitString( line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - for (std::string attribute : attributes) { + for (const std::string& attribute : attributes) { if (ui::AXTreeFormatter::MatchesPropertyFilters(property_filters, attribute, false)) { str += attribute + " "; From 37be7b0f636fc9ab99002de69f851a9e968f706c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:02:31 +0200 Subject: [PATCH 295/339] build: update yarn to 1.22.22 (#47639) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Samuel Attard --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f168341b431bc..337dc8f77887d 100644 --- a/DEPS +++ b/DEPS @@ -31,7 +31,7 @@ vars = { 'sysroots_json_path': 'electron/script/sysroots.json', # KEEP IN SYNC WITH utils.js FILE - 'yarn_version': '1.15.2', + 'yarn_version': '1.22.22', # To be able to build clean Chromium from sources. 'apply_patches': True, From da2f4eb2a994f7cea3229d3e42a954b5c8943d1f Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:59:39 +0200 Subject: [PATCH 296/339] chore: bump node to v22.17.0 (36-x-y) (#47554) * chore: bump node in DEPS to v22.17.0 * build: use //third_party/simdutf by default in GN https://github.com/nodejs/node/pull/58115 * chore: adjust crypto specs: - https://github.com/nodejs/node/pull/58117 - https://github.com/nodejs/node/pull/58387 * deps: update libuv to 1.51.0 https://github.com/nodejs/node/pull/58124 * test: fix test-buffer-tostring-range on allocation failure https://github.com/nodejs/node/pull/58416 * build: use FILE_OFFSET_BITS=64 esp. on 32-bit arch https://github.com/nodejs/node/pull/58090 * build: use //third_party/simdutf by default in GN https://github.com/nodejs/node/pull/58115 * inspector: add protocol method Network.dataReceived https://github.com/nodejs/node/pull/58001 * chore: fixup patch indices * 6049967: Remove protocol::Maybe and roll inspector_protocol https://chromium-review.googlesource.com/c/chromium/src/+/6049967 * chore: fixup crypto test patch * src: fix module buffer allocation https://github.com/nodejs/node/pull/57738 * crypto: expose process.features.openssl_is_boringssl https://github.com/nodejs/node/pull/58387 * util: add internal assignFunctionName() function https://github.com/nodejs/node/pull/57916 * build: fix pointer compression builds https://github.com/nodejs/node/pull/58171 * chore: put back config options * fixup! deps: update libuv to 1.51.0 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com> --- DEPS | 2 +- lib/node/asar-fs-wrapper.ts | 11 +- patches/node/.patches | 4 +- patches/node/build_add_gn_build_files.patch | 25 +- ...etraits_signatures_to_avoid_conflict.patch | 18 +- .../build_compile_with_c_20_support.patch | 6 +- patches/node/build_enable_perfetto.patch | 6 +- ...compilation_fails_if_not_using_a_new.patch | 18 +- ...f_original-fs_and_custom_embedder_js.patch | 2 +- ...o_use_custom_inspector_protocol_path.patch | 26 -- ...e_clang_as_default_compiler_on_macos.patch | 4 +- .../node/build_use_third_party_simdutf.patch | 20 - ...deprecation_ftbfs_in_simdjson_header.patch | 8 +- ...e_expose_importmoduledynamically_and.patch | 14 +- ...move_protocol_maybe_from_node_string.patch | 33 ++ ...cli_move_--trace-atomics-wait_to_eol.patch | 14 +- .../node/cli_remove_deprecated_v8_flag.patch | 8 +- ...enable_crashpad_linux_node_processes.patch | 10 +- .../expose_get_builtin_module_function.patch | 8 +- ...o_change_option_to_uv_loop_configure.patch | 92 ++-- ...matched-new-delete_in_debug_utils_cc.patch | 31 ++ ..._values_for_variables_in_common_gypi.patch | 4 +- ...g_fileexists_fn_to_legacymainresolve.patch | 10 +- ...ssert_module_in_the_renderer_process.patch | 4 +- .../node/fix_cppgc_initializing_twice.patch | 4 +- .../fix_crypto_tests_to_run_with_bssl.patch | 401 ++++-------------- ..._do_not_resolve_electron_entrypoints.patch | 2 +- ...se_readfilesync_override_for_modules.patch | 4 +- ...n_electron_module_via_the_esm_loader.patch | 4 +- ...ingssl_and_openssl_incompatibilities.patch | 29 +- ...in_esm_loaders_to_apply_asar_patches.patch | 2 +- ...ix_remove_deprecated_errno_constants.patch | 10 +- .../fix_remove_fastapitypedarray_usage.patch | 8 +- ...rmony-import-assertions_from_node_cc.patch | 4 +- ...pwritev64_before_preadv_pwritev_4683.patch | 51 --- .../pass_all_globals_through_require.patch | 2 +- ...dder_overriding_of_internal_fs_calls.patch | 2 +- ...ch_cppgc_heap_on_v8_isolate_creation.patch | 10 +- ..._on_wrapper-descriptor-based_cppheap.patch | 6 +- ...ted_fields_of_fastapicallbackoptions.patch | 4 +- .../node/support_v8_sandboxed_pointers.patch | 17 +- ...st_formally_mark_some_tests_as_flaky.patch | 2 +- 42 files changed, 359 insertions(+), 581 deletions(-) delete mode 100644 patches/node/build_use_third_party_simdutf.patch create mode 100644 patches/node/chore_remove_protocol_maybe_from_node_string.patch create mode 100644 patches/node/fix_-wmismatched-new-delete_in_debug_utils_cc.patch delete mode 100644 patches/node/linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch diff --git a/DEPS b/DEPS index 337dc8f77887d..493c1dfdf9b7c 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.177', 'node_version': - 'v22.16.0', + 'v22.17.0', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/lib/node/asar-fs-wrapper.ts b/lib/node/asar-fs-wrapper.ts index 6ecaa0ac785b5..90d256342a060 100644 --- a/lib/node/asar-fs-wrapper.ts +++ b/lib/node/asar-fs-wrapper.ts @@ -54,6 +54,10 @@ const { getDirent } = __non_webpack_require__('internal/fs/utils'); +const { + assignFunctionName +} = __non_webpack_require__('internal/util'); + const { validateBoolean, validateFunction @@ -235,7 +239,10 @@ const overrideAPI = function (module: Record, name: string, pathArg }; if (old[util.promisify.custom]) { - module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex); + module[name][util.promisify.custom] = assignFunctionName( + name, + makePromiseFunction(old[util.promisify.custom], pathArgumentIndex) + ); } if (module.promises && module.promises[name]) { @@ -1238,7 +1245,7 @@ export const wrapFsWithAsar = (fs: Record) => { // command as a single path to an archive. const { exec, execSync } = childProcess; childProcess.exec = invokeWithNoAsar(exec); - childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]); + childProcess.exec[util.promisify.custom] = assignFunctionName('exec', invokeWithNoAsar(exec[util.promisify.custom])); childProcess.execSync = invokeWithNoAsar(execSync); overrideAPI(childProcess, 'execFile'); diff --git a/patches/node/.patches b/patches/node/.patches index 80c88af3b6167..ecbe186e500f9 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -34,10 +34,8 @@ fix_remove_harmony-import-assertions_from_node_cc.patch chore_disable_deprecation_ftbfs_in_simdjson_header.patch build_allow_unbundling_of_node_js_dependencies.patch test_use_static_method_names_in_call_stacks.patch -build_use_third_party_simdutf.patch fix_remove_fastapitypedarray_usage.patch test_handle_explicit_resource_management_globals.patch -linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch @@ -48,3 +46,5 @@ cli_move_--trace-atomics-wait_to_eol.patch fix_cppgc_initializing_twice.patch fix_task_starvation_in_inspector_context_test.patch fix_expose_readfilesync_override_for_modules.patch +chore_remove_protocol_maybe_from_node_string.patch +fix_-wmismatched-new-delete_in_debug_utils_cc.patch diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 6f6e1a48a0662..28ee7c05fe8b0 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -10,8 +10,21 @@ however those files were cherry-picked from main branch and do not really in 20/21. We have to wait until 22 is released to be able to build with upstream GN files. +diff --git a/configure.py b/configure.py +index 4560bac7b8e3c707ecea5a425f642efb9de9ed36..e9c2a4391f4058a21a259cacaac4fde5d199288e 100755 +--- a/configure.py ++++ b/configure.py +@@ -1722,7 +1722,7 @@ def configure_v8(o, configs): + # Until we manage to get rid of all those, v8_enable_sandbox cannot be used. + # Note that enabling pointer compression without enabling sandbox is unsupported by V8, + # so this can be broken at any time. +- o['variables']['v8_enable_sandbox'] = 0 ++ o['variables']['v8_enable_sandbox'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 + o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 diff --git a/node.gni b/node.gni -index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3fd219be7d 100644 +index 35ccd0487f20cece033d58827ecb7ed016908ee4..b4450e3dd17994d1eaf59eb5cff5912545e89793 100644 --- a/node.gni +++ b/node.gni @@ -5,10 +5,10 @@ @@ -55,10 +68,10 @@ index a2123cc6c6d21c53fafc8934203b3720393e7b11..245a43920c7baf000ba63192a84a4c3f assert(!node_enable_inspector || node_use_openssl, diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index defb657a62a0316224a02b68505ac1142fd89d03..d637faac88875bfa110e2b8d1f53962061d98279 100644 +index 092341dbfbabe15b15ed43057d399f754505f6fd..f14b45850e42585f5686b7201e2b8281ed8c24e1 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -785,6 +785,7 @@ void BuiltinLoader::RegisterExternalReferences( +@@ -788,6 +788,7 @@ void BuiltinLoader::RegisterExternalReferences( registry->Register(GetNatives); RegisterExternalReferencesForInternalizedBuiltinCode(registry); @@ -80,10 +93,10 @@ index f9426599f2d5dc6ad061407f0c4eb2c9203a4433..302030f610965f07dd6998d282275c1b // Handles compilation and caching of built-in JavaScript modules and // bootstrap scripts, whose source are bundled into the binary as static data. diff --git a/tools/install.py b/tools/install.py -index 17515720ba9c85d533465365188021074a8d30f4..92f83a83be67aafc9ead6923b868dbb0de39db34 100755 +index 8797b59e59c85a8877b977fa3281e50165e6f6b2..0af01e075616195f38fb242626dcab770ec1eb57 100755 --- a/tools/install.py +++ b/tools/install.py -@@ -212,6 +212,7 @@ def headers(options, action): +@@ -222,6 +222,7 @@ def headers(options, action): 'include/cppgc/internal/caged-heap-local-data.h', 'include/cppgc/internal/caged-heap.h', 'include/cppgc/internal/compiler-specific.h', @@ -91,7 +104,7 @@ index 17515720ba9c85d533465365188021074a8d30f4..92f83a83be67aafc9ead6923b868dbb0 'include/cppgc/internal/finalizer-trait.h', 'include/cppgc/internal/gc-info.h', 'include/cppgc/internal/logging.h', -@@ -291,6 +292,7 @@ def headers(options, action): +@@ -301,6 +302,7 @@ def headers(options, action): 'include/v8-promise.h', 'include/v8-proxy.h', 'include/v8-regexp.h', diff --git a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch index 68cbb15ef5eaa..3054af71962d5 100644 --- a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch +++ b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shelley Vohr -Date: Wed, 12 Feb 2025 15:31:07 +0100 +Date: Thu, 26 Jun 2025 09:25:24 +0000 Subject: build: change crdtp::ProtocolTypeTraits signatures to avoid conflict After https://github.com/nodejs/node/pull/56649 we see conflicts with the @@ -14,7 +14,7 @@ error: duplicate symbol: crdtp::ProtocolTypeTraitstokenizer()->TokenTag() == cbor::CBORTokenTag::STRING8) { span cbor_span = state->tokenizer()->GetString8(); value->assign(reinterpret_cast(cbor_span.data()), -@@ -24,12 +25,13 @@ bool ProtocolTypeTraits::Deserialize(DeserializerState* state, +@@ -24,7 +25,8 @@ bool ProtocolTypeTraits::Deserialize(DeserializerState* state, } void ProtocolTypeTraits::Serialize(const std::string& value, @@ -36,18 +36,12 @@ index 6db4bee1072bfe911a4179c3edb2bbaf18f1a182..c603f95f1f93438bd55bce3ff7f5bb31 + void* extra) { cbor::EncodeString8(SpanFrom(value), bytes); } -- - } // namespace crdtp -+ - namespace node { - namespace inspector { - namespace protocol { diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index 38cf96e874dcc49cde87137b2737c35a84f418d0..b2f67c224acc7b3a3b867867e251a7c62833f46e 100644 +index 94ec9b2301998c4c5aad9ca3dae72ecf323fa0bb..a0d19a592d7bf9b00d6b98ef1ae931626ebb945c 100644 --- a/src/inspector/node_string.h +++ b/src/inspector/node_string.h -@@ -15,8 +15,8 @@ namespace crdtp { +@@ -19,8 +19,8 @@ namespace crdtp { template <> struct ProtocolTypeTraits { @@ -57,4 +51,4 @@ index 38cf96e874dcc49cde87137b2737c35a84f418d0..b2f67c224acc7b3a3b867867e251a7c6 + static void Serialize(const std::string& value, std::vector* bytes, void* extra = nullptr); }; - } // namespace crdtp + template <> diff --git a/patches/node/build_compile_with_c_20_support.patch b/patches/node/build_compile_with_c_20_support.patch index 10aec30f9fc6c..83862862dcd55 100644 --- a/patches/node/build_compile_with_c_20_support.patch +++ b/patches/node/build_compile_with_c_20_support.patch @@ -10,10 +10,10 @@ V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8 This can be removed when Electron upgrades to a version of Node.js containing the required V8 version. diff --git a/common.gypi b/common.gypi -index f3476a91e4c3cda7cecf49e07bb594a167ac46ef..de73f6c18131f43e6fe3107c866599aa3398cf10 100644 +index 03fefab4b0a9727925411b95310831ffdc33e8d9..f9b5e47f1d67807435529c99d12f419d0fd4269f 100644 --- a/common.gypi +++ b/common.gypi -@@ -530,7 +530,7 @@ +@@ -538,7 +538,7 @@ '-fno-rtti', '-fno-exceptions', '-fno-strict-aliasing', @@ -22,7 +22,7 @@ index f3476a91e4c3cda7cecf49e07bb594a167ac46ef..de73f6c18131f43e6fe3107c866599aa ], 'defines': [ '__STDC_FORMAT_MACROS' ], 'ldflags': [ '-rdynamic' ], -@@ -700,7 +700,7 @@ +@@ -708,7 +708,7 @@ ['clang==1', { 'xcode_settings': { 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0', diff --git a/patches/node/build_enable_perfetto.patch b/patches/node/build_enable_perfetto.patch index f0b97e2c4acae..1e2e8823fe0cb 100644 --- a/patches/node/build_enable_perfetto.patch +++ b/patches/node/build_enable_perfetto.patch @@ -64,10 +64,10 @@ index 251f51ec454f9cba4023b8b6729241ee753aac13..1de8cac6e3953ce9cab9db03530da327 module.exports = { diff --git a/node.gyp b/node.gyp -index ad010a8d99cf08013b7202eddce66e5b3885652d..d735b887d05ddfadec8e56dd8eae09646890aa84 100644 +index 0434887c363a586cbfa0438765fc8800d4237057..20fbf03cee24e66f9ad0d394dbcfa3ad03348890 100644 --- a/node.gyp +++ b/node.gyp -@@ -176,7 +176,6 @@ +@@ -175,7 +175,6 @@ 'src/timers.cc', 'src/timer_wrap.cc', 'src/tracing/agent.cc', @@ -75,7 +75,7 @@ index ad010a8d99cf08013b7202eddce66e5b3885652d..d735b887d05ddfadec8e56dd8eae0964 'src/tracing/node_trace_writer.cc', 'src/tracing/trace_event.cc', 'src/tracing/traced_value.cc', -@@ -305,7 +304,6 @@ +@@ -302,7 +301,6 @@ 'src/tcp_wrap.h', 'src/timers.h', 'src/tracing/agent.h', diff --git a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch index 0527e9476f670..259c40e0aeb53 100644 --- a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch +++ b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch @@ -7,10 +7,10 @@ Subject: build: ensure native module compilation fails if not using a new This should not be upstreamed, it is a quality-of-life patch for downstream module builders. diff --git a/common.gypi b/common.gypi -index d9c0b721fe0a629a30efb3c4e04905176ca0a7f5..f3476a91e4c3cda7cecf49e07bb594a167ac46ef 100644 +index bfe567e016cf102d2087f7647e64cc051116ab8d..03fefab4b0a9727925411b95310831ffdc33e8d9 100644 --- a/common.gypi +++ b/common.gypi -@@ -88,6 +88,8 @@ +@@ -89,6 +89,8 @@ 'v8_use_perfetto': 0, 'tsan%': 0, @@ -19,15 +19,17 @@ index d9c0b721fe0a629a30efb3c4e04905176ca0a7f5..f3476a91e4c3cda7cecf49e07bb594a1 ##### end V8 defaults ##### # When building native modules using 'npm install' with the system npm, -@@ -293,6 +295,7 @@ - # Defines these mostly for node-gyp to pickup. - 'defines': [ +@@ -297,7 +299,8 @@ '_GLIBCXX_USE_CXX11_ABI=1', + # This help forks when building Node.js on a 32-bit arch as + # libuv is always compiled with _FILE_OFFSET_BITS=64 +- '_FILE_OFFSET_BITS=64' ++ '_FILE_OFFSET_BITS=64', + 'ELECTRON_ENSURE_CONFIG_GYPI', ], # Forcibly disable -Werror. We support a wide range of compilers, it's -@@ -449,6 +452,11 @@ +@@ -454,6 +457,11 @@ }], ], }], @@ -40,10 +42,10 @@ index d9c0b721fe0a629a30efb3c4e04905176ca0a7f5..f3476a91e4c3cda7cecf49e07bb594a1 # list in v8/BUILD.gn. ['v8_enable_v8_checks == 1', { diff --git a/configure.py b/configure.py -index 932484674e5b15b765b8bfe307bdf99b49b5039f..befaa85527b9ebebad226e603586e23d04ec1e51 100755 +index e9c2a4391f4058a21a259cacaac4fde5d199288e..7821a0d3a7179a9e7fa9e48a062c2b0e7705ca6f 100755 --- a/configure.py +++ b/configure.py -@@ -1698,6 +1698,7 @@ def configure_library(lib, output, pkgname=None): +@@ -1704,6 +1704,7 @@ def configure_library(lib, output, pkgname=None): def configure_v8(o, configs): set_configuration_variable(configs, 'v8_enable_v8_checks', release=1, debug=0) diff --git a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch index c4c54c21fdfe0..025e06ca2388a 100644 --- a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch +++ b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch @@ -34,7 +34,7 @@ index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b let kResistStopPropagation; diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index d637faac88875bfa110e2b8d1f53962061d98279..e0b58c4d0ac5640a677c22d710f88f1b318378d7 100644 +index f14b45850e42585f5686b7201e2b8281ed8c24e1..915b8cba6d512096e6090272ab3fbc63d5c61ce8 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -35,6 +35,7 @@ using v8::Value; diff --git a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch index 8e3aa3dbea37a..5b00462181125 100644 --- a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch +++ b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch @@ -25,32 +25,6 @@ index 203b4abbc44df9e58083c819f61f9025104abdc6..73bf3839866a2652ca660f1117e8f249 # The NODE_MODULE_VERSION defined in node_version.h. node_module_version = exec_script("$node_path/tools/getmoduleversion.py", [], "value") -diff --git a/src/inspector/node_json.cc b/src/inspector/node_json.cc -index d8aacbdf1a8fc858c792ad3ce17ca2f46baebe7e..4625008c048532c2c3340130670647d2877430bd 100644 ---- a/src/inspector/node_json.cc -+++ b/src/inspector/node_json.cc -@@ -72,7 +72,7 @@ class ValueParserHandler : public ParserHandler { - - void HandleBinary(span bytes) override { - AddValueToParent( -- BinaryValue::create(Binary::fromSpan(bytes.data(), bytes.size()))); -+ BinaryValue::create(Binary::fromSpan(bytes))); - } - - void HandleDouble(double value) override { -diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index b2f67c224acc7b3a3b867867e251a7c62833f46e..33e93ce5bf7dda7e30b7b1b198ff3b53ccfac22a 100644 ---- a/src/inspector/node_string.h -+++ b/src/inspector/node_string.h -@@ -66,7 +66,7 @@ class Binary { - static Binary fromBase64(const std::string_view base64, bool* success) { - UNREACHABLE(); - } -- static Binary fromSpan(const uint8_t* data, size_t size) { UNREACHABLE(); } -+ static Binary fromSpan(crdtp::span data) { UNREACHABLE(); } - }; - - } // namespace protocol diff --git a/src/inspector/unofficial.gni b/src/inspector/unofficial.gni index 3d7aa148678b2646b88fa7c32abec91791b02b82..4810d93eb971b253f7dadff7011a632f6dbe6a2b 100644 --- a/src/inspector/unofficial.gni diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index 73e7e840edf73..d7262f8fc5c5a 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,10 +11,10 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index de73f6c18131f43e6fe3107c866599aa3398cf10..e2171e14b9e29dfc3c629f8164545d56d5e9057e 100644 +index f9b5e47f1d67807435529c99d12f419d0fd4269f..c99f583d3674347dd6eb9a5eea1ed5588e376d31 100644 --- a/common.gypi +++ b/common.gypi -@@ -127,6 +127,7 @@ +@@ -128,6 +128,7 @@ 'v8_base': '<(PRODUCT_DIR)/obj.target/tools/v8_gypfiles/libv8_snapshot.a', }], ['OS=="mac"', { diff --git a/patches/node/build_use_third_party_simdutf.patch b/patches/node/build_use_third_party_simdutf.patch deleted file mode 100644 index 07d50bf3a9e44..0000000000000 --- a/patches/node/build_use_third_party_simdutf.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Charles Kerr -Date: Mon, 9 Dec 2024 11:18:51 -0600 -Subject: build: use third_party/simdutf - -use the Chromium version of simdutf to avoid duplicate symbols - -diff --git a/node.gni b/node.gni -index 56a554175b805c1703f13d62041f8c80d6e94dd9..203b4abbc44df9e58083c819f61f9025104abdc6 100644 ---- a/node.gni -+++ b/node.gni -@@ -14,7 +14,7 @@ declare_args() { - node_openssl_path = "//third_party/boringssl" - - # The location of simdutf - use the one from node's deps by default. -- node_simdutf_path = "$node_path/deps/simdutf" -+ node_simdutf_path = "//third_party/simdutf" - - # The NODE_MODULE_VERSION defined in node_version.h. - node_module_version = exec_script("$node_path/tools/getmoduleversion.py", [], "value") diff --git a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch index a51aa2f27749d..0bde5d3fc9c81 100644 --- a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch +++ b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch @@ -11,10 +11,10 @@ Without this patch, building with simdjson fails with This patch can be removed once this is fixed upstream in simdjson. diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h -index c1535ee81300b9cb93eb9ee6e769246793f936c3..3350287401e181e1d4ee432b8bd16081d0d7a73e 100644 +index a0d449975224a3e0db5c05de79b290763d6e390c..e77e47f972b4609e38aa8b68ab0d81ed1575effb 100644 --- a/deps/simdjson/simdjson.h +++ b/deps/simdjson/simdjson.h -@@ -3837,12 +3837,17 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result padded_string::load(std::string_view filen +@@ -4273,6 +4278,9 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson @@ -42,7 +42,7 @@ index c1535ee81300b9cb93eb9ee6e769246793f936c3..3350287401e181e1d4ee432b8bd16081 inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } -@@ -4250,6 +4258,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len +@@ -4281,6 +4289,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len return simdjson::padded_string(reinterpret_cast(str), len); } #endif diff --git a/patches/node/chore_expose_importmoduledynamically_and.patch b/patches/node/chore_expose_importmoduledynamically_and.patch index 5e9f79e6ff54d..497ea5842d984 100644 --- a/patches/node/chore_expose_importmoduledynamically_and.patch +++ b/patches/node/chore_expose_importmoduledynamically_and.patch @@ -11,7 +11,7 @@ its own blended handler between Node and Blink. Not upstreamable. diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js -index fd17ce8695c55f8f38ed19d59960acc1a7692bc8..96754db3277b3a0445b69275368602166c6d5423 100644 +index 9d6f850f667c5186efe6855bc3d5f5af332bdaa7..8521759e20adf53024e5893dbf3cb36e1752085e 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -30,7 +30,7 @@ const { @@ -40,10 +40,10 @@ index fd17ce8695c55f8f38ed19d59960acc1a7692bc8..96754db3277b3a0445b6927536860216 /** diff --git a/src/module_wrap.cc b/src/module_wrap.cc -index 912acc8da815405531d8b527383f19c3731be100..8d48f4693c3b5f0d1d94d3edadc48c4f36d1bf97 100644 +index cdd0ba00eb0cafbc79b816017423f9021ca2979d..6916497f6feb14e482cf5080b57d639ae7292d20 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc -@@ -858,7 +858,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( +@@ -875,7 +875,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( return module->module_.Get(isolate); } @@ -52,7 +52,7 @@ index 912acc8da815405531d8b527383f19c3731be100..8d48f4693c3b5f0d1d94d3edadc48c4f Local context, Local host_defined_options, Local resource_name, -@@ -923,12 +923,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( +@@ -947,12 +947,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( Realm* realm = Realm::GetCurrent(args); HandleScope handle_scope(isolate); @@ -68,7 +68,7 @@ index 912acc8da815405531d8b527383f19c3731be100..8d48f4693c3b5f0d1d94d3edadc48c4f } void ModuleWrap::HostInitializeImportMetaObjectCallback( -@@ -970,13 +971,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( +@@ -994,13 +995,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( Realm* realm = Realm::GetCurrent(args); Isolate* isolate = realm->isolate(); @@ -87,7 +87,7 @@ index 912acc8da815405531d8b527383f19c3731be100..8d48f4693c3b5f0d1d94d3edadc48c4f MaybeLocal ModuleWrap::SyntheticModuleEvaluationStepsCallback( diff --git a/src/module_wrap.h b/src/module_wrap.h -index 83b5793013cbc453cf92c0a006fc7be3c06ad276..90353954bc497cb4ae413dc134850f8abb4efc7c 100644 +index ef4dfd1d6b091d2b0f71b946904a47415b6435ba..862f946a75f2a2949d7eeb7f97e96289beab8078 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -8,6 +8,7 @@ @@ -123,7 +123,7 @@ index 83b5793013cbc453cf92c0a006fc7be3c06ad276..90353954bc497cb4ae413dc134850f8a private: ModuleWrap(Realm* realm, v8::Local object, -@@ -129,7 +139,6 @@ class ModuleWrap : public BaseObject { +@@ -130,7 +140,6 @@ class ModuleWrap : public BaseObject { v8::Local specifier, v8::Local import_attributes, v8::Local referrer); diff --git a/patches/node/chore_remove_protocol_maybe_from_node_string.patch b/patches/node/chore_remove_protocol_maybe_from_node_string.patch new file mode 100644 index 0000000000000..12352945185c9 --- /dev/null +++ b/patches/node/chore_remove_protocol_maybe_from_node_string.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Thu, 26 Jun 2025 09:20:43 +0000 +Subject: chore: remove protocol::Maybe from node_string + +It was removed upstream in https://chromium-review.googlesource.com/c/chromium/src/+/6049967. + +This should be upstreamed. + +diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h +index a0d19a592d7bf9b00d6b98ef1ae931626ebb945c..ddedca4a5b9b35258050f8b4cb446ceeba956896 100644 +--- a/src/inspector/node_string.h ++++ b/src/inspector/node_string.h +@@ -6,7 +6,6 @@ + #include + #include + #include +-#include "crdtp/maybe.h" + #include "crdtp/protocol_core.h" + #include "util.h" + #include "v8-inspector.h" +@@ -31,11 +30,6 @@ struct ProtocolTypeTraits { + std::vector* bytes); + }; + +-template <> +-struct detail::MaybeTypedef { +- typedef ValueMaybe type; +-}; +- + } // namespace crdtp + + namespace node { diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch index 6eec17ed7bf01..681ea40add6e0 100644 --- a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -15,10 +15,10 @@ Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 121d8f2bbd2b1d93067a06a902b1e7b986bcdb49..3460ad33c6186dcc3aa3281d80b723a1cc1d50dd 100644 +index d924287df3ca29681cf71e2fbd402314ce8edd97..f2f4d25a838b9758234cd667b0fb537d0d0fcced 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3367,7 +3367,6 @@ one is included in the list below. +@@ -3386,7 +3386,6 @@ one is included in the list below. * `--tls-min-v1.1` * `--tls-min-v1.2` * `--tls-min-v1.3` @@ -43,7 +43,7 @@ index 663d123ac728f097e8a76c94cf10c53d059983d7..497f5a61182beafbaa26b94518105635 Print stack traces for deprecations. . diff --git a/src/node.cc b/src/node.cc -index 6f8f6386d0db8aef1e2e0126cc9064101cbe6112..bc670a6c8b5027417cdc35e1cd94a60f63fd342d 100644 +index 0c2a4d344c991c2ca0d9d90934cf7921abf2a629..19d9fb77f1aaf003e43b7d7016f45e6c35df06b3 100644 --- a/src/node.cc +++ b/src/node.cc @@ -232,44 +232,6 @@ void Environment::WaitForInspectorFrontendByOptions() { @@ -110,10 +110,10 @@ index 6f8f6386d0db8aef1e2e0126cc9064101cbe6112..bc670a6c8b5027417cdc35e1cd94a60f isolate_->SetPromiseHook(TracePromises); } diff --git a/src/node_options.cc b/src/node_options.cc -index a9500716f2a955fc591628a969c5fba40783a2e7..b153d2c6a771e80bcdf5ed6adbc1cd225b3bf97e 100644 +index 9829b0b2b2d013b93ac14d3ec6d46c35abcc4635..5fb2e713a50185b997935cb15fddd7b8b65a5b82 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -770,10 +770,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { +@@ -773,10 +773,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "throw an exception on deprecations", &EnvironmentOptions::throw_deprecation, kAllowedInEnvvar); @@ -125,10 +125,10 @@ index a9500716f2a955fc591628a969c5fba40783a2e7..b153d2c6a771e80bcdf5ed6adbc1cd22 "show stack traces on deprecations", &EnvironmentOptions::trace_deprecation, diff --git a/src/node_options.h b/src/node_options.h -index 60068b008b2e2a034c3f0c58b947a8d04d55e3b2..d821bc6a9adf28ea312a9c446f8acfc8ed586ae3 100644 +index eb18fdd617fd19e5b97cd67f351e70c28fee3e75..216c0f92167bd131e5ef2ea96ad47425ff51c3f7 100644 --- a/src/node_options.h +++ b/src/node_options.h -@@ -203,7 +203,6 @@ class EnvironmentOptions : public Options { +@@ -204,7 +204,6 @@ class EnvironmentOptions : public Options { std::vector coverage_include_pattern; std::vector coverage_exclude_pattern; bool throw_deprecation = false; diff --git a/patches/node/cli_remove_deprecated_v8_flag.patch b/patches/node/cli_remove_deprecated_v8_flag.patch index 70a46ac99e195..f4525f0ae0d24 100644 --- a/patches/node/cli_remove_deprecated_v8_flag.patch +++ b/patches/node/cli_remove_deprecated_v8_flag.patch @@ -18,10 +18,10 @@ Reviewed-By: Michaël Zasso Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index 6f984926a62973ba36bd3c27cc39b01f2bcac819..121d8f2bbd2b1d93067a06a902b1e7b986bcdb49 100644 +index cc311472678108f21eed70281e91b0d40c5fe7b6..d924287df3ca29681cf71e2fbd402314ce8edd97 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3404,7 +3404,6 @@ V8 options that are allowed are: +@@ -3423,7 +3423,6 @@ V8 options that are allowed are: * `--disallow-code-generation-from-strings` * `--enable-etw-stack-walking` * `--expose-gc` @@ -30,10 +30,10 @@ index 6f984926a62973ba36bd3c27cc39b01f2bcac819..121d8f2bbd2b1d93067a06a902b1e7b9 * `--jitless` * `--max-old-space-size` diff --git a/src/node_options.cc b/src/node_options.cc -index bb1e80ece4158dfed1b8bab7dc6d00dd56505aac..a9500716f2a955fc591628a969c5fba40783a2e7 100644 +index d2e945b1d6ef6729709cc73c238cfae46d4e56b6..9829b0b2b2d013b93ac14d3ec6d46c35abcc4635 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -992,11 +992,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( +@@ -995,11 +995,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( "disallow eval and friends", V8Option{}, kAllowedInEnvvar); diff --git a/patches/node/enable_crashpad_linux_node_processes.patch b/patches/node/enable_crashpad_linux_node_processes.patch index 4204092a3ea56..158a8bdeff2da 100644 --- a/patches/node/enable_crashpad_linux_node_processes.patch +++ b/patches/node/enable_crashpad_linux_node_processes.patch @@ -8,10 +8,10 @@ to child processes spawned with `ELECTRON_RUN_AS_NODE` which is used by the crashpad client to connect with the handler process. diff --git a/lib/child_process.js b/lib/child_process.js -index bb27670112c1ea42c7ff00883fe4b684544d9cd4..4d4da798ce59ce42e42d1f05fccf07699c033d46 100644 +index e848b3d5ee9b13ea3ea303eb3b57ef47ef951580..5b89c420f1a917526ab311fed52bff01637a86fe 100644 --- a/lib/child_process.js +++ b/lib/child_process.js -@@ -61,6 +61,7 @@ let debug = require('internal/util/debuglog').debuglog( +@@ -62,6 +62,7 @@ let debug = require('internal/util/debuglog').debuglog( ); const { Buffer } = require('buffer'); const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap'); @@ -19,7 +19,7 @@ index bb27670112c1ea42c7ff00883fe4b684544d9cd4..4d4da798ce59ce42e42d1f05fccf0769 const { AbortError, -@@ -153,7 +154,6 @@ function fork(modulePath, args = [], options) { +@@ -154,7 +155,6 @@ function fork(modulePath, args = [], options) { ArrayPrototypeSplice(execArgv, index - 1, 2); } } @@ -27,7 +27,7 @@ index bb27670112c1ea42c7ff00883fe4b684544d9cd4..4d4da798ce59ce42e42d1f05fccf0769 args = [...execArgv, modulePath, ...args]; if (typeof options.stdio === 'string') { -@@ -609,6 +609,22 @@ function normalizeSpawnArguments(file, args, options) { +@@ -610,6 +610,22 @@ function normalizeSpawnArguments(file, args, options) { 'options.windowsVerbatimArguments'); } @@ -50,7 +50,7 @@ index bb27670112c1ea42c7ff00883fe4b684544d9cd4..4d4da798ce59ce42e42d1f05fccf0769 if (options.shell) { validateArgumentNullCheck(options.shell, 'options.shell'); const command = ArrayPrototypeJoin([file, ...args], ' '); -@@ -642,7 +658,6 @@ function normalizeSpawnArguments(file, args, options) { +@@ -643,7 +659,6 @@ function normalizeSpawnArguments(file, args, options) { ArrayPrototypeUnshift(args, file); } diff --git a/patches/node/expose_get_builtin_module_function.patch b/patches/node/expose_get_builtin_module_function.patch index bee428c167591..8b95ccbe3290c 100644 --- a/patches/node/expose_get_builtin_module_function.patch +++ b/patches/node/expose_get_builtin_module_function.patch @@ -9,10 +9,10 @@ modules to sandboxed renderers. TODO(codebytere): remove and replace with a public facing API. diff --git a/src/node_binding.cc b/src/node_binding.cc -index 6c337232149ccb8bdb1188e72d59a052b1cb79c1..44ad4a480a33a2c39494a7d961318270a25620d8 100644 +index aa4213c3622eab077fa8d764775c1f95c6313e34..11f722d2d7c21079cbc65033429086231a786ca7 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc -@@ -653,6 +653,10 @@ void GetInternalBinding(const FunctionCallbackInfo& args) { +@@ -652,6 +652,10 @@ void GetInternalBinding(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(exports); } @@ -24,10 +24,10 @@ index 6c337232149ccb8bdb1188e72d59a052b1cb79c1..44ad4a480a33a2c39494a7d961318270 Environment* env = Environment::GetCurrent(args); diff --git a/src/node_binding.h b/src/node_binding.h -index eb1364cb01a2bea52bce768056e73b0f3a86ae35..d421a2773403e7b22fcca2fcf8275ef2d9654c55 100644 +index 611f38ef5e21cc303127326d50c648fbb557b4da..3d95ad2733dc83d0b7d37d57c337732c8a0e83d7 100644 --- a/src/node_binding.h +++ b/src/node_binding.h -@@ -146,6 +146,8 @@ void GetInternalBinding(const v8::FunctionCallbackInfo& args); +@@ -154,6 +154,8 @@ void GetInternalBinding(const v8::FunctionCallbackInfo& args); void GetLinkedBinding(const v8::FunctionCallbackInfo& args); void DLOpen(const v8::FunctionCallbackInfo& args); diff --git a/patches/node/feat_add_uv_loop_interrupt_on_io_change_option_to_uv_loop_configure.patch b/patches/node/feat_add_uv_loop_interrupt_on_io_change_option_to_uv_loop_configure.patch index 5fd144ea0a870..32992b2832354 100644 --- a/patches/node/feat_add_uv_loop_interrupt_on_io_change_option_to_uv_loop_configure.patch +++ b/patches/node/feat_add_uv_loop_interrupt_on_io_change_option_to_uv_loop_configure.patch @@ -23,10 +23,10 @@ index d1f41e1c9f44838410326df23b1b175fa16ba199..dcf69093469b611a6f9db2bb84530456 asynchronous file system operations. diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h -index 9e450c5110fe57117b686bf683cc6631f37efaeb..f75a496071ac3396cbc6dec819eaab7294609deb 100644 +index 938e998fdc54d17c8d0d4b1949087b968044829f..0295eeddbb4eb1cffd9dbcd02e037907fadc1b7e 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h -@@ -261,6 +261,7 @@ typedef struct uv_metrics_s uv_metrics_t; +@@ -263,6 +263,7 @@ typedef struct uv_metrics_s uv_metrics_t; typedef enum { UV_LOOP_BLOCK_SIGNAL = 0, UV_METRICS_IDLE_TIME, @@ -35,18 +35,27 @@ index 9e450c5110fe57117b686bf683cc6631f37efaeb..f75a496071ac3396cbc6dec819eaab72 #define UV_LOOP_USE_IO_URING_SQPOLL UV_LOOP_USE_IO_URING_SQPOLL } uv_loop_option; diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c -index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..117190ef26338944b78dbed7380c631de8057223 100644 +index 538ae7876f2b2463716459b179d74843383295be..6ce53f12834c7a34241ea0865bda99af0541ea5f 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c -@@ -38,7 +38,6 @@ - #include +@@ -40,7 +40,7 @@ + + #if UV__KQUEUE_EVFILT_USER + static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT; +-static int kqueue_evfilt_user_support = 1; ++int kqueue_evfilt_user_support = 1; + + + static void uv__kqueue_runtime_detection(void) { +@@ -66,7 +66,6 @@ static void uv__kqueue_runtime_detection(void) { + } #endif -static void uv__async_send(uv_loop_t* loop); static int uv__async_start(uv_loop_t* loop); static void uv__cpu_relax(void); -@@ -78,7 +77,7 @@ int uv_async_send(uv_async_t* handle) { +@@ -106,7 +105,7 @@ int uv_async_send(uv_async_t* handle) { /* Wake up the other thread's event loop. */ if (atomic_exchange(pending, 1) == 0) @@ -55,7 +64,7 @@ index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..117190ef26338944b78dbed7380c631d /* Set the loop to not-busy. */ atomic_fetch_add(busy, -1); -@@ -178,39 +177,6 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +@@ -210,50 +209,6 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { } @@ -76,6 +85,17 @@ index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..117190ef26338944b78dbed7380c631d - len = sizeof(val); - fd = loop->async_io_watcher.fd; /* eventfd */ - } +-#elif UV__KQUEUE_EVFILT_USER +- struct kevent ev; +- +- if (kqueue_evfilt_user_support) { +- fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */ +- EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); +- r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); +- if (r == 0) +- return; +- abort(); +- } -#endif - - do @@ -96,20 +116,20 @@ index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..117190ef26338944b78dbed7380c631d static int uv__async_start(uv_loop_t* loop) { int pipefd[2]; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c -index 0c52ccf2ad7b2dcae77a7bc4b3af9d1a1346ce18..13cd33a7d3031c5e19c9418a18217d1e4158c82e 100644 +index bd51b69b8120e878f3342c2812fb1f4c0fd35071..4c005360c8d83955b727bde3c0ff0bc3e32061a4 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c -@@ -937,6 +937,9 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { - loop->watchers[w->fd] = w; +@@ -944,6 +944,9 @@ int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { loop->nfds++; } -+ + + if (uv__get_internal_fields(loop)->flags & UV_LOOP_INTERRUPT_ON_IO_CHANGE) + uv__loop_interrupt(loop); ++ + return 0; } - -@@ -968,6 +971,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +@@ -993,6 +996,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { } else if (uv__queue_empty(&w->watcher_queue)) uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); @@ -119,7 +139,7 @@ index 0c52ccf2ad7b2dcae77a7bc4b3af9d1a1346ce18..13cd33a7d3031c5e19c9418a18217d1e } -@@ -984,6 +990,9 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { +@@ -1009,6 +1015,9 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { if (uv__queue_empty(&w->pending_queue)) uv__queue_insert_tail(&loop->pending_queue, &w->pending_queue); @@ -129,11 +149,24 @@ index 0c52ccf2ad7b2dcae77a7bc4b3af9d1a1346ce18..13cd33a7d3031c5e19c9418a18217d1e } +diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h +index a1d7d4366308acb6ba8d2b84b7a2be4b89abe2ce..a86505eba1243a8c89d77a1d036d16ac8313a785 100644 +--- a/deps/uv/src/unix/internal.h ++++ b/deps/uv/src/unix/internal.h +@@ -523,6 +523,8 @@ int uv__get_constrained_cpu(long long* quota); + * people will be directed here if this number gets printed due to some + * kqueue error and they google for help. */ + #define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711 ++/* Global variable to cache kqueue EVFILT_USER support detection result */ ++extern int kqueue_evfilt_user_support; + #else + #define UV__KQUEUE_EVFILT_USER 0 + #endif diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c -index 179ee999d8052e779dc692aeb5b673d210aaa997..03cca2c491015e5ef958f61a0167d29dfc56e247 100644 +index 5d3f0c7a348b334e5651bf8682fe29b355ec0cb9..d0b8686783d009365e89731366afee1878a6757b 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c -@@ -224,6 +224,10 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { +@@ -228,6 +228,10 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { } #endif @@ -144,7 +177,7 @@ index 179ee999d8052e779dc692aeb5b673d210aaa997..03cca2c491015e5ef958f61a0167d29d if (option != UV_LOOP_BLOCK_SIGNAL) return UV_ENOSYS; -@@ -234,3 +238,40 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { +@@ -238,3 +242,51 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { loop->flags |= UV_LOOP_BLOCK_SIGPROF; return 0; } @@ -167,6 +200,17 @@ index 179ee999d8052e779dc692aeb5b673d210aaa997..03cca2c491015e5ef958f61a0167d29d + len = sizeof(val); + fd = loop->async_io_watcher.fd; /* eventfd */ + } ++#elif UV__KQUEUE_EVFILT_USER ++ struct kevent ev; ++ ++ if (kqueue_evfilt_user_support) { ++ fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */ ++ EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); ++ r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); ++ if (r == 0) ++ return; ++ abort(); ++ } +#endif + + do @@ -186,10 +230,10 @@ index 179ee999d8052e779dc692aeb5b673d210aaa997..03cca2c491015e5ef958f61a0167d29d + abort(); +} diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h -index 4baede2e506ee1787d554a0ec75bc9eb346fc8f2..385d4f420b50bfd2dc23f119d535c0442a3ce4e7 100644 +index b9a8e976eefdd62cfad31f6d3ed9b6179776eaa4..985b8fa3f2594f729af1845334b4afade3e054ee 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h -@@ -144,6 +144,8 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); +@@ -149,6 +149,8 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); @@ -198,7 +242,7 @@ index 4baede2e506ee1787d554a0ec75bc9eb346fc8f2..385d4f420b50bfd2dc23f119d535c044 int uv__read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb); -@@ -280,6 +282,10 @@ void uv__threadpool_cleanup(void); +@@ -291,6 +293,10 @@ void uv__threadpool_cleanup(void); if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \ (h)->flags |= UV_HANDLE_ACTIVE; \ if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h); \ @@ -210,7 +254,7 @@ index 4baede2e506ee1787d554a0ec75bc9eb346fc8f2..385d4f420b50bfd2dc23f119d535c044 while (0) diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c -index e9885a0f1ff3890a8d957c8793e22b01cedc0e97..ae3d09878253fe7169ad7b74b3faea0223f89de5 100644 +index 5f41c87ad5ed137c51b43e4941414c89ddff5216..73f26c68e5734d340fe210b4418f0161dc172182 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -384,10 +384,20 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { @@ -459,10 +503,10 @@ index 6e9917239aa5626dd56fffd6eb2469d3e63224bf..b0da9d1cddc69428e9fb3379d1338cf8 MAKE_VALGRIND_HAPPY(loop); return 0; diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h -index e07bd61ecf73c122a553d5d8232a7478980751a5..21cf8c09edac15ba5ea010d54d3e158e0d1b7e5b 100644 +index 0dea544699d75fcf11d4a8b6a7f0d5f4242f892a..6f8c70bc8b6cb2d2ffc1f63fe60920f8fb8a1abe 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h -@@ -279,6 +279,7 @@ TEST_DECLARE (process_priority) +@@ -281,6 +281,7 @@ TEST_DECLARE (process_priority) TEST_DECLARE (has_ref) TEST_DECLARE (active) TEST_DECLARE (embed) @@ -470,7 +514,7 @@ index e07bd61ecf73c122a553d5d8232a7478980751a5..21cf8c09edac15ba5ea010d54d3e158e TEST_DECLARE (async) TEST_DECLARE (async_null_cb) TEST_DECLARE (eintr_handling) -@@ -919,6 +920,7 @@ TASK_LIST_START +@@ -929,6 +930,7 @@ TASK_LIST_START TEST_ENTRY (active) TEST_ENTRY (embed) diff --git a/patches/node/fix_-wmismatched-new-delete_in_debug_utils_cc.patch b/patches/node/fix_-wmismatched-new-delete_in_debug_utils_cc.patch new file mode 100644 index 0000000000000..f06d854cdddb4 --- /dev/null +++ b/patches/node/fix_-wmismatched-new-delete_in_debug_utils_cc.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Thu, 26 Jun 2025 12:46:41 +0000 +Subject: fix: -Wmismatched-new-delete in debug_utils.cc + +Fixes the following error: + +Error: ../../third_party/electron_node/src/debug_utils.cc(491,11): error: 'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'? [-Werror,-Wmismatched-new-delete] + 491 | delete str; + | ^ + | [] +../../third_party/electron_node/src/debug_utils.cc(487,23): note: allocated with 'new[]' here + 487 | char* str = new char[size]; + | ^ +1 error generated. + +Upstreamed in https://github.com/nodejs/node/pull/58844. + +diff --git a/src/debug_utils.cc b/src/debug_utils.cc +index 97884b9a0f8d6d88e36da06b05642505c767338d..65283ef31da35d0f08fcff7a5e79bf960d861126 100644 +--- a/src/debug_utils.cc ++++ b/src/debug_utils.cc +@@ -488,7 +488,7 @@ std::vector NativeSymbolDebuggingContext::GetLoadedLibraries() { + WideCharToMultiByte( + CP_UTF8, 0, module_name, -1, str, size, nullptr, nullptr); + list.emplace_back(str); +- delete str; ++ delete[] str; + } + } + } diff --git a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch index f21f31edd6765..e2f7f19bc3655 100644 --- a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch +++ b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch @@ -7,10 +7,10 @@ common.gypi is a file that's included in the node header bundle, despite the fact that we do not build node with gyp. diff --git a/common.gypi b/common.gypi -index 393fe32765794fbc5e92690e968e3c18f0d749fa..d9c0b721fe0a629a30efb3c4e04905176ca0a7f5 100644 +index a73d4401f26d8493802d3ecec3e015a77717720a..bfe567e016cf102d2087f7647e64cc051116ab8d 100644 --- a/common.gypi +++ b/common.gypi -@@ -90,6 +90,23 @@ +@@ -91,6 +91,23 @@ ##### end V8 defaults ##### diff --git a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch index 6ca8291f2b3e6..ffd52bf513310 100644 --- a/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch +++ b/patches/node/fix_allow_passing_fileexists_fn_to_legacymainresolve.patch @@ -53,10 +53,10 @@ index 2879e5cf541fb4d226cfd7cc0fe367ca448fb926..03082f0ec4f91382933eec48e77331cd const maybeMain = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? packageConfig.main || './' : ''; diff --git a/src/node_file.cc b/src/node_file.cc -index 49816349d8bab37fea1d84e5326ee5a11acad7a2..5aea65d6800494def62829432ee48f9c06e65d63 100644 +index fe28c340c9b50384e79fe14263d8b3807f49e0b3..37172158d318d6569194fd3c5441d107e155e54c 100644 --- a/src/node_file.cc +++ b/src/node_file.cc -@@ -3237,13 +3237,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { +@@ -3523,13 +3523,25 @@ static void CpSyncCopyDir(const FunctionCallbackInfo& args) { } BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile( @@ -83,7 +83,7 @@ index 49816349d8bab37fea1d84e5326ee5a11acad7a2..5aea65d6800494def62829432ee48f9c uv_fs_t req; int rc = uv_fs_stat(env->event_loop(), &req, file_path.c_str(), nullptr); -@@ -3301,6 +3313,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3587,6 +3599,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { std::optional initial_file_path; std::string file_path; @@ -95,7 +95,7 @@ index 49816349d8bab37fea1d84e5326ee5a11acad7a2..5aea65d6800494def62829432ee48f9c if (args.Length() >= 2 && args[1]->IsString()) { auto package_config_main = Utf8Value(isolate, args[1]).ToString(); -@@ -3321,7 +3338,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3607,7 +3624,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { BufferValue buff_file_path(isolate, local_file_path); ToNamespacedPath(env, &buff_file_path); @@ -104,7 +104,7 @@ index 49816349d8bab37fea1d84e5326ee5a11acad7a2..5aea65d6800494def62829432ee48f9c case BindingData::FilePathIsFileReturnType::kIsFile: return args.GetReturnValue().Set(i); case BindingData::FilePathIsFileReturnType::kIsNotFile: -@@ -3358,7 +3375,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { +@@ -3644,7 +3661,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo& args) { BufferValue buff_file_path(isolate, local_file_path); ToNamespacedPath(env, &buff_file_path); diff --git a/patches/node/fix_assert_module_in_the_renderer_process.patch b/patches/node/fix_assert_module_in_the_renderer_process.patch index 63191d23c70b6..3ab6d454f6e8c 100644 --- a/patches/node/fix_assert_module_in_the_renderer_process.patch +++ b/patches/node/fix_assert_module_in_the_renderer_process.patch @@ -44,10 +44,10 @@ index 59b5a16f1309a5e4055bccfdb7a529045ad30402..bfdaf6211466a01b64b7942f7b16c480 let filename = call.getFileName(); const line = call.getLineNumber() - 1; diff --git a/src/node_options.cc b/src/node_options.cc -index 8be78889e8234eb3100f309829bf7470db544dcd..bb1e80ece4158dfed1b8bab7dc6d00dd56505aac 100644 +index 14f7764c995e8de6582faf58c9b98a9cbe4fab73..d2e945b1d6ef6729709cc73c238cfae46d4e56b6 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -1557,14 +1557,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { +@@ -1560,14 +1560,16 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { } Isolate* isolate = args.GetIsolate(); diff --git a/patches/node/fix_cppgc_initializing_twice.patch b/patches/node/fix_cppgc_initializing_twice.patch index 432dce585764e..475bd2830be6a 100644 --- a/patches/node/fix_cppgc_initializing_twice.patch +++ b/patches/node/fix_cppgc_initializing_twice.patch @@ -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 bc670a6c8b5027417cdc35e1cd94a60f63fd342d..49a9670de72c222d6b4a681be001efb4a2651ea3 100644 +index 19d9fb77f1aaf003e43b7d7016f45e6c35df06b3..9fad3198757ce639eb491eb628c6264a17002bf2 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -1209,7 +1209,7 @@ InitializeOncePerProcessInternal(const std::vector& args, +@@ -1208,7 +1208,7 @@ InitializeOncePerProcessInternal(const std::vector& args, result->platform_ = per_process::v8_platform.Platform(); } diff --git a/patches/node/fix_crypto_tests_to_run_with_bssl.patch b/patches/node/fix_crypto_tests_to_run_with_bssl.patch index 5e27975c22151..a46f89cb41230 100644 --- a/patches/node/fix_crypto_tests_to_run_with_bssl.patch +++ b/patches/node/fix_crypto_tests_to_run_with_bssl.patch @@ -10,50 +10,15 @@ This should be upstreamed in some form, though it may need to be tweaked before it's acceptable to upstream, as this patch comments out a couple of tests that upstream probably cares about. -diff --git a/test/common/index.js b/test/common/index.js -index 8f5af57a83dc6b426f1b11bd2e3a8c6c0f2d9a85..f6e00c9f3f3ac4b42662eed6c8d190586f92ab99 100644 ---- a/test/common/index.js -+++ b/test/common/index.js -@@ -56,6 +56,8 @@ const hasCrypto = Boolean(process.versions.openssl) && - - const hasQuic = hasCrypto && !!process.config.variables.openssl_quic; - -+const openSSLIsBoringSSL = process.versions.openssl === '0.0.0'; -+ - function parseTestFlags(filename = process.argv[1]) { - // The copyright notice is relatively big and the flags could come afterwards. - const bytesToRead = 1500; -@@ -901,6 +903,7 @@ const common = { - mustNotMutateObjectDeep, - mustSucceed, - nodeProcessAborted, -+ openSSLIsBoringSSL, - PIPE, - parseTestFlags, - platformTimeout, -diff --git a/test/parallel/test-buffer-tostring-range.js b/test/parallel/test-buffer-tostring-range.js -index d033cd204b3200cdd736b581abe027d6e46e4ff3..73fec107a36c3db4af6f492137d0ca174f2d0547 100644 ---- a/test/parallel/test-buffer-tostring-range.js -+++ b/test/parallel/test-buffer-tostring-range.js -@@ -102,7 +102,8 @@ assert.throws(() => { - // Must not throw when start and end are within kMaxLength - // Cannot test on 32bit machine as we are testing the case - // when start and end are above the threshold --common.skipIf32Bits(); -+if (!common.openSSLIsBoringSSL) { - const threshold = 0xFFFFFFFF; - const largeBuffer = Buffer.alloc(threshold + 20); - largeBuffer.toString('utf8', threshold, threshold + 20); -+} diff --git a/test/parallel/test-crypto-async-sign-verify.js b/test/parallel/test-crypto-async-sign-verify.js -index b35dd08e6c49796418cd9d10eb5cc9d02b39961e..a49fdde82ea4cbadd60307cdc99439be892ef5a6 100644 +index b35dd08e6c49796418cd9d10eb5cc9d02b39961e..97bcd79b331db140d157e6b1faf92625597edc98 100644 --- a/test/parallel/test-crypto-async-sign-verify.js +++ b/test/parallel/test-crypto-async-sign-verify.js @@ -89,6 +89,7 @@ test('rsa_public.pem', 'rsa_private.pem', 'sha256', false, // ED25519 test('ed25519_public.pem', 'ed25519_private.pem', undefined, true); // ED448 -+if (!common.openSSLIsBoringSSL) { ++if (!process.features.openssl_is_boringssl) { test('ed448_public.pem', 'ed448_private.pem', undefined, true); // ECDSA w/ der signature encoding @@ -72,7 +37,7 @@ index b35dd08e6c49796418cd9d10eb5cc9d02b39961e..a49fdde82ea4cbadd60307cdc99439be - const expected = hasOpenSSL3 ? - /operation not supported for this keytype/ : /no default digest/; + let expected = /no default digest/; -+ if (hasOpenSSL3 || common.openSSLIsBoringSSL) { ++ if (hasOpenSSL3 || process.features.openssl_is_boringssl) { + expected = /operation[\s_]not[\s_]supported[\s_]for[\s_]this[\s_]keytype/i; + } @@ -144,23 +109,29 @@ index 81a469c226c261564dee1e0b06b6571b18a41f1f..58b66045dba4201b7ebedd78b129420f const availableCurves = new Set(crypto.getCurves()); diff --git a/test/parallel/test-crypto-dh-errors.js b/test/parallel/test-crypto-dh-errors.js -index 0af4db0310750cea9350ecff7fc44404c6df6c83..85ab03f6019989ad4fe93b779c3b4772ce1f5130 100644 +index 0af4db0310750cea9350ecff7fc44404c6df6c83..b14b4bbf88b902b6de916b92e3d48335c01df911 100644 --- a/test/parallel/test-crypto-dh-errors.js +++ b/test/parallel/test-crypto-dh-errors.js -@@ -33,9 +33,9 @@ for (const bits of [-1, 0, 1]) { +@@ -27,7 +27,7 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { + for (const bits of [-1, 0, 1]) { + if (hasOpenSSL3) { + assert.throws(() => crypto.createDiffieHellman(bits), { +- code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL', ++ code: 'ERR_OSSL_BN_BITS_TOO_SMALL', + name: 'Error', + message: /modulus too small/, }); - } else { +@@ -35,7 +35,7 @@ for (const bits of [-1, 0, 1]) { assert.throws(() => crypto.createDiffieHellman(bits), { -- code: 'ERR_OSSL_BN_BITS_TOO_SMALL', -+ code: /ERR_OSSL_BN_BITS_TOO_SMALL|ERR_OSSL_DH_MODULUS_TOO_LARGE/, + code: 'ERR_OSSL_BN_BITS_TOO_SMALL', name: 'Error', - message: /bits too small/, -+ message: /bits too small|BITS_TOO_SMALL|MODULUS_TOO_LARGE/, ++ message: /bits[\s_]too[\s_]small/i, }); } } diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js -index d7ffbe5eca92734aa2380f482c7f9bfe7e2a36c7..21ab2333431ea70bdf98dde43624e0b712566395 100644 +index d7ffbe5eca92734aa2380f482c7f9bfe7e2a36c7..b4e7002d862907d2af3b4f8e985700bd03300809 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -60,18 +60,17 @@ const { @@ -171,10 +142,10 @@ index d7ffbe5eca92734aa2380f482c7f9bfe7e2a36c7..21ab2333431ea70bdf98dde43624e0b7 - code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', - library: 'Provider routines', - reason: 'wrong final block length' -+ message: /error:1C80006B:Provider routines::wrong final block length|error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH/, ++ message: /wrong[\s_]final[\s_]block[\s_]length/i, + code: /ERR_OSSL_(EVP_)?WRONG_FINAL_BLOCK_LENGTH/, -+ library: /digital envelope routines|Cipher functions/, -+ reason: /wrong final block length|WRONG_FINAL_BLOCK_LENGTH/ ++ library: /Provider routines|Cipher functions/, ++ reason: /wrong[\s_]final[\s_]block[\s_]length/i, }; } else { wrongBlockLength = { @@ -183,10 +154,10 @@ index d7ffbe5eca92734aa2380f482c7f9bfe7e2a36c7..21ab2333431ea70bdf98dde43624e0b7 - code: 'ERR_OSSL_EVP_WRONG_FINAL_BLOCK_LENGTH', - library: 'digital envelope routines', - reason: 'wrong final block length' -+ message: /error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length|error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH/, ++ message: /wrong[\s_]final[\s_]block[\s_]length/i, + code: /ERR_OSSL_(EVP_)?WRONG_FINAL_BLOCK_LENGTH/, + library: /digital envelope routines|Cipher functions/, -+ reason: /wrong final block length|WRONG_FINAL_BLOCK_LENGTH/ ++ reason: /wrong[\s_]final[\s_]block[\s_]length/i, }; } @@ -217,25 +188,6 @@ index d7ffbe5eca92734aa2380f482c7f9bfe7e2a36c7..21ab2333431ea70bdf98dde43624e0b7 + name: 'Error' + }); } -diff --git a/test/parallel/test-crypto-getcipherinfo.js b/test/parallel/test-crypto-getcipherinfo.js -index 64b79fc36ccf4d38f763fcd8c1930473c82cefd7..1c6717ebd46497384b9b13174b65894ca89e7f2d 100644 ---- a/test/parallel/test-crypto-getcipherinfo.js -+++ b/test/parallel/test-crypto-getcipherinfo.js -@@ -62,9 +62,13 @@ assert(getCipherInfo('aes-128-cbc', { ivLength: 16 })); - - assert(!getCipherInfo('aes-128-ccm', { ivLength: 1 })); - assert(!getCipherInfo('aes-128-ccm', { ivLength: 14 })); -+if (!common.openSSLIsBoringSSL) { - for (let n = 7; n <= 13; n++) - assert(getCipherInfo('aes-128-ccm', { ivLength: n })); -+} - - assert(!getCipherInfo('aes-128-ocb', { ivLength: 16 })); -+if (!common.openSSLIsBoringSSL) { - for (let n = 1; n < 16; n++) - assert(getCipherInfo('aes-128-ocb', { ivLength: n })); -+} -\ No newline at end of file diff --git a/test/parallel/test-crypto-hash-stream-pipe.js b/test/parallel/test-crypto-hash-stream-pipe.js index d22281abbd5c3cab3aaa3ac494301fa6b4a8a968..5f0c6a4aed2e868a1a1049212edf218791cd6868 100644 --- a/test/parallel/test-crypto-hash-stream-pipe.js @@ -258,7 +210,7 @@ index d22281abbd5c3cab3aaa3ac494301fa6b4a8a968..5f0c6a4aed2e868a1a1049212edf2187 s.pipe(h).on('data', common.mustCall(function(c) { assert.strictEqual(c, expect); diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js -index 61145aee0727fbe0b9781acdb3eeb641e7010729..fd7d4bd7d3f86caa30ffd03ea880eeac023bbcbb 100644 +index 61145aee0727fbe0b9781acdb3eeb641e7010729..51693e637b310981f76f23c2f35d43e4d2d9c4ef 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -183,7 +183,7 @@ assert.throws( @@ -266,57 +218,57 @@ index 61145aee0727fbe0b9781acdb3eeb641e7010729..fd7d4bd7d3f86caa30ffd03ea880eeac // Test XOF hash functions and the outputLength option. -{ -+if (!common.openSSLIsBoringSSL) { ++if (!process.features.openssl_is_boringssl) { // Default outputLengths. Since OpenSSL 3.4 an outputLength is mandatory if (!hasOpenSSL(3, 4)) { assert.strictEqual(crypto.createHash('shake128').digest('hex'), -diff --git a/test/parallel/test-crypto-hkdf.js b/test/parallel/test-crypto-hkdf.js -index 3f7e61e9b2ebc0ca7c367d7c229afe9ab87762b8..36bd78105d153b75b42e4736f11d80a257916607 100644 ---- a/test/parallel/test-crypto-hkdf.js -+++ b/test/parallel/test-crypto-hkdf.js -@@ -125,7 +125,7 @@ const algorithms = [ - ['sha256', '', 'salt', '', 10], - ['sha512', 'secret', 'salt', '', 15], - ]; --if (!hasOpenSSL3) -+if (!hasOpenSSL3 && !common.openSSLIsBoringSSL) - algorithms.push(['whirlpool', 'secret', '', 'info', 20]); - - algorithms.forEach(([ hash, secret, salt, info, length ]) => { diff --git a/test/parallel/test-crypto-padding.js b/test/parallel/test-crypto-padding.js -index 48cd1ed4df61aaddeee8785cb90f83bdd9628187..a18aeb2bdffcc7a7e9ef12328b849994e39d6c27 100644 +index 48cd1ed4df61aaddeee8785cb90f83bdd9628187..d09e01712c617597833bb1320a32a967bcf1d318 100644 --- a/test/parallel/test-crypto-padding.js +++ b/test/parallel/test-crypto-padding.js -@@ -88,10 +88,9 @@ assert.throws(function() { - code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', - reason: 'wrong final block length', +@@ -84,14 +84,13 @@ assert.throws(function() { + // Input must have block length %. + enc(ODD_LENGTH_PLAIN, false); + }, hasOpenSSL3 ? { +- message: 'error:1C80006B:Provider routines::wrong final block length', +- code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', +- reason: 'wrong final block length', ++ message: /wrong[\s_]final[\s_]block[\s_]length/i, ++ code: /ERR_OSSL(_EVP)?_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH/, ++ message: /wrong[\s_]final[\s_]block[\s_]length/i, } : { - message: 'error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:' + - 'data not multiple of block length', - code: 'ERR_OSSL_EVP_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH', - reason: 'data not multiple of block length', -+ message: /error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length|error:1e00006a:Cipher functions:OPENSSL_internal:DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH/, ++ message: /data[\s_]not[\s_]multiple[\s_]of[\s_]block[\s_]length/i, + code: /ERR_OSSL(_EVP)?_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH/, -+ reason: /data not multiple of block length|DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH/, ++ reason: /data[\s_]not[\s_]multiple[\s_]of[\s_]block[\s_]length/i, } ); -@@ -115,10 +114,9 @@ assert.throws(function() { - reason: 'bad decrypt', - code: 'ERR_OSSL_BAD_DECRYPT', - } : { +@@ -110,15 +109,10 @@ assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED, false).length, 48); + assert.throws(function() { + // Must have at least 1 byte of padding (PKCS): + assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED_NOPAD, true), EVEN_LENGTH_PLAIN); +-}, hasOpenSSL3 ? { +- message: 'error:1C800064:Provider routines::bad decrypt', +- reason: 'bad decrypt', +- code: 'ERR_OSSL_BAD_DECRYPT', +-} : { - message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' + - 'bad decrypt', - reason: 'bad decrypt', - code: 'ERR_OSSL_EVP_BAD_DECRYPT', -+ message: /error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt|error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT/, -+ reason: /bad decrypt|BAD_DECRYPT/, ++}, { ++ message: /bad[\s_]decrypt/i, ++ reason: /bad[\s_]decrypt/i, + code: /ERR_OSSL(_EVP)?_BAD_DECRYPT/, }); // No-pad encrypted string should return the same: diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js -index dcd5045daaf58c60e27c1e2f7941033302241339..6ac75565792b92a97c622baba73f821d754b8d01 100644 +index dcd5045daaf58c60e27c1e2f7941033302241339..b52ec0e2cd5d6b1c9a0fee3064f2f8ff3b6e4308 100644 --- a/test/parallel/test-crypto-rsa-dsa.js +++ b/test/parallel/test-crypto-rsa-dsa.js @@ -29,12 +29,11 @@ const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem'); @@ -342,7 +294,7 @@ index dcd5045daaf58c60e27c1e2f7941033302241339..6ac75565792b92a97c622baba73f821d - if (padding === constants.RSA_PKCS1_PADDING) { -+ if (!common.openSSLIsBoringSSL) { ++ if (!process.features.openssl_is_boringssl) { if (!process.config.variables.node_shared_openssl) { assert.throws(() => { crypto.privateDecrypt({ @@ -351,7 +303,7 @@ index dcd5045daaf58c60e27c1e2f7941033302241339..6ac75565792b92a97c622baba73f821d // Test DSA signing and verification // -{ -+if (!common.openSSLIsBoringSSL) { ++if (!process.features.openssl_is_boringssl) { const input = 'I AM THE WALRUS'; // DSA signatures vary across runs so there is no static string to verify @@ -369,7 +321,7 @@ index 03a18c7522531c7317f12705550117dc389a0245..2f0f46f2c6ddc62de89877cfa0ca8094 }; assert.throws(() => crypto.scrypt('pass', 'salt', 1, options, () => {}), diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js -index 0589d60736e377f24dc8550f87a6b7624173fc44..547f22cdc130cf0c68d117f92068e3ac53a0efc2 100644 +index 0589d60736e377f24dc8550f87a6b7624173fc44..113003826fc47a589cf2334f7345e33d3e189d0a 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -33,7 +33,7 @@ const keySize = 2048; @@ -377,7 +329,7 @@ index 0589d60736e377f24dc8550f87a6b7624173fc44..547f22cdc130cf0c68d117f92068e3ac // Test handling of exceptional conditions -{ -+if (!common.openSSLIsBoringSSL) { ++if (!process.features.openssl_is_boringssl) { const library = { configurable: true, set() { @@ -426,77 +378,24 @@ index 0589d60736e377f24dc8550f87a6b7624173fc44..547f22cdc130cf0c68d117f92068e3ac for (const [file, length] of keys) { const privKey = fixtures.readKey(file); diff --git a/test/parallel/test-crypto-stream.js b/test/parallel/test-crypto-stream.js -index 62be4eaf6edfb01ce275e7db3e56b51d09ac66ce..3fb6cd833d959d1c3c8522ebacc8f18352672628 100644 +index 747af780469c22eb8e4c6c35424043e868f75c3d..ed0916b036a9af23d805007ebd609973ee954473 100644 --- a/test/parallel/test-crypto-stream.js +++ b/test/parallel/test-crypto-stream.js -@@ -78,10 +78,10 @@ cipher.pipe(decipher) - library: 'Provider routines', - reason: 'bad decrypt', +@@ -73,9 +73,9 @@ const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); + const decipher = crypto.createDecipheriv('aes-128-cbc', badkey, iv); + + cipher.pipe(decipher) +- .on('error', common.expectsError(hasOpenSSL3 ? { +- message: /bad[\s_]decrypt/, +- library: 'Provider routines', ++ .on('error', common.expectsError((hasOpenSSL3 || process.features.openssl_is_boringssl) ? { ++ message: /bad[\s_]decrypt/i, ++ library: /Provider routines|Cipher functions/, + reason: /bad[\s_]decrypt/i, } : { -- message: /bad decrypt/, -- function: 'EVP_DecryptFinal_ex', -- library: 'digital envelope routines', -- reason: 'bad decrypt', -+ message: /bad decrypt|BAD_DECRYPT/, -+ function: /EVP_DecryptFinal_ex|OPENSSL_internal/, -+ library: /digital envelope routines|Cipher functions/, -+ reason: /bad decrypt|BAD_DECRYPT/, - })); - - cipher.end('Papaya!'); // Should not cause an unhandled exception. -diff --git a/test/parallel/test-crypto-x509.js b/test/parallel/test-crypto-x509.js -index f75e1d63470bfb7ce7fb354118b87a1a6fe5e4cc..5c0852e83a466ab4b255e8c9c9a33aca1beb9b94 100644 ---- a/test/parallel/test-crypto-x509.js -+++ b/test/parallel/test-crypto-x509.js -@@ -97,8 +97,10 @@ const der = Buffer.from( - assert.strictEqual(x509.infoAccess, infoAccessCheck); - assert.strictEqual(x509.validFrom, 'Sep 3 21:40:37 2022 GMT'); - assert.strictEqual(x509.validTo, 'Jun 17 21:40:37 2296 GMT'); -+ if (!common.openSSLIsBoringSSL) { - assert.deepStrictEqual(x509.validFromDate, new Date('2022-09-03T21:40:37Z')); - assert.deepStrictEqual(x509.validToDate, new Date('2296-06-17T21:40:37Z')); -+ } - assert.strictEqual( - x509.fingerprint, - '8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53'); -@@ -326,6 +328,7 @@ oans248kpal88CGqsN2so/wZKxVnpiXlPHMdiNL7hRSUqlHkUi07FrP2Htg8kjI= - legacyObjectCheck.serialNumberPattern); - } - -+if (!common.openSSLIsBoringSSL) { - { - // This X.509 Certificate can be parsed by OpenSSL because it contains a - // structurally sound TBSCertificate structure. However, the SPKI field of the -@@ -364,6 +367,7 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0 - - assert.strictEqual(cert.checkIssued(cert), false); - } -+} - - { - // Test date parsing of `validFromDate` and `validToDate` fields, according to RFC 5280. -@@ -401,8 +405,10 @@ UidvpWWipVLZgK+oDks+bKTobcoXGW9oXobiIYqslXPy - -----END CERTIFICATE-----`.trim(); - const c1 = new X509Certificate(certPemUTCTime); - -+ if (!common.openSSLIsBoringSSL) { - assert.deepStrictEqual(c1.validFromDate, new Date('1949-12-25T23:59:58Z')); - assert.deepStrictEqual(c1.validToDate, new Date('1950-01-01T23:59:58Z')); -+ } - - // The GeneralizedTime format is used for dates in 2050 or later. - const certPemGeneralizedTime = `-----BEGIN CERTIFICATE----- -@@ -436,6 +442,8 @@ CWwQO8JZjJqFtqtuzy2n+gLCvqePgG/gmSqHOPm2ZbLW - -----END CERTIFICATE-----`.trim(); - const c2 = new X509Certificate(certPemGeneralizedTime); - -+ if (!common.openSSLIsBoringSSL) { - assert.deepStrictEqual(c2.validFromDate, new Date('2049-12-26T00:00:01Z')); - assert.deepStrictEqual(c2.validToDate, new Date('2050-01-02T00:00:01Z')); -+ } - } + message: /bad[\s_]decrypt/i, diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js -index 93644e016de447d2aadc519123f18cd72b7a5750..8b16c83cd47bd8969654242296c987ecc97ccaeb 100644 +index 84111740cd9ef6425b747e24e984e66e46b0b2ef..b1621d310536fae3fdec91a6a9d275ec8fc99a98 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -62,7 +62,7 @@ assert.throws(() => { @@ -539,25 +438,10 @@ index 93644e016de447d2aadc519123f18cd72b7a5750..8b16c83cd47bd8969654242296c987ec } ); -+if (!common.openSSLIsBoringSSL) { ++if (!process.features.openssl_is_boringssl) { assert.throws(() => { const priv = [ '-----BEGIN RSA PRIVATE KEY-----', -@@ -217,10 +216,10 @@ assert.throws(() => { - library: 'rsa routines', - } : { - name: 'Error', -- message: /routines:RSA_sign:digest too big for rsa key$/, -- library: 'rsa routines', -- function: 'RSA_sign', -- reason: 'digest too big for rsa key', -+ message: /routines:RSA_sign:digest too big for rsa key$|routines:OPENSSL_internal:DIGEST_TOO_BIG_FOR_RSA_KEY$/, -+ library: /rsa routines|RSA routines/, -+ function: /RSA_sign|OPENSSL_internal/, -+ reason: /digest too big for rsa key|DIGEST_TOO_BIG_FOR_RSA_KEY/, - code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY' - }); - return true; @@ -253,7 +252,7 @@ if (!hasOpenSSL3) { return true; }); @@ -567,162 +451,29 @@ index 93644e016de447d2aadc519123f18cd72b7a5750..8b16c83cd47bd8969654242296c987ec // Make sure memory isn't released before being returned console.log(crypto.randomBytes(16)); -diff --git a/test/parallel/test-https-agent-additional-options.js b/test/parallel/test-https-agent-additional-options.js -index 543ee176fb6af38874fee9f14be76f3fdda11060..fef9f1bc2f9fc6c220cf47847e86e03882b51b1d 100644 ---- a/test/parallel/test-https-agent-additional-options.js -+++ b/test/parallel/test-https-agent-additional-options.js -@@ -13,7 +13,7 @@ const options = { - cert: fixtures.readKey('agent1-cert.pem'), - ca: fixtures.readKey('ca1-cert.pem'), - minVersion: 'TLSv1.1', -- ciphers: 'ALL@SECLEVEL=0' -+ // ciphers: 'ALL@SECLEVEL=0' - }; - - const server = https.Server(options, (req, res) => { -@@ -28,7 +28,7 @@ function getBaseOptions(port) { - ca: options.ca, - rejectUnauthorized: true, - servername: 'agent1', -- ciphers: 'ALL@SECLEVEL=0' -+ // ciphers: 'ALL@SECLEVEL=0' - }; - } - -diff --git a/test/parallel/test-https-agent-session-eviction.js b/test/parallel/test-https-agent-session-eviction.js -index 6f88e81e9ff29defe73800fc038b0d96d1ebd846..c0b92e2bdf86d3d2638c973f8be3110d5ae31f78 100644 ---- a/test/parallel/test-https-agent-session-eviction.js -+++ b/test/parallel/test-https-agent-session-eviction.js -@@ -17,7 +17,7 @@ const options = { - key: readKey('agent1-key.pem'), - cert: readKey('agent1-cert.pem'), - secureOptions: SSL_OP_NO_TICKET, -- ciphers: 'RSA@SECLEVEL=0' -+ // ciphers: 'RSA@SECLEVEL=0' - }; - - // Create TLS1.2 server diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js -index cba5bebaa29b6f8ac4fd0fcedaadb2f7bb3eb321..019d95df499892b14ab088f99013ee32c432779c 100644 +index 7bd42bbe721c4c9442410d524c5ca740078fc72c..de49dbdc2b75517f497af353a6b24b1beb11ed69 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js -@@ -35,7 +35,7 @@ let iter = 0; - - const errorHandler = common.mustCall((err) => { - let expectedErrorCode = 'ERR_SSL_WRONG_VERSION_NUMBER'; -- let expectedErrorReason = 'wrong version number'; -+ let expectedErrorReason = /wrong[\s_]version[\s_]number/i; - if (hasOpenSSL(3, 2)) { - expectedErrorCode = 'ERR_SSL_PACKET_LENGTH_TOO_LONG'; - expectedErrorReason = 'packet length too long'; -@@ -43,8 +43,8 @@ const errorHandler = common.mustCall((err) => { +@@ -43,7 +43,8 @@ const errorHandler = common.mustCall((err) => { assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); -- assert.strictEqual(err.reason, expectedErrorReason); -+ if (!hasOpenSSL3 && !common.openSSLIsBoringSSL) assert.strictEqual(err.function, 'ssl3_get_record'); -+ assert.match(err.reason, expectedErrorReason); ++ if (!hasOpenSSL3 && !process.features.openssl_is_boringssl) ++ assert.strictEqual(err.function, 'ssl3_get_record'); + assert.match(err.reason, expectedErrorReason); errorReceived = true; if (canCloseServer()) - server.close(); -@@ -98,15 +98,15 @@ function sendBADTLSRecord() { - })); - client.on('error', common.mustCall((err) => { - let expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'; -- let expectedErrorReason = 'tlsv1 alert protocol version'; -+ let expectedErrorReason = /tlsv1[\s_]alert[\s_]protocol[\s_]version/i; - if (hasOpenSSL(3, 2)) { - expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_RECORD_OVERFLOW'; - expectedErrorReason = 'tlsv1 alert record overflow'; +@@ -105,7 +106,7 @@ function sendBADTLSRecord() { } assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!hasOpenSSL3) -+ if (!hasOpenSSL3 && !common.openSSLIsBoringSSL) ++ if (!hasOpenSSL3 && !process.features.openssl_is_boringssl) assert.strictEqual(err.function, 'ssl3_read_bytes'); -- assert.strictEqual(err.reason, expectedErrorReason); -+ assert.match(err.reason, expectedErrorReason); + assert.match(err.reason, expectedErrorReason); })); - } -diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js -index b1eab88fd6517e3698934dea17752ef2bb8d8d54..3ad6db20316baa8490e3787dd55903b58a54ad06 100644 ---- a/test/parallel/test-tls-getprotocol.js -+++ b/test/parallel/test-tls-getprotocol.js -@@ -29,7 +29,7 @@ const clientConfigs = [ - - const serverConfig = { - secureProtocol: 'TLS_method', -- ciphers: 'RSA@SECLEVEL=0', -+ // ciphers: 'RSA@SECLEVEL=0', - key: fixtures.readKey('agent2-key.pem'), - cert: fixtures.readKey('agent2-cert.pem') - }; -diff --git a/test/parallel/test-tls-write-error.js b/test/parallel/test-tls-write-error.js -index b06f2fa2c53ea72f9a66f0d002dd9281d0259a0f..864fffeebfad75d95416fd47efdea7f222c507a2 100644 ---- a/test/parallel/test-tls-write-error.js -+++ b/test/parallel/test-tls-write-error.js -@@ -17,7 +17,7 @@ const server_cert = fixtures.readKey('agent1-cert.pem'); - const opts = { - key: server_key, - cert: server_cert, -- ciphers: 'ALL@SECLEVEL=0' -+ // ciphers: 'ALL@SECLEVEL=0' - }; - - const server = https.createServer(opts, (req, res) => { -diff --git a/test/parallel/test-webcrypto-derivebits.js b/test/parallel/test-webcrypto-derivebits.js -index eb09bc24f0cb8244b05987e3a7c1d203360d3a38..8c251ff2371fb59bf679160574e1c5dc1b4b2665 100644 ---- a/test/parallel/test-webcrypto-derivebits.js -+++ b/test/parallel/test-webcrypto-derivebits.js -@@ -101,8 +101,9 @@ const { subtle } = globalThis.crypto; - tests.then(common.mustCall()); - } - -+ - // Test X25519 and X448 bit derivation --{ -+if (!common.openSSLIsBoringSSL) { - async function test(name) { - const [alice, bob] = await Promise.all([ - subtle.generateKey({ name }, true, ['deriveBits']), -diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js -index 558d37d90d5796b30101d1b512c9df3e7661d0db..f42bf8f4be0b439dd7e7c8d0f6f8a41e01588870 100644 ---- a/test/parallel/test-webcrypto-derivekey.js -+++ b/test/parallel/test-webcrypto-derivekey.js -@@ -176,7 +176,7 @@ const { KeyObject } = require('crypto'); - } - - // Test X25519 and X448 key derivation --{ -+if (!common.openSSLIsBoringSSL) { - async function test(name) { - const [alice, bob] = await Promise.all([ - subtle.generateKey({ name }, true, ['deriveKey']), -diff --git a/test/parallel/test-webcrypto-sign-verify.js b/test/parallel/test-webcrypto-sign-verify.js -index de736102bdcb71a5560c95f7041537f25026aed4..12d7fa39446c196bdf1479dbe74c9ee8ab02f949 100644 ---- a/test/parallel/test-webcrypto-sign-verify.js -+++ b/test/parallel/test-webcrypto-sign-verify.js -@@ -105,8 +105,9 @@ const { subtle } = globalThis.crypto; - test('hello world').then(common.mustCall()); - } - -+ - // Test Sign/Verify Ed25519 --{ -+if (!common.openSSLIsBoringSSL) { - async function test(data) { - const ec = new TextEncoder(); - const { publicKey, privateKey } = await subtle.generateKey({ -@@ -126,7 +127,7 @@ const { subtle } = globalThis.crypto; - } - - // Test Sign/Verify Ed448 --{ -+if (!common.openSSLIsBoringSSL) { - async function test(data) { - const ec = new TextEncoder(); - const { publicKey, privateKey } = await subtle.generateKey({ diff --git a/test/parallel/test-webcrypto-wrap-unwrap.js b/test/parallel/test-webcrypto-wrap-unwrap.js index d1ca571af4be713082d32093bfb8a65f2aef9800..57b8df2ce18df58ff54b2d828af67e3c2e082fe0 100644 --- a/test/parallel/test-webcrypto-wrap-unwrap.js diff --git a/patches/node/fix_do_not_resolve_electron_entrypoints.patch b/patches/node/fix_do_not_resolve_electron_entrypoints.patch index a489cf55632f5..a55af942e64b7 100644 --- a/patches/node/fix_do_not_resolve_electron_entrypoints.patch +++ b/patches/node/fix_do_not_resolve_electron_entrypoints.patch @@ -6,7 +6,7 @@ Subject: fix: do not resolve electron entrypoints This wastes fs cycles and can result in strange behavior if this path actually exists on disk diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index 49aacb6262502ced54e817f99dd596db85b9659c..f9f065bb743275e9b2ce71375e6a9f06e00c0f36 100644 +index bbd3085cfbf14aefd611954fa836f6c96d7e402c..9e71d2d0b79656e212fb21efdb3efcf988529f5e 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -291,6 +291,9 @@ function cjsPreparseModuleExports(filename, source, isMain, format) { diff --git a/patches/node/fix_expose_readfilesync_override_for_modules.patch b/patches/node/fix_expose_readfilesync_override_for_modules.patch index c2b377ee140aa..9fb45295e340a 100644 --- a/patches/node/fix_expose_readfilesync_override_for_modules.patch +++ b/patches/node/fix_expose_readfilesync_override_for_modules.patch @@ -8,10 +8,10 @@ an API override to replace the native `ReadFileSync` in the `modules` binding. diff --git a/src/env_properties.h b/src/env_properties.h -index cbb7eab2df0416087cd3e6fb80eef2079143d9c8..7e9f5e977506149d69c6015e85d031770325e1da 100644 +index 3df4e303e05105b43a542456546cbc050d0445fa..4f7aee6cb64b4c1d25e98b43d13e747c4546adaa 100644 --- a/src/env_properties.h +++ b/src/env_properties.h -@@ -501,6 +501,7 @@ +@@ -502,6 +502,7 @@ V(maybe_cache_generated_source_map, v8::Function) \ V(messaging_deserialize_create_object, v8::Function) \ V(message_port, v8::Object) \ diff --git a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch index dec3592cbdc61..181b24add48b2 100644 --- a/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch +++ b/patches/node/fix_expose_the_built-in_electron_module_via_the_esm_loader.patch @@ -64,7 +64,7 @@ index 5ba13096b98047ff33e4d44167c2a069ccc5e69d..09a332c0999086b30fd952d9456f7889 } } diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js -index aff686577df3c366f06f90666e23a03fc376cf53..e8857a151428acd6f8ece74d92774a18accc1e13 100644 +index 5a817ed0434b8bc08f9b9bd260c978bbe5ddc664..f9dba42cddb08f69e02834f76d6d80c5e47a637d 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -492,7 +492,7 @@ class ModuleLoader { @@ -101,7 +101,7 @@ index bfd9bd3d127404de1cbb6f30c43ab0342590759d..9e7d8ef0adef3b68a3ec186e4b218f59 const packageConfig = packageJsonReader.read(packageJSONPath, { __proto__: null, specifier, base, isESM: true }); diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index 7dcd7afe8ce3c25ceb314cdf15c330f3d66eb84f..4445bcc4c4c6c6f87ac45e693012a18a93739f9b 100644 +index 0695b47a8a8f78451c20c025a3d9dac7cadab686..1db58d31dad24cd2da6bdc5e45e9dd4f0b899a23 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -186,7 +186,7 @@ function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModul diff --git a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch index 709e710e07aa5..134bd2a779163 100644 --- a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch +++ b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch @@ -172,7 +172,7 @@ index e5bf2b529bf23914677e25d7468aad58a4684557..9a3c6029ff3319cce58c79782a7bd5d1 }; // Check to see if the given public key is suitable for this DH instance. diff --git a/node.gni b/node.gni -index 245a43920c7baf000ba63192a84a4c3fd219be7d..56a554175b805c1703f13d62041f8c80d6e94dd9 100644 +index b4450e3dd17994d1eaf59eb5cff5912545e89793..203b4abbc44df9e58083c819f61f9025104abdc6 100644 --- a/node.gni +++ b/node.gni @@ -11,7 +11,7 @@ declare_args() { @@ -183,7 +183,7 @@ index 245a43920c7baf000ba63192a84a4c3fd219be7d..56a554175b805c1703f13d62041f8c80 + node_openssl_path = "//third_party/boringssl" # The location of simdutf - use the one from node's deps by default. - node_simdutf_path = "$node_path/deps/simdutf" + node_simdutf_path = "//third_party/simdutf" diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 2176fb6982484e2c42538478eeb4dd81c9d50ee1..c00d3616e08b00b1e0a3a29b2dbb5278e1e14fcc 100644 --- a/src/crypto/crypto_cipher.cc @@ -520,7 +520,7 @@ index 7c548d32b40365343f0e208c3aa856a1c847f4c3..6346f8f7199cf7b7d3736c59571606ff } // namespace diff --git a/src/env.h b/src/env.h -index b6bdff9b8580d2588a39f00b594c4c480157d78a..cfe917c797a6e4bb0f0284ec56be82637f840129 100644 +index c42493ad958508f650917bf5ca92088714a5056c..07accfbcca491966c6c8ad9c20e146dbd22347f0 100644 --- a/src/env.h +++ b/src/env.h @@ -50,7 +50,7 @@ @@ -532,7 +532,7 @@ index b6bdff9b8580d2588a39f00b594c4c480157d78a..cfe917c797a6e4bb0f0284ec56be8263 #include #endif -@@ -1073,7 +1073,7 @@ class Environment final : public MemoryRetainer { +@@ -1076,7 +1076,7 @@ class Environment final : public MemoryRetainer { kExitInfoFieldCount }; @@ -541,8 +541,23 @@ index b6bdff9b8580d2588a39f00b594c4c480157d78a..cfe917c797a6e4bb0f0284ec56be8263 #if OPENSSL_VERSION_MAJOR >= 3 // We declare another alias here to avoid having to include crypto_util.h using EVPMDPointer = DeleteFnPtr; +diff --git a/src/node_config.cc b/src/node_config.cc +index 6032bbd10f41da7bae44828a8e908c1bec0ea0b6..2013de54f0f6a036e8378deefbff8d7cb5f7cfb2 100644 +--- a/src/node_config.cc ++++ b/src/node_config.cc +@@ -7,6 +7,10 @@ + #include "node_options.h" + #include "util-inl.h" + ++#if HAVE_OPENSSL ++#include ++#endif ++ + namespace node { + + using v8::Context; diff --git a/src/node_metadata.h b/src/node_metadata.h -index 6f8cb433ff8059c63d5cf16c8783139ae92fbf61..603ac3dde7d1a1109afbc451b69c8d0935b81641 100644 +index 7b2072ad39c3f1a7c73101b25b69beb781141e26..d23536d88d21255d348175425a59e2424332cd19 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -6,7 +6,7 @@ @@ -555,7 +570,7 @@ index 6f8cb433ff8059c63d5cf16c8783139ae92fbf61..603ac3dde7d1a1109afbc451b69c8d09 #if NODE_OPENSSL_HAS_QUIC #include diff --git a/src/node_options.cc b/src/node_options.cc -index 3fc8194475ec0e8a9047c1f3da5d120f25d66190..8be78889e8234eb3100f309829bf7470db544dcd 100644 +index da39abf79c53fcc3d83d3431deda9dbdf3b0621e..14f7764c995e8de6582faf58c9b98a9cbe4fab73 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -7,7 +7,7 @@ @@ -568,7 +583,7 @@ index 3fc8194475ec0e8a9047c1f3da5d120f25d66190..8be78889e8234eb3100f309829bf7470 #endif diff --git a/src/node_options.h b/src/node_options.h -index 7d14f06370d936a3866f0d988123de9fe614ce09..60068b008b2e2a034c3f0c58b947a8d04d55e3b2 100644 +index 165950c207ca752ec942ef27a671af66cbd2b938..eb18fdd617fd19e5b97cd67f351e70c28fee3e75 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -11,7 +11,7 @@ diff --git a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch index 3eb241ca1eb19..c672873f6622d 100644 --- a/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch +++ b/patches/node/fix_lazyload_fs_in_esm_loaders_to_apply_asar_patches.patch @@ -60,7 +60,7 @@ index 9e7d8ef0adef3b68a3ec186e4b218f591aa69266..2879e5cf541fb4d226cfd7cc0fe367ca }); const { search, hash } = resolved; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js -index 4445bcc4c4c6c6f87ac45e693012a18a93739f9b..49aacb6262502ced54e817f99dd596db85b9659c 100644 +index 1db58d31dad24cd2da6bdc5e45e9dd4f0b899a23..bbd3085cfbf14aefd611954fa836f6c96d7e402c 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -24,7 +24,7 @@ const { diff --git a/patches/node/fix_remove_deprecated_errno_constants.patch b/patches/node/fix_remove_deprecated_errno_constants.patch index 0e9ce33bc3b42..55ac88fa57cd0 100644 --- a/patches/node/fix_remove_deprecated_errno_constants.patch +++ b/patches/node/fix_remove_deprecated_errno_constants.patch @@ -10,19 +10,19 @@ This change removes the usage of these constants to fix a compilation failure du See: https://github.com/llvm/llvm-project/pull/80542 diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h -index f75a496071ac3396cbc6dec819eaab7294609deb..30f9a05f2f508b55a7d7ae036612660068c8400e 100644 +index 0295eeddbb4eb1cffd9dbcd02e037907fadc1b7e..7c2f9d2a8b13584ff6b33cd3ff4745e9fb3c4170 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h -@@ -155,7 +155,6 @@ struct uv__queue { +@@ -156,7 +156,6 @@ struct uv__queue { XX(EFTYPE, "inappropriate file type or format") \ XX(EILSEQ, "illegal byte sequence") \ XX(ESOCKTNOSUPPORT, "socket type not supported") \ - XX(ENODATA, "no data available") \ XX(EUNATCH, "protocol driver not attached") \ + XX(ENOEXEC, "exec format error") \ - #define UV_HANDLE_TYPE_MAP(XX) \ diff --git a/deps/uv/include/uv/errno.h b/deps/uv/include/uv/errno.h -index 127278ef916161a96e23e645927d16bedfdaca5b..b36da3daa5744e6f994e32d9d82aaef689008a5f 100644 +index ac00778cfc59fb55e361b24fc81a965a5e8f97e7..f0c4d6dfc9f03bee59e656b2da9ac325bced7b69 100644 --- a/deps/uv/include/uv/errno.h +++ b/deps/uv/include/uv/errno.h @@ -456,18 +456,6 @@ @@ -45,7 +45,7 @@ index 127278ef916161a96e23e645927d16bedfdaca5b..b36da3daa5744e6f994e32d9d82aaef6 # define UV__EUNATCH UV__ERR(EUNATCH) #else diff --git a/src/node_constants.cc b/src/node_constants.cc -index 13263149c111beede83b7063fa54f56655aea54c..99068098e1867af4846e18a5d039a25689722a4a 100644 +index 8c44e32381a44675792ca0922e47df1adda48e41..d193725ea9a3270ed9affea12d11467fb14efdf8 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -241,10 +241,6 @@ void DefineErrnoConstants(Local target) { diff --git a/patches/node/fix_remove_fastapitypedarray_usage.patch b/patches/node/fix_remove_fastapitypedarray_usage.patch index 1d5b56cbe1fea..29a42af4b16cf 100644 --- a/patches/node/fix_remove_fastapitypedarray_usage.patch +++ b/patches/node/fix_remove_fastapitypedarray_usage.patch @@ -48,7 +48,7 @@ index 867a1c4aca54b9d41490d23a5eb55088b7e941cc..09f4c65a18efea262b1f854f993c6f18 static v8::CFunction fast_equal(v8::CFunction::Make(FastTimingSafeEqual)); diff --git a/src/node_buffer.cc b/src/node_buffer.cc -index 5bdffc0a4d7f8f643343593a543f2064b670c1b9..6931404b75dbe17bf3c7b561430b8d7c0921d085 100644 +index b8021c079ca6b141ea99a4abbc828430da9d3093..98ade40ec7a6bc805acb579c8a51dcd5468c2893 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -44,6 +44,14 @@ @@ -194,7 +194,7 @@ index 5bdffc0a4d7f8f643343593a543f2064b670c1b9..6931404b75dbe17bf3c7b561430b8d7c static const v8::CFunction fast_write_string_ascii( diff --git a/src/node_external_reference.h b/src/node_external_reference.h -index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d46afdce75 100644 +index 3bc7b2cab5a12e281058b79a7d0f734eb527986c..4218eaf365c739a4c04ca94e29d6d2a33a637d44 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -43,16 +43,16 @@ using CFunctionCallbackWithStrings = @@ -219,7 +219,7 @@ index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d4 uint32_t, int64_t, bool); -@@ -71,18 +71,20 @@ using CFunctionWithBool = void (*)(v8::Local, +@@ -73,18 +73,20 @@ using CFunctionWithBool = void (*)(v8::Local, using CFunctionWriteString = uint32_t (*)(v8::Local receiver, @@ -246,7 +246,7 @@ index bb007dbdcce486659afeed07b78103e44b00307b..314a4ded6908a94107de1ae1e550b7d4 // This class manages the external references from the V8 heap // to the C++ addresses in Node.js. diff --git a/src/util.h b/src/util.h -index a77332f583402af956cc886fd5b9771390cc4827..f0d7571caa458a3f9a9c252a95f72eb5fbddd06d 100644 +index 6376cf4f81113cdb2e3c179b800f1c79b51ab762..cc7ad99f981f564fba0395159d9d8b39901050ff 100644 --- a/src/util.h +++ b/src/util.h @@ -60,6 +60,7 @@ diff --git a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch index 81346e984b9a8..eb48a5a18140f 100644 --- a/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch +++ b/patches/node/fix_remove_harmony-import-assertions_from_node_cc.patch @@ -11,10 +11,10 @@ This patch can be removed when we upgrade to a V8 version that contains the above CL. diff --git a/src/node.cc b/src/node.cc -index 0fbcd55d674b1d0cae88f04fe337cfcca702255f..092b1c525c7d4d50a09f99dc088d0698afcaf8a6 100644 +index c0d0b734edfa729c91a8112189c480e3f2382988..a43d36c731693b9d2ed1ba13f6b4bb33bf6c4ca4 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -814,7 +814,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, +@@ -816,7 +816,7 @@ static ExitCode ProcessGlobalArgsInternal(std::vector* args, } // TODO(nicolo-ribaudo): remove this once V8 doesn't enable it by default // anymore. diff --git a/patches/node/linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch b/patches/node/linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch deleted file mode 100644 index 06bb2affd320e..0000000000000 --- a/patches/node/linux_try_preadv64_pwritev64_before_preadv_pwritev_4683.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Noordhuis -Date: Tue, 28 Jan 2025 09:27:58 +0100 -Subject: linux: try preadv64/pwritev64 before preadv/pwritev (#4683) - -Fixes: https://github.com/libuv/libuv/issues/4678 -Refs: https://github.com/libuv/libuv/issues/4532 - -diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c -index 239ecda16a7eb9b40453502cf0362ae66366cf72..1631d9340bc10c2ac4c3d53a63ed9bc10f3e1c7c 100644 ---- a/deps/uv/src/unix/fs.c -+++ b/deps/uv/src/unix/fs.c -@@ -461,12 +461,7 @@ static ssize_t uv__pwritev_emul(int fd, - - /* The function pointer cache is an uintptr_t because _Atomic void* - * doesn't work on macos/ios/etc... -- * Disable optimization on armv7 to work around the bug described in -- * https://github.com/libuv/libuv/issues/4532 - */ --#if defined(__arm__) && (__ARM_ARCH == 7) --__attribute__((optimize("O0"))) --#endif - static ssize_t uv__preadv_or_pwritev(int fd, - const struct iovec* bufs, - size_t nbufs, -@@ -479,7 +474,12 @@ static ssize_t uv__preadv_or_pwritev(int fd, - p = (void*) atomic_load_explicit(cache, memory_order_relaxed); - if (p == NULL) { - #ifdef RTLD_DEFAULT -- p = dlsym(RTLD_DEFAULT, is_pread ? "preadv" : "pwritev"); -+ /* Try _LARGEFILE_SOURCE version of preadv/pwritev first, -+ * then fall back to the plain version, for libcs like musl. -+ */ -+ p = dlsym(RTLD_DEFAULT, is_pread ? "preadv64" : "pwritev64"); -+ if (p == NULL) -+ p = dlsym(RTLD_DEFAULT, is_pread ? "preadv" : "pwritev"); - dlerror(); /* Clear errors. */ - #endif /* RTLD_DEFAULT */ - if (p == NULL) -@@ -487,10 +487,7 @@ static ssize_t uv__preadv_or_pwritev(int fd, - atomic_store_explicit(cache, (uintptr_t) p, memory_order_relaxed); - } - -- /* Use memcpy instead of `f = p` to work around a compiler bug, -- * see https://github.com/libuv/libuv/issues/4532 -- */ -- memcpy(&f, &p, sizeof(p)); -+ f = p; - return f(fd, bufs, nbufs, off); - } - diff --git a/patches/node/pass_all_globals_through_require.patch b/patches/node/pass_all_globals_through_require.patch index 918c6d2aac4f2..7d2b862278233 100644 --- a/patches/node/pass_all_globals_through_require.patch +++ b/patches/node/pass_all_globals_through_require.patch @@ -6,7 +6,7 @@ Subject: Pass all globals through "require" (cherry picked from commit 7d015419cb7a0ecfe6728431a4ed2056cd411d62) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index ccd2b4ced3134d81ddd37b8b5b90218457633421..a19fc5e52109bf2ad63fbe554c02a458c3096081 100644 +index d90476addb3f7cc2d0b8d8686386873894e75c25..e647bdda4624317615490cedd8d4edbcebbca8bc 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -200,6 +200,13 @@ const { diff --git a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch index e7344756b3855..dbbf389088cd5 100644 --- a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch +++ b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch @@ -7,7 +7,7 @@ We use this to allow node's 'fs' module to read from ASAR files as if they were a real filesystem. diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js -index 4fd535f730e6382672b853bf2098b3fefc1f83b4..bd6724de52ee1f01fa42084c7652c71054f0a9c6 100644 +index dd9e3e58d72fb9ada1528212f80e0e911292a266..5758c74f6139dbe4fbeeae9d1e9b078688261257 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -132,6 +132,10 @@ process.domain = null; diff --git a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch index a526b752d46d3..f6eff26f8203a 100644 --- a/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch +++ b/patches/node/refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -47,7 +47,7 @@ index 5b7f6e0609c8414c686d2d5ca603ea5c8bc484d0..6c9c81ff3c08fc28dc35578229a78552 event_loop, platform, diff --git a/src/env.cc b/src/env.cc -index ddf82c8a18c29c355641e33974c1e5e67867379d..ae43803d8c2de90fb191a8e10605f6f3b18816ed 100644 +index 400ff494f4e153408a2fce343d7b156d7ccefc7b..cac2d97323d131451eab4ca68ce771cb936447e4 100644 --- a/src/env.cc +++ b/src/env.cc @@ -577,14 +577,6 @@ IsolateData::IsolateData(Isolate* isolate, @@ -82,7 +82,7 @@ index ddf82c8a18c29c355641e33974c1e5e67867379d..ae43803d8c2de90fb191a8e10605f6f3 void SetCppgcReference(Isolate* isolate, Local object, diff --git a/src/env.h b/src/env.h -index 9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f..c429eecd937d1df32a2ff90bc0a22a2e58df3a3d 100644 +index 1079e3beb02e5f5d71a15fd2db65cb93ebd175d6..a7be609c3ab9093cec5145367b95ae6859936a24 100644 --- a/src/env.h +++ b/src/env.h @@ -155,7 +155,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { @@ -102,10 +102,10 @@ index 9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f..c429eecd937d1df32a2ff90bc0a22a2e worker::Worker* worker_context_ = nullptr; PerIsolateWrapperData* wrapper_data_; diff --git a/src/node.cc b/src/node.cc -index 092b1c525c7d4d50a09f99dc088d0698afcaf8a6..6f8f6386d0db8aef1e2e0126cc9064101cbe6112 100644 +index a43d36c731693b9d2ed1ba13f6b4bb33bf6c4ca4..0c2a4d344c991c2ca0d9d90934cf7921abf2a629 100644 --- a/src/node.cc +++ b/src/node.cc -@@ -1258,6 +1258,14 @@ InitializeOncePerProcessInternal(const std::vector& args, +@@ -1257,6 +1257,14 @@ InitializeOncePerProcessInternal(const std::vector& args, result->platform_ = per_process::v8_platform.Platform(); } @@ -120,7 +120,7 @@ index 092b1c525c7d4d50a09f99dc088d0698afcaf8a6..6f8f6386d0db8aef1e2e0126cc906410 if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) { V8::Initialize(); -@@ -1267,14 +1275,6 @@ InitializeOncePerProcessInternal(const std::vector& args, +@@ -1266,14 +1274,6 @@ InitializeOncePerProcessInternal(const std::vector& args, absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore); } diff --git a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch index 3936c9bc7017a..76a7169955aec 100644 --- a/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch +++ b/patches/node/src_remove_dependency_on_wrapper-descriptor-based_cppheap.patch @@ -16,7 +16,7 @@ patch: (cherry picked from commit 30329d06235a9f9733b1d4da479b403462d1b326) diff --git a/src/env-inl.h b/src/env-inl.h -index d4b211dfb2f77a65f311701d95365e66d92f8875..4c1a5f2b92d7fdddb8c2e7bbfd6788fdff3645b9 100644 +index 0d32d9f5a55da257a5e5a895a2ff002b780f96fe..9567d9499c62ea44cca651e87ab912ad55e2d90b 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -62,31 +62,6 @@ inline uv_loop_t* IsolateData::event_loop() const { @@ -52,7 +52,7 @@ index d4b211dfb2f77a65f311701d95365e66d92f8875..4c1a5f2b92d7fdddb8c2e7bbfd6788fd return &(wrapper_data_->cppgc_id); } diff --git a/src/env.cc b/src/env.cc -index f8c24e422756d5101a258175a2f28a96648bea47..ddf82c8a18c29c355641e33974c1e5e67867379d 100644 +index 99bd3e37853f99ecb2e45df373f17c10b3bd1561..400ff494f4e153408a2fce343d7b156d7ccefc7b 100644 --- a/src/env.cc +++ b/src/env.cc @@ -23,6 +23,7 @@ @@ -146,7 +146,7 @@ index f8c24e422756d5101a258175a2f28a96648bea47..ddf82c8a18c29c355641e33974c1e5e6 void IsolateData::MemoryInfo(MemoryTracker* tracker) const { diff --git a/src/env.h b/src/env.h -index cfe917c797a6e4bb0f0284ec56be82637f840129..9f1c7ef45b6df10f811936a78ea6d9fcc13fef4f 100644 +index 07accfbcca491966c6c8ad9c20e146dbd22347f0..1079e3beb02e5f5d71a15fd2db65cb93ebd175d6 100644 --- a/src/env.h +++ b/src/env.h @@ -175,10 +175,6 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer { diff --git a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch index 4a6e3d5147779..ae9a03f622734 100644 --- a/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch +++ b/patches/node/src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch @@ -40,10 +40,10 @@ index 5641990e0bac455c33ddf7b9a865deba871516e7..bd757f42e02391abbeec007d9c4cea60 } HistogramBase* histogram; diff --git a/src/node_file.cc b/src/node_file.cc -index 5aea65d6800494def62829432ee48f9c06e65d63..80cf6648ed99241eea8c176c5c958d76fe33aa14 100644 +index 37172158d318d6569194fd3c5441d107e155e54c..41498615a37945111348e22b18214c3bcc9533a0 100644 --- a/src/node_file.cc +++ b/src/node_file.cc -@@ -1071,13 +1071,8 @@ static int32_t FastInternalModuleStat( +@@ -1074,13 +1074,8 @@ static int32_t FastInternalModuleStat( // NOLINTNEXTLINE(runtime/references) This is V8 api. FastApiCallbackOptions& options) { // This needs a HandleScope which needs an isolate. diff --git a/patches/node/support_v8_sandboxed_pointers.patch b/patches/node/support_v8_sandboxed_pointers.patch index 2aedd3ceeae9a..e0c48e287c723 100644 --- a/patches/node/support_v8_sandboxed_pointers.patch +++ b/patches/node/support_v8_sandboxed_pointers.patch @@ -189,7 +189,7 @@ index f616223cfb0f6e10f7cf57ada9704316bde2797e..eb6dad44a49d997097c8fb5009eeb60a Local ret; if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {}; diff --git a/src/node_i18n.cc b/src/node_i18n.cc -index ea7810e41e2667713a896250dc1b904b0a7cf198..865b3128c1edfe7074769f25a0b87878ca094f31 100644 +index 61b6ecd240c9500f21f683065a2f920af3afb502..ad2b1c76325cb5c8f18a618c5a85ae87b6a7bbe7 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -104,7 +104,7 @@ namespace { @@ -384,18 +384,3 @@ index 9787b14352753c5e0f8dc2b90093680e7cd10f1a..31af9e62396368af1b81f8841a705fd3 auto ab = ArrayBuffer::New(isolate, std::move(bs)); v8::Local u8 = v8::Uint8Array::New(ab, 0, 1); -diff --git a/test/parallel/test-buffer-tostring-range.js b/test/parallel/test-buffer-tostring-range.js -index 73fec107a36c3db4af6f492137d0ca174f2d0547..a1153ec381f7b12a1640b611073f6997e1ec5696 100644 ---- a/test/parallel/test-buffer-tostring-range.js -+++ b/test/parallel/test-buffer-tostring-range.js -@@ -102,8 +102,8 @@ assert.throws(() => { - // Must not throw when start and end are within kMaxLength - // Cannot test on 32bit machine as we are testing the case - // when start and end are above the threshold --if (!common.openSSLIsBoringSSL) { -+/* - const threshold = 0xFFFFFFFF; - const largeBuffer = Buffer.alloc(threshold + 20); - largeBuffer.toString('utf8', threshold, threshold + 20); --} -+*/ diff --git a/patches/node/test_formally_mark_some_tests_as_flaky.patch b/patches/node/test_formally_mark_some_tests_as_flaky.patch index 5dd4584847450..c137995d960c0 100644 --- a/patches/node/test_formally_mark_some_tests_as_flaky.patch +++ b/patches/node/test_formally_mark_some_tests_as_flaky.patch @@ -7,7 +7,7 @@ Instead of disabling the tests, flag them as flaky so they still run but don't cause CI failures on flakes. diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status -index cc99efd7a743d683d5210ad83e258560c28cbd16..b2f0f7fb49665f0dc924cdd3e344c2579d617fbf 100644 +index 8b9a4e80a08031085dde8a6952224e6ca3ca052e..2e83f166a34930083e697aaf06271e79cefe3f23 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -5,6 +5,16 @@ prefix parallel From fd131e30aaa839833cc8e19c2bb9abb7f7173fb7 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 2 Jul 2025 20:33:00 +0200 Subject: [PATCH 297/339] fix: silent printing of PDFs with `webContents.print` (#47624) fix: silent printing of PDFs with webContents.print --- patches/chromium/printing.patch | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index c28f38927c2be..0f7bfd5c1b2dc 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -666,7 +666,7 @@ index 6809c4576c71bc1e1a6ad4e0a37707272a9a10f4..3aad10424a6a31dab2ca393d00149ec6 PrintingFailed(int32 cookie, PrintFailureReason reason); diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc -index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3b3ffe41c 100644 +index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..bac316baaf4340848666085b3de69c99ee522abe 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc @@ -52,6 +52,7 @@ @@ -744,7 +744,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 print_preview_context_.OnPrintPreview(); #if BUILDFLAG(IS_CHROMEOS) -@@ -2062,17 +2068,19 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { +@@ -2062,17 +2068,25 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, const blink::WebNode& node, @@ -759,6 +759,12 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 FrameReference frame_ref(frame); - if (!InitPrintSettings(frame, node)) { ++ // If we're silently printing a PDF, we bypass settings logic ++ // that sets modifiability to false so ensure it's set here. ++ if (silent && IsPrintingPdfFrame(frame, node)) { ++ settings.Set(kSettingPreviewModifiable, false); ++ } ++ + if (!InitPrintSettings(frame, node, std::move(settings))) { // Browser triggered this code path. It already knows about the failure. notify_browser_of_print_failure_ = false; @@ -767,7 +773,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 DidFinishPrinting(PrintingResult::kFailPrintInit); return; } -@@ -2093,8 +2101,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, +@@ -2093,8 +2107,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, print_pages_params_->params->print_scaling_option; auto self = weak_ptr_factory_.GetWeakPtr(); @@ -784,7 +790,7 @@ index 08af2c8458fdc2539b437c3ff7e3fa2b5d36eeda..5a8d2f3cc57a66535afb027ee43eebc3 // Check if `this` is still valid. if (!self) return; -@@ -2362,29 +2377,43 @@ void PrintRenderFrameHelper::IPCProcessed() { +@@ -2362,29 +2383,43 @@ void PrintRenderFrameHelper::IPCProcessed() { } bool PrintRenderFrameHelper::InitPrintSettings(blink::WebLocalFrame* frame, From 729ceb4360e7d9fdcfe50276816b3e0b0ddee301 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 3 Jul 2025 15:32:00 +0200 Subject: [PATCH 298/339] fix: add back fallback wasm-trap handling (#47623) --- patches/chromium/.patches | 1 - ...le_freezing_flags_after_init_in_node.patch | 30 ---------- shell/renderer/electron_renderer_client.cc | 58 +++++++++++++++++++ shell/renderer/electron_renderer_client.h | 3 + 4 files changed, 61 insertions(+), 31 deletions(-) delete mode 100644 patches/chromium/disable_freezing_flags_after_init_in_node.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index e3ad536f9142f..ddb8f6e1976bb 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -80,7 +80,6 @@ introduce_ozoneplatform_electron_can_call_x11_property.patch make_gtk_getlibgtk_public.patch custom_protocols_plzserviceworker.patch feat_filter_out_non-shareable_windows_in_the_current_application_in.patch -disable_freezing_flags_after_init_in_node.patch short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch chore_add_electron_deps_to_gitignores.patch chore_modify_chromium_handling_of_mouse_events.patch diff --git a/patches/chromium/disable_freezing_flags_after_init_in_node.patch b/patches/chromium/disable_freezing_flags_after_init_in_node.patch deleted file mode 100644 index fa6e951731d35..0000000000000 --- a/patches/chromium/disable_freezing_flags_after_init_in_node.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeremy Rose -Date: Mon, 20 Jun 2022 14:53:37 -0700 -Subject: disable freezing flags after init in node - -This was introduced in -https://chromium-review.googlesource.com/c/chromium/src/+/3687671. - -When running node in the renderer, flags are updated after initialization, so -freezing the flags in Blink causes node initialization to fail. - -If possible, it would be ideal to do this without a patch. -https://bugs.chromium.org/p/v8/issues/detail?id=12887 suggests that there may -at some point be an API to "unfreeze" the flags, or we may be able to refactor -node initialization to not update flags after V8 initialization. - -diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc -index eb11068e932b7b94cbf215d6f84ae427ce77fcd5..9744e45974af215bfbe9e5feb2db7274f8efebf0 100644 ---- a/content/renderer/render_process_impl.cc -+++ b/content/renderer/render_process_impl.cc -@@ -208,6 +208,9 @@ RenderProcessImpl::RenderProcessImpl() - v8::V8::SetFlagsFromString(kSABPerContextFlag, sizeof(kSABPerContextFlag)); - } - -+ // Freezing flags after init conflicts with node in the renderer. -+ v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); -+ - if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) { - content::GetContentClient()->renderer()->SetUpWebAssemblyTrapHandler(); - } diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index c18b81d49ddee..6f1724f092e75 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -6,6 +6,7 @@ #include +#include "base/base_switches.h" #include "base/command_line.h" #include "base/containers/contains.h" #include "base/debug/stack_trace.h" @@ -25,6 +26,13 @@ #include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck +#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)) +#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX +#include "components/crash/core/app/crashpad.h" +#include "content/public/common/content_switches.h" +#include "v8/include/v8-wasm-trap-handler-posix.h" +#endif + namespace electron { ElectronRendererClient::ElectronRendererClient() @@ -35,6 +43,14 @@ ElectronRendererClient::ElectronRendererClient() ElectronRendererClient::~ElectronRendererClient() = default; +void ElectronRendererClient::PostIOThreadCreated( + base::SingleThreadTaskRunner* io_thread_task_runner) { + // Freezing flags after init conflicts with node in the renderer. + // We do this here in order to avoid having to patch the ctor in + // content/renderer/render_process_impl.cc. + v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); +} + void ElectronRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new ElectronRenderFrameObserver(render_frame, this); @@ -236,6 +252,48 @@ void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread( } } +void ElectronRendererClient::SetUpWebAssemblyTrapHandler() { +// See CL:5372409 - copied from ShellContentRendererClient. +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) + // Mac and Windows use the default implementation (where the default v8 trap + // handler gets set up). + ContentRendererClient::SetUpWebAssemblyTrapHandler(); + return; +#elif defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX) + const bool crash_reporter_enabled = + crash_reporter::GetHandlerSocket(nullptr, nullptr); + + if (crash_reporter_enabled) { + // If either --enable-crash-reporter or --enable-crash-reporter-for-testing + // is enabled it should take care of signal handling for us, use the default + // implementation which doesn't register an additional handler. + ContentRendererClient::SetUpWebAssemblyTrapHandler(); + return; + } + + const bool use_v8_default_handler = + base::CommandLine::ForCurrentProcess()->HasSwitch( + ::switches::kDisableInProcessStackTraces); + + if (use_v8_default_handler) { + // There is no signal handler yet, but it's okay if v8 registers one. + v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/true); + return; + } + + if (base::debug::SetStackDumpFirstChanceCallback( + v8::TryHandleWebAssemblyTrapPosix)) { + // Crashpad and Breakpad are disabled, but the in-process stack dump + // handlers are enabled, so set the callback on the stack dump handlers. + v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/false); + return; + } + + // As the registration of the callback failed, we don't enable trap + // handlers. +#endif // defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX) +} + node::Environment* ElectronRendererClient::GetEnvironment( content::RenderFrame* render_frame) const { if (!injected_frames_.contains(render_frame)) diff --git a/shell/renderer/electron_renderer_client.h b/shell/renderer/electron_renderer_client.h index 5471f872eb394..1cabd0a546a12 100644 --- a/shell/renderer/electron_renderer_client.h +++ b/shell/renderer/electron_renderer_client.h @@ -38,6 +38,8 @@ class ElectronRendererClient : public RendererClientBase { void UndeferLoad(content::RenderFrame* render_frame); // content::ContentRendererClient: + void PostIOThreadCreated( + base::SingleThreadTaskRunner* io_thread_task_runner) override; void RenderFrameCreated(content::RenderFrame*) override; void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; @@ -45,6 +47,7 @@ class ElectronRendererClient : public RendererClientBase { v8::Local context) override; void WillDestroyWorkerContextOnWorkerThread( v8::Local context) override; + void SetUpWebAssemblyTrapHandler() override; node::Environment* GetEnvironment(content::RenderFrame* frame) const; From ceefcd1d32dbf4eab3b66abb84b4399c30d2cc39 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:19:12 +0200 Subject: [PATCH 299/339] fix: crash on source capture with empty thumbnail size (#47651) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_desktop_capturer.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shell/browser/api/electron_api_desktop_capturer.cc b/shell/browser/api/electron_api_desktop_capturer.cc index d0b3f40b5dcc0..a5453ff8cd0f5 100644 --- a/shell/browser/api/electron_api_desktop_capturer.cc +++ b/shell/browser/api/electron_api_desktop_capturer.cc @@ -233,7 +233,8 @@ DesktopCapturer::DesktopListListener::~DesktopListListener() = default; void DesktopCapturer::DesktopListListener::OnDelegatedSourceListSelection() { if (have_thumbnail_) { - std::move(update_callback_).Run(); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + std::move(update_callback_)); } else { have_selection_ = true; } @@ -246,7 +247,8 @@ void DesktopCapturer::DesktopListListener::OnSourceThumbnailChanged(int index) { have_selection_ = false; // PipeWire returns a single source, so index is not relevant. - std::move(update_callback_).Run(); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + std::move(update_callback_)); } else { have_thumbnail_ = true; } From e39cf315dc2247fda8b39a875922b3afda07d3dc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 10:57:23 +0200 Subject: [PATCH 300/339] fix: accent color should reflect system settings without restart (#47657) fix: accentColor should reflect system settings without restart Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window_views.h | 2 +- shell/browser/native_window_views_win.cc | 44 ++++++++++++++++-------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 7f6c44793ea7e..ab75b7ad76d56 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -305,7 +305,7 @@ class NativeWindowViews : public NativeWindow, // Whether the window is currently being moved. bool is_moving_ = false; - std::variant accent_color_ = true; + std::variant accent_color_; std::optional pending_bounds_change_; diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 219fdaca646a6..159e77a9c0a18 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -572,28 +572,42 @@ void NativeWindowViews::UpdateWindowAccentColor() { if (base::win::GetVersion() < base::win::Version::WIN11) return; - if (!IsAccentColorOnTitleBarsEnabled()) - return; - COLORREF border_color; - if (std::holds_alternative(accent_color_)) { - // Don't set accent color if the user has disabled it. - if (!std::get(accent_color_)) - return; + bool should_apply_accent = false; - std::optional accent_color = GetAccentColor(); - if (!accent_color.has_value()) - return; - - border_color = - RGB(GetRValue(accent_color.value()), GetGValue(accent_color.value()), - GetBValue(accent_color.value())); - } else { + if (std::holds_alternative(accent_color_)) { + bool force_accent = std::get(accent_color_); + if (!force_accent) { + should_apply_accent = false; + } else { + std::optional accent_color = GetAccentColor(); + if (accent_color.has_value()) { + border_color = RGB(GetRValue(accent_color.value()), + GetGValue(accent_color.value()), + GetBValue(accent_color.value())); + should_apply_accent = true; + } + } + } else if (std::holds_alternative(accent_color_)) { SkColor color = std::get(accent_color_); border_color = RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); + should_apply_accent = true; + } else if (std::holds_alternative(accent_color_)) { + if (IsAccentColorOnTitleBarsEnabled()) { + std::optional accent_color = GetAccentColor(); + if (accent_color.has_value()) { + border_color = RGB(GetRValue(accent_color.value()), + GetGValue(accent_color.value()), + GetBValue(accent_color.value())); + should_apply_accent = true; + } + } } + // Reset to default system colors when accent color should not be applied. + if (!should_apply_accent) + border_color = DWMWA_COLOR_DEFAULT; SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), border_color); } From c9c4c36a72fde2dcfa6c70746189006f296e48a9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:21:41 +0200 Subject: [PATCH 301/339] fix: fullscreen for windows without rounded corners (#47683) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window_mac.h | 2 + shell/browser/native_window_mac.mm | 17 ++++--- shell/browser/ui/cocoa/electron_ns_window.mm | 3 -- .../ui/cocoa/electron_ns_window_delegate.h | 2 + .../ui/cocoa/electron_ns_window_delegate.mm | 5 ++ spec/api-browser-window-spec.ts | 48 +++++++++++++++++++ 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 5420e14aecd7d..9eb4df3c22d7c 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -175,6 +175,8 @@ class NativeWindowMac : public NativeWindow, // cleanup in destructor. void Cleanup(); + void SetBorderless(bool borderless); + void UpdateVibrancyRadii(bool fullscreen); void UpdateWindowOriginalFrame(); diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 8da2b2f36f628..6826f7ea80a2f 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -158,13 +158,6 @@ static bool FromV8(v8::Isolate* isolate, NSUInteger styleMask = NSWindowStyleMaskTitled; - // The NSWindowStyleMaskFullSizeContentView style removes rounded corners - // for frameless window. - const bool rounded_corner = - options.ValueOrDefault(options::kRoundedCorners, true); - if (!rounded_corner && !has_frame()) - styleMask = NSWindowStyleMaskBorderless; - if (minimizable) styleMask |= NSWindowStyleMaskMiniaturizable; if (closable) @@ -223,6 +216,11 @@ static bool FromV8(v8::Isolate* isolate, SetParentWindow(parent); } + const bool rounded_corner = + options.ValueOrDefault(options::kRoundedCorners, true); + if (!rounded_corner && !has_frame()) + SetBorderless(!rounded_corner); + if (transparent()) { // Setting the background color to clear will also hide the shadow. [window_ setBackgroundColor:[NSColor clearColor]]; @@ -774,6 +772,11 @@ static bool FromV8(v8::Isolate* isolate, [window_ orderWindowByShuffling:NSWindowAbove relativeTo:0]; } +void NativeWindowMac::SetBorderless(bool borderless) { + SetStyleMask(!borderless, NSWindowStyleMaskTitled); + SetStyleMask(borderless, NSWindowStyleMaskBorderless); +} + void NativeWindowMac::SetResizable(bool resizable) { ScopedDisableResize disable_resize; SetStyleMask(resizable, NSWindowStyleMaskResizable); diff --git a/shell/browser/ui/cocoa/electron_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm index fabd68cd7f1f1..e657539adf497 100644 --- a/shell/browser/ui/cocoa/electron_ns_window.mm +++ b/shell/browser/ui/cocoa/electron_ns_window.mm @@ -378,9 +378,6 @@ - (void)performClose:(id)sender { } - (BOOL)toggleFullScreenMode:(id)sender { - if (!shell_->has_frame() && !shell_->HasStyleMask(NSWindowStyleMaskTitled)) - return NO; - bool is_simple_fs = shell_->IsSimpleFullScreen(); bool always_simple_fs = shell_->always_simple_fullscreen(); diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.h b/shell/browser/ui/cocoa/electron_ns_window_delegate.h index f65d78a095d1b..1035f9e049e60 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.h +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.h @@ -24,6 +24,8 @@ class NativeWindowMac; int level_; bool is_resizable_; + bool is_borderless_; + // Whether the window is currently minimized. Used to work // around a macOS bug with child window minimization. bool is_minimized_; diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm index d2a64514f12e3..df7b7b66eadb4 100644 --- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -299,6 +299,8 @@ - (void)windowDidEndLiveResize:(NSNotification*)notification { - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Store resizable mask so it can be restored after exiting fullscreen. is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable); + // Store borderless mask so it can be restored after exiting fullscreen. + is_borderless_ = !shell_->HasStyleMask(NSWindowStyleMaskTitled); shell_->set_is_transitioning_fullscreen(true); @@ -306,6 +308,8 @@ - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Set resizable to true before entering fullscreen. shell_->SetResizable(true); + // Set borderless to false before entering fullscreen. + shell_->SetBorderless(false); } - (void)windowDidEnterFullScreen:(NSNotification*)notification { @@ -341,6 +345,7 @@ - (void)windowDidExitFullScreen:(NSNotification*)notification { shell_->set_is_transitioning_fullscreen(false); shell_->SetResizable(is_resizable_); + shell_->SetBorderless(is_borderless_); shell_->NotifyWindowLeaveFullScreen(); if (shell_->HandleDeferredClose()) diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 5cd300f481c40..c69bef807708d 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -5993,6 +5993,54 @@ describe('BrowserWindow module', () => { await leaveFullScreen; }); + it('should not crash if rounded corners are disabled', async () => { + const w = new BrowserWindow({ + frame: false, + roundedCorners: false + }); + + const enterFullScreen = once(w, 'enter-full-screen'); + w.setFullScreen(true); + await enterFullScreen; + + await setTimeout(); + + const leaveFullScreen = once(w, 'leave-full-screen'); + w.setFullScreen(false); + await leaveFullScreen; + }); + + it('should not crash if opening a borderless child window from fullscreen parent', async () => { + const parent = new BrowserWindow(); + + const parentFS = once(parent, 'enter-full-screen'); + parent.setFullScreen(true); + await parentFS; + + await setTimeout(); + + const child = new BrowserWindow({ + width: 400, + height: 300, + show: false, + parent, + frame: false, + roundedCorners: false + }); + + await setTimeout(); + + const childFS = once(child, 'enter-full-screen'); + child.show(); + await childFS; + + await setTimeout(); + + const leaveFullScreen = once(child, 'leave-full-screen'); + child.setFullScreen(false); + await leaveFullScreen; + }); + it('should be able to load a URL while transitioning to fullscreen', async () => { const w = new BrowserWindow({ fullscreen: true }); w.loadFile(path.join(fixtures, 'pages', 'c.html')); From 1169e25f9d5a0cd9d98b8268a2a09fde7c043f93 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 12:31:50 +0200 Subject: [PATCH 302/339] docs: update build prerequisites (#47697) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/development/build-instructions-linux.md | 7 ++----- docs/development/build-instructions-macos.md | 4 ++-- docs/development/build-instructions-windows.md | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/development/build-instructions-linux.md b/docs/development/build-instructions-linux.md index 8bfd6349a511a..07cd2c49d091a 100644 --- a/docs/development/build-instructions-linux.md +++ b/docs/development/build-instructions-linux.md @@ -7,11 +7,8 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p ## Prerequisites * At least 25GB disk space and 8GB RAM. -* Python >= 3.7. -* Node.js. There are various ways to install Node. You can download - source code from [nodejs.org](https://nodejs.org) and compile it. - Doing so permits installing Node on your own home directory as a standard user. - Or try repositories such as [NodeSource](https://nodesource.com/blog/nodejs-v012-iojs-and-the-nodesource-linux-repositories). +* Python >= 3.9. +* [Node.js](https://nodejs.org/download/) >= 22.12.0 * [clang](https://clang.llvm.org/get_started.html) 3.4 or later. * Development headers of GTK 3 and libnotify. diff --git a/docs/development/build-instructions-macos.md b/docs/development/build-instructions-macos.md index 8ab4670d1971c..cc3ab4fc8a7dc 100644 --- a/docs/development/build-instructions-macos.md +++ b/docs/development/build-instructions-macos.md @@ -10,8 +10,8 @@ Follow the guidelines below for building **Electron itself** on macOS, for the p * [Xcode](https://developer.apple.com/technologies/tools/). The exact version needed depends on what branch you are building, but the latest version of Xcode is generally a good bet for building `main`. -* [node.js](https://nodejs.org) (external) -* Python >= 3.7 +* Python >= 3.9 +* [Node.js](https://nodejs.org/download/) >= 22.12.0 ### Arm64-specific prerequisites diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 0d1a079c5d705..3ff011c45e04e 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -14,7 +14,7 @@ Follow the guidelines below for building **Electron itself** on Windows, for the set a few environment variables to point the toolchains to your installation path. * `vs2022_install = DRIVE:\path\to\Microsoft Visual Studio\2022\Community`, replacing `2022` and `Community` with your installed versions and replacing `DRIVE:` with the drive that Visual Studio is on. Often, this will be `C:`. * `WINDOWSSDKDIR = DRIVE:\path\to\Windows Kits\10`, replacing `DRIVE:` with the drive that Windows Kits is on. Often, this will be `C:`. -* [Node.js](https://nodejs.org/download/) +* [Node.js](https://nodejs.org/download/) >= 22.12.0 * [Git](https://git-scm.com) * Debugging Tools for Windows of Windows SDK 10.0.15063.468 if you plan on creating a full distribution since `symstore.exe` is used for creating a symbol From 3c396a674ef984c08f93a709672a107e04f1e62f Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 15:57:15 +0200 Subject: [PATCH 303/339] ci: set git `core.longpaths` to true (#47712) ci: set git core.longpaths to true Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/install-build-tools/action.yml | 1 + .github/workflows/pipeline-segment-electron-test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/actions/install-build-tools/action.yml b/.github/actions/install-build-tools/action.yml index 12cffbc835519..b401df9717820 100644 --- a/.github/actions/install-build-tools/action.yml +++ b/.github/actions/install-build-tools/action.yml @@ -11,6 +11,7 @@ runs: git config --global core.autocrlf false git config --global branch.autosetuprebase always git config --global core.fscache true + git config --global core.longpaths true git config --global core.preloadindex true fi export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 369bb86104703..978b5202d4cbf 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -135,6 +135,7 @@ jobs: git config --global core.autocrlf false git config --global branch.autosetuprebase always git config --global core.fscache true + git config --global core.longpaths true git config --global core.preloadindex true git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git # Ensure depot_tools does not update. From ed82dba57e687fc32cc7dd328a2d94fcd341a929 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:58:41 -0400 Subject: [PATCH 304/339] build: cleanup symlinks in cache (#47730) * build: cleanup symlinks in cache Co-authored-by: John Kleinschmidt * build: ignore broken links Co-authored-by: John Kleinschmidt * try --ignore-failed-read Co-authored-by: John Kleinschmidt * build: dont deref symlinks Co-authored-by: John Kleinschmidt * build: add flag to 7zip to resolve symlink error Needed to ignore Dangerous symbolic link path was ignored errors Co-authored-by: John Kleinschmidt * Revert "build: cleanup symlinks in cache" This reverts commit 69e53cdc8850a62af3cb8cb1d4b6246b1e378c29. Co-authored-by: John Kleinschmidt --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/restore-cache-azcopy/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index 22e82e0f97843..2272ee6e76876 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -96,7 +96,7 @@ runs: $TEMP_DIR=New-Item -ItemType Directory -Path temp-cache $TEMP_DIR_PATH = $TEMP_DIR.FullName - C:\ProgramData\Chocolatey\bin\7z.exe -y x $src_cache -o"$TEMP_DIR_PATH" + C:\ProgramData\Chocolatey\bin\7z.exe -y -snld x $src_cache -o"$TEMP_DIR_PATH" - name: Move Src Cache (Windows) if: ${{ inputs.target-platform == 'win' }} From b4a439fb4fafb806774dcb3803ac1cf7578730e9 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 14 Jul 2025 01:07:03 -0700 Subject: [PATCH 305/339] build: drop eslint-plugin-unicorn (#47689) --- build/.eslintrc.json | 4 +- default_app/.eslintrc.json | 4 +- package.json | 3 +- script/.eslintrc.json | 4 +- spec/.eslintrc.json | 6 +- spec/fixtures/module/preload-sandbox.js | 6 +- yarn.lock | 988 +++++++++++++++++++----- 7 files changed, 789 insertions(+), 226 deletions(-) diff --git a/build/.eslintrc.json b/build/.eslintrc.json index dc7dde78dc189..35c8df531937d 100644 --- a/build/.eslintrc.json +++ b/build/.eslintrc.json @@ -1,8 +1,8 @@ { "plugins": [ - "unicorn" + "import" ], "rules": { - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/default_app/.eslintrc.json b/default_app/.eslintrc.json index dc7dde78dc189..35c8df531937d 100644 --- a/default_app/.eslintrc.json +++ b/default_app/.eslintrc.json @@ -1,8 +1,8 @@ { "plugins": [ - "unicorn" + "import" ], "rules": { - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/package.json b/package.json index d2bfd9bfa718f..8f6c837ddfa47 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,12 @@ "dugite": "^2.7.1", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.30.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-mocha": "^10.5.0", "eslint-plugin-n": "^16.6.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.6.0", "eslint-plugin-standard": "^5.0.0", - "eslint-plugin-unicorn": "^55.0.0", "events": "^3.2.0", "folder-hash": "^2.1.1", "got": "^11.8.5", diff --git a/script/.eslintrc.json b/script/.eslintrc.json index dc7dde78dc189..35c8df531937d 100644 --- a/script/.eslintrc.json +++ b/script/.eslintrc.json @@ -1,8 +1,8 @@ { "plugins": [ - "unicorn" + "import" ], "rules": { - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/spec/.eslintrc.json b/spec/.eslintrc.json index 364b1639cbaef..83b471f885996 100644 --- a/spec/.eslintrc.json +++ b/spec/.eslintrc.json @@ -11,11 +11,11 @@ "WebView": true }, "plugins": [ - "mocha", - "unicorn" + "import", + "mocha" ], "rules": { "mocha/no-exclusive-tests": "error", - "unicorn/prefer-node-protocol": "error" + "import/enforce-node-protocol-usage": ["error", "always"] } } diff --git a/spec/fixtures/module/preload-sandbox.js b/spec/fixtures/module/preload-sandbox.js index ce0b0d3d816a2..f6a4d55e55b7a 100644 --- a/spec/fixtures/module/preload-sandbox.js +++ b/spec/fixtures/module/preload-sandbox.js @@ -33,11 +33,11 @@ systemVersion: invoke(() => process.getSystemVersion()), cpuUsage: invoke(() => process.getCPUUsage()), uptime: invoke(() => process.uptime()), - // eslint-disable-next-line unicorn/prefer-node-protocol + // eslint-disable-next-line import/enforce-node-protocol-usage nodeEvents: invoke(() => require('events') === require('node:events')), - // eslint-disable-next-line unicorn/prefer-node-protocol + // eslint-disable-next-line import/enforce-node-protocol-usage nodeTimers: invoke(() => require('timers') === require('node:timers')), - // eslint-disable-next-line unicorn/prefer-node-protocol + // eslint-disable-next-line import/enforce-node-protocol-usage nodeUrl: invoke(() => require('url') === require('node:url')), env: process.env, execPath: process.execPath, diff --git a/yarn.lock b/yarn.lock index ad0ed30e207f1..4ff9dd5afc01d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -156,11 +156,6 @@ "@babel/highlight" "^7.25.7" picocolors "^1.0.0" -"@babel/helper-validator-identifier@^7.24.5": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" - integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== - "@babel/helper-validator-identifier@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz#77b7f60c40b15c97df735b38a66ba1d7c3e93da5" @@ -999,11 +994,6 @@ dependencies: undici-types "~6.19.2" -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1472,6 +1462,14 @@ array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" +array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + array-includes@^3.1.5, array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" @@ -1483,34 +1481,37 @@ array-includes@^3.1.5, array-includes@^3.1.6: get-intrinsic "^1.1.3" is-string "^1.0.7" -array-includes@^3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" - integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== +array-includes@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - is-string "^1.0.7" + es-abstract "^1.24.0" + es-object-atoms "^1.1.1" + get-intrinsic "^1.3.0" + is-string "^1.1.1" + math-intrinsics "^1.1.0" array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.findlastindex@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" - integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== +array.prototype.findlastindex@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.2" + es-abstract "^1.23.9" es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" + es-object-atoms "^1.1.1" + es-shim-unscopables "^1.1.0" array.prototype.flat@^1.3.1: version "1.3.1" @@ -1522,15 +1523,15 @@ array.prototype.flat@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flat@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" - integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== +array.prototype.flat@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" array.prototype.flatmap@^1.3.1: version "1.3.1" @@ -1542,15 +1543,15 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" - integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== +array.prototype.flatmap@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" array.prototype.tosorted@^1.1.1: version "1.1.1" @@ -1577,6 +1578,19 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -1592,6 +1606,11 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + async@^3.2.0: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -1671,7 +1690,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.21.10, browserslist@^4.23.3: +browserslist@^4.21.10: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -1744,6 +1763,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -1763,6 +1790,24 @@ call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1876,13 +1921,6 @@ ci-info@^4.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== -clean-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" - integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw== - dependencies: - escape-string-regexp "^1.0.5" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -2013,13 +2051,6 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -core-js-compat@^3.37.0: - version "3.38.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" - integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== - dependencies: - browserslist "^4.23.3" - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2054,6 +2085,15 @@ data-view-buffer@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" @@ -2063,6 +2103,15 @@ data-view-byte-length@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-offset@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" @@ -2072,6 +2121,15 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" @@ -2195,6 +2253,15 @@ dugite@^2.7.1: progress "^2.0.3" tar "^6.1.11" +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2399,6 +2466,66 @@ es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23 unbox-primitive "^1.0.2" which-typed-array "^1.1.15" +es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-negative-zero "^2.0.3" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + es-define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" @@ -2406,6 +2533,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" @@ -2423,6 +2555,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -2441,6 +2580,16 @@ es-set-tostringtag@^2.0.3: has-tostringtag "^1.0.2" hasown "^2.0.1" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -2455,6 +2604,13 @@ es-shim-unscopables@^1.0.2: dependencies: hasown "^2.0.0" +es-shim-unscopables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== + dependencies: + hasown "^2.0.2" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2464,6 +2620,15 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + es6-error@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" @@ -2524,6 +2689,13 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" +eslint-module-utils@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" + integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== + dependencies: + debug "^3.2.7" + eslint-module-utils@^2.7.4: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" @@ -2531,13 +2703,6 @@ eslint-module-utils@^2.7.4: dependencies: debug "^3.2.7" -eslint-module-utils@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz#b99b211ca4318243f09661fae088f373ad5243c4" - integrity sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ== - dependencies: - debug "^3.2.7" - eslint-plugin-es-x@^7.5.0: version "7.8.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" @@ -2584,28 +2749,29 @@ eslint-plugin-import@^2.26.0: semver "^6.3.0" tsconfig-paths "^3.14.1" -eslint-plugin-import@^2.30.0: - version "2.30.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz#21ceea0fc462657195989dd780e50c92fe95f449" - integrity sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw== +eslint-plugin-import@^2.32.0: + version "2.32.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" + integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== dependencies: "@rtsao/scc" "^1.1.0" - array-includes "^3.1.8" - array.prototype.findlastindex "^1.2.5" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" + array-includes "^3.1.9" + array.prototype.findlastindex "^1.2.6" + array.prototype.flat "^1.3.3" + array.prototype.flatmap "^1.3.3" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.9.0" + eslint-module-utils "^2.12.1" hasown "^2.0.2" - is-core-module "^2.15.1" + is-core-module "^2.16.1" is-glob "^4.0.3" minimatch "^3.1.2" object.fromentries "^2.0.8" object.groupby "^1.0.3" - object.values "^1.2.0" + object.values "^1.2.1" semver "^6.3.1" + string.prototype.trimend "^1.0.9" tsconfig-paths "^3.15.0" eslint-plugin-mocha@^10.5.0: @@ -2696,28 +2862,6 @@ eslint-plugin-standard@^5.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-5.0.0.tgz#c43f6925d669f177db46f095ea30be95476b1ee4" integrity sha512-eSIXPc9wBM4BrniMzJRBm2uoVuXz2EPa+NXPk2+itrVt+r5SbKFERx/IgrK/HmfjddyKVz2f+j+7gBRvu19xLg== -eslint-plugin-unicorn@^55.0.0: - version "55.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz#e2aeb397914799895702480970e7d148df5bcc7b" - integrity sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA== - dependencies: - "@babel/helper-validator-identifier" "^7.24.5" - "@eslint-community/eslint-utils" "^4.4.0" - ci-info "^4.0.0" - clean-regexp "^1.0.0" - core-js-compat "^3.37.0" - esquery "^1.5.0" - globals "^15.7.0" - indent-string "^4.0.0" - is-builtin-module "^3.2.1" - jsesc "^3.0.2" - pluralize "^8.0.0" - read-pkg-up "^7.0.1" - regexp-tree "^0.1.27" - regjsparser "^0.10.0" - semver "^7.6.1" - strip-indent "^3.0.0" - eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -2828,13 +2972,6 @@ esquery@^1.4.2: dependencies: estraverse "^5.1.0" -esquery@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== - dependencies: - estraverse "^5.1.0" - esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -2999,7 +3136,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -3044,6 +3181,13 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -3117,6 +3261,18 @@ function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" +function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -3153,11 +3309,35 @@ get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stdin@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" @@ -3187,6 +3367,15 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + get-tsconfig@^4.7.0: version "4.8.1" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" @@ -3280,11 +3469,6 @@ globals@^13.24.0: dependencies: type-fest "^0.20.2" -globals@^15.7.0: - version "15.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-15.9.0.tgz#e9de01771091ffbc37db5714dab484f9f69ff399" - integrity sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA== - globalthis@^1.0.1, globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -3292,6 +3476,14 @@ globalthis@^1.0.1, globalthis@^1.0.3: dependencies: define-properties "^1.1.3" +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + globby@14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-14.1.0.tgz#138b78e77cf5a8d794e327b15dce80bf1fb0a73e" @@ -3311,6 +3503,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + got@^11.8.5: version "11.8.5" resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" @@ -3387,11 +3584,23 @@ has-proto@^1.0.3: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -3464,11 +3673,6 @@ hastscript@^8.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - hosted-git-info@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" @@ -3602,6 +3806,15 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -3642,11 +3855,31 @@ is-array-buffer@^3.0.4: call-bind "^1.0.2" get-intrinsic "^1.2.1" +is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -3654,6 +3887,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3669,6 +3909,14 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" @@ -3688,13 +3936,20 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" -is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.15.1: +is-core-module@^2.12.1, is-core-module@^2.13.0: version "2.15.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: hasown "^2.0.2" +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-core-module@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -3716,6 +3971,15 @@ is-data-view@^1.0.1: dependencies: is-typed-array "^1.1.13" +is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -3723,6 +3987,14 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + is-decimal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.0.tgz#db1140337809fd043a056ae40a9bd1cdc563034c" @@ -3738,11 +4010,28 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -3767,6 +4056,11 @@ is-interactive@^2.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3784,6 +4078,14 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3824,11 +4126,26 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -3843,6 +4160,13 @@ is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" @@ -3855,6 +4179,14 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -3862,6 +4194,15 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + is-typed-array@^1.1.10, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" @@ -3880,6 +4221,13 @@ is-typed-array@^1.1.13: dependencies: which-typed-array "^1.1.14" +is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + is-unicode-supported@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" @@ -3890,6 +4238,11 @@ is-unicode-supported@^2.0.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3897,6 +4250,21 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -3960,16 +4328,6 @@ js-yaml@^3.2.7: argparse "^1.0.7" esprima "^4.0.0" -jsesc@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - json-buffer@3.0.1, json-buffer@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -4376,6 +4734,11 @@ matcher@^3.0.0: dependencies: escape-string-regexp "^4.0.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + mdast-comment-marker@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/mdast-comment-marker/-/mdast-comment-marker-1.1.1.tgz#9c9c18e1ed57feafc1965d92b028f37c3c8da70d" @@ -4824,11 +5187,6 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -4964,16 +5322,6 @@ nopt@^7.2.1: dependencies: abbrev "^2.0.0" -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-package-data@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" @@ -5055,6 +5403,11 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5080,6 +5433,18 @@ object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + object.entries@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" @@ -5134,12 +5499,13 @@ object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" -object.values@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" - integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== +object.values@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" es-object-atoms "^1.0.0" @@ -5191,6 +5557,15 @@ ora@^8.1.0: string-width "^7.2.0" strip-ansi "^7.1.0" +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + p-cancelable@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" @@ -5579,25 +5954,6 @@ read-package-json-fast@^3.0.0: json-parse-even-better-errors "^3.0.0" npm-normalize-package-bin "^3.0.0" -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - readable-stream@^2, readable-stream@^2.0.1, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -5641,10 +5997,19 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" -regexp-tree@^0.1.27: - version "0.1.27" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" - integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" regexp.prototype.flags@^1.4.3: version "1.5.0" @@ -5665,18 +6030,23 @@ regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" +regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + regexpp@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== -regjsparser@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892" - integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA== - dependencies: - jsesc "~0.5.0" - remark-cli@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/remark-cli/-/remark-cli-12.0.1.tgz#991ede01adfdf0471177c381168105da4b93f99a" @@ -6252,15 +6622,6 @@ resolve@^1.1.6: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.10.0, resolve@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== - dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - resolve@^1.10.1: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -6277,6 +6638,15 @@ resolve@^1.20.0, resolve@^1.22.2, resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -6374,6 +6744,17 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -6384,6 +6765,14 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -6402,6 +6791,15 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" @@ -6416,11 +6814,6 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -6433,7 +6826,7 @@ semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.8: dependencies: lru-cache "^6.0.0" -semver@^7.1.1, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.1, semver@^7.6.3: +semver@^7.1.1, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -6452,7 +6845,7 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -set-function-length@^1.2.1: +set-function-length@^1.2.1, set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -6464,7 +6857,7 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" -set-function-name@^2.0.1: +set-function-name@^2.0.1, set-function-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== @@ -6474,6 +6867,15 @@ set-function-name@^2.0.1: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -6510,6 +6912,35 @@ shx@^0.3.4: minimist "^1.2.3" shelljs "^0.8.5" +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -6529,6 +6960,17 @@ side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -6667,6 +7109,14 @@ stdin-discarder@^0.2.2: resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be" integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + stream-chain@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09" @@ -6743,6 +7193,19 @@ string.prototype.matchall@^4.0.8: regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" @@ -6780,6 +7243,16 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -6852,13 +7325,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -7117,16 +7583,6 @@ type-fest@^0.3.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-fest@^3.8.0: version "3.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" @@ -7141,6 +7597,15 @@ typed-array-buffer@^1.0.2: es-errors "^1.3.0" is-typed-array "^1.1.13" +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + typed-array-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" @@ -7152,6 +7617,17 @@ typed-array-byte-length@^1.0.1: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + typed-array-byte-offset@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" @@ -7164,6 +7640,19 @@ typed-array-byte-offset@^1.0.2: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -7185,6 +7674,18 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -7210,6 +7711,16 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" @@ -7428,7 +7939,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: +validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -7662,6 +8173,46 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" @@ -7673,6 +8224,19 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.15: gopd "^1.0.1" has-tostringtag "^1.0.2" +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" From f4bc4c29991a855422a8f03c50ee11774077d0e0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:45:41 +0200 Subject: [PATCH 306/339] ci: add `kTCCServiceAppleEvents` perm override to fix AppleScript errors (#47736) ci: add kTCCServiceAppleEvents perm override to fix AppleScript errors Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/workflows/pipeline-segment-electron-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 978b5202d4cbf..fe2c7fb62fec6 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -103,6 +103,7 @@ jobs: "'kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" ) for values in "${userValuesArray[@]}"; do # Sonoma and higher have a few extra values From 4e86467751f9d30f7cb5dc47a511f2e586eacfa0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:31:53 +0200 Subject: [PATCH 307/339] test: add response to bluetooth request possibilities (#47744) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- spec/chromium-spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 90cfe8c8b92eb..da0a183d1e7b1 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3637,8 +3637,14 @@ describe('navigator.bluetooth', () => { it('can request bluetooth devices', async () => { const bluetooth = await w.webContents.executeJavaScript(` - navigator.bluetooth.requestDevice({ acceptAllDevices: true}).then(device => "Found a device!").catch(err => err.message);`, true); - expect(bluetooth).to.be.oneOf(['Found a device!', 'Bluetooth adapter not available.', 'User cancelled the requestDevice() chooser.']); + navigator.bluetooth.requestDevice({ acceptAllDevices: true }).then(device => "Found a device!").catch(err => err.message);`, true); + const requestResponses = [ + 'Found a device!', + 'Bluetooth adapter not available.', + 'User cancelled the requestDevice() chooser.', + 'User denied the browser permission to scan for Bluetooth devices.' + ]; + expect(bluetooth).to.be.oneOf(requestResponses, `Unexpected response: ${bluetooth}`); }); }); @@ -3668,6 +3674,7 @@ describe('navigator.hid', () => { server.close(); closeAllWindows(); }); + afterEach(() => { session.defaultSession.setPermissionCheckHandler(null); session.defaultSession.setDevicePermissionHandler(null); From 99cda5805dad31d2e08263c166184260cbddd079 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:58:09 +0200 Subject: [PATCH 308/339] fix: missing SQLite builtin support in Node.js (#47755) https://github.com/nodejs/node/pull/58122 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- patches/node/build_add_gn_build_files.patch | 48 ++++++++++++++----- ...w_unbundling_of_node_js_dependencies.patch | 8 ++-- ...o_use_custom_inspector_protocol_path.patch | 6 +-- ...ingssl_and_openssl_incompatibilities.patch | 2 +- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 28ee7c05fe8b0..86ea0937ab059 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -24,7 +24,7 @@ index 4560bac7b8e3c707ecea5a425f642efb9de9ed36..e9c2a4391f4058a21a259cacaac4fde5 o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 diff --git a/node.gni b/node.gni -index 35ccd0487f20cece033d58827ecb7ed016908ee4..b4450e3dd17994d1eaf59eb5cff5912545e89793 100644 +index 35ccd0487f20cece033d58827ecb7ed016908ee4..62cd49c6a87074912a1cb6792576c8d4f239b669 100644 --- a/node.gni +++ b/node.gni @@ -5,10 +5,10 @@ @@ -40,7 +40,15 @@ index 35ccd0487f20cece033d58827ecb7ed016908ee4..b4450e3dd17994d1eaf59eb5cff59125 # The location of OpenSSL - use the one from node's deps by default. node_openssl_path = "$node_path/deps/openssl" -@@ -44,7 +44,7 @@ declare_args() { +@@ -39,12 +39,15 @@ declare_args() { + # The variable is called "openssl" for parity with node's GYP build. + node_use_openssl = true + ++ # Build node with SQLite support. ++ node_use_sqlite = true ++ + # Use the specified path to system CA (PEM format) in addition to + # the BoringSSL supplied CA store or compiled-in Mozilla CA copy. node_openssl_system_ca_path = "" # Initialize v8 platform during node.js startup. @@ -49,7 +57,7 @@ index 35ccd0487f20cece033d58827ecb7ed016908ee4..b4450e3dd17994d1eaf59eb5cff59125 # Custom build tag. node_tag = "" -@@ -64,10 +64,16 @@ declare_args() { +@@ -64,10 +67,16 @@ declare_args() { # TODO(zcbenz): There are few broken things for now: # 1. cross-os compilation is not supported. # 2. node_mksnapshot crashes when cross-compiling for x64 from arm64. @@ -271,10 +279,22 @@ index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd1 if sys.platform == 'win32': files = [ x.replace('\\', '/') for x in files ] diff --git a/unofficial.gni b/unofficial.gni -index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187f3c04c5c 100644 +index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3c1c8bd1b 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -142,32 +142,42 @@ template("node_gn_build") { +@@ -22,6 +22,11 @@ template("node_gn_build") { + } else { + defines += [ "HAVE_OPENSSL=0" ] + } ++ if (node_use_sqlite) { ++ defines += [ "HAVE_SQLITE=1" ] ++ } else { ++ defines += [ "HAVE_SQLITE=0" ] ++ } + if (node_use_amaro) { + defines += [ "HAVE_AMARO=1" ] + } else { +@@ -142,32 +147,41 @@ template("node_gn_build") { public_configs = [ ":node_external_config", "deps/googletest:googletest_config", @@ -297,7 +317,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 "deps/nghttp2", - "deps/ngtcp2", "deps/postject", - "deps/sqlite", +- "deps/sqlite", "deps/uvwasi", - "deps/zstd", "//third_party/zlib", @@ -320,7 +340,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 "$target_gen_dir/node_javascript.cc", ] + gypi_values.node_sources -@@ -190,7 +200,7 @@ template("node_gn_build") { +@@ -190,9 +204,13 @@ template("node_gn_build") { } if (node_use_openssl) { deps += [ "deps/ncrypto" ] @@ -328,8 +348,14 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 + public_deps += [ "//third_party/boringssl" ] sources += gypi_values.node_crypto_sources } ++ if (node_use_sqlite) { ++ deps += [ "deps/sqlite" ] ++ sources += gypi_values.node_sqlite_sources ++ } if (node_enable_inspector) { -@@ -214,6 +224,10 @@ template("node_gn_build") { + deps += [ + "src/inspector:crdtp", +@@ -214,6 +232,10 @@ template("node_gn_build") { } } @@ -340,7 +366,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 executable(target_name) { forward_variables_from(invoker, "*") -@@ -288,6 +302,7 @@ template("node_gn_build") { +@@ -288,6 +310,7 @@ template("node_gn_build") { } executable("node_js2c") { @@ -348,7 +374,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 deps = [ "deps/uv", "$node_simdutf_path", -@@ -298,26 +313,75 @@ template("node_gn_build") { +@@ -298,26 +321,75 @@ template("node_gn_build") { "src/embedded_data.cc", "src/embedded_data.h", ] @@ -434,7 +460,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..e17e4f043af6e4047ab82723ffd83187 outputs = [ "$target_gen_dir/node_javascript.cc" ] # Get the path to node_js2c executable of the host toolchain. -@@ -331,11 +395,11 @@ template("node_gn_build") { +@@ -331,11 +403,11 @@ template("node_gn_build") { get_label_info(":node_js2c($host_toolchain)", "name") + host_executable_suffix diff --git a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch index c36f65af02e60..d93e9bef3bc21 100644 --- a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch +++ b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch @@ -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 e17e4f043af6e4047ab82723ffd83187f3c04c5c..d591dfc99fdea4f830008502786ee44d863a31fc 100644 +index a6cfd45b109c7b38fcf1529468ff64d3c1c8bd1b..332c9ee7262108ae9616e9bc8bd950a4940a858c 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -155,7 +155,6 @@ template("node_gn_build") { +@@ -160,7 +160,6 @@ template("node_gn_build") { ":run_node_js2c", "deps/cares", "deps/histogram", @@ -25,7 +25,7 @@ index e17e4f043af6e4047ab82723ffd83187f3c04c5c..d591dfc99fdea4f830008502786ee44d "deps/nbytes", "deps/nghttp2", "deps/postject", -@@ -194,7 +193,17 @@ template("node_gn_build") { +@@ -198,7 +197,17 @@ template("node_gn_build") { configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] configs += [ "//build/config/gcc:symbol_visibility_default" ] } @@ -44,7 +44,7 @@ index e17e4f043af6e4047ab82723ffd83187f3c04c5c..d591dfc99fdea4f830008502786ee44d if (v8_enable_i18n_support) { deps += [ "//third_party/icu" ] } -@@ -222,6 +231,19 @@ template("node_gn_build") { +@@ -230,6 +239,19 @@ template("node_gn_build") { sources += node_inspector.node_inspector_sources + node_inspector.node_inspector_generated_sources } diff --git a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch index 5b00462181125..339cddb7a744e 100644 --- a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch +++ b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch @@ -12,7 +12,7 @@ protocol deps to contain https://chromium-review.googlesource.com/c/v8/v8/+/5996 Rest of the changes can be upstreamed. diff --git a/node.gni b/node.gni -index 203b4abbc44df9e58083c819f61f9025104abdc6..73bf3839866a2652ca660f1117e8f249d33fa46a 100644 +index 165b26a79a7f2b74d2a2252dc2350b2e10c091e6..c64761b730e61edcdc0e46a48699f2fd5bb1c0a6 100644 --- a/node.gni +++ b/node.gni @@ -16,6 +16,9 @@ declare_args() { @@ -39,10 +39,10 @@ index 3d7aa148678b2646b88fa7c32abec91791b02b82..4810d93eb971b253f7dadff7011a632f gypi_values = exec_script( "../../tools/gypi_to_gn.py", diff --git a/unofficial.gni b/unofficial.gni -index d591dfc99fdea4f830008502786ee44d863a31fc..9e26399482d6a1cdb843efb72c152d5cdd5e08ea 100644 +index 332c9ee7262108ae9616e9bc8bd950a4940a858c..8886f2a79ae77614789d6ae0defd4f18fc756456 100644 --- a/unofficial.gni +++ b/unofficial.gni -@@ -214,13 +214,14 @@ template("node_gn_build") { +@@ -222,13 +222,14 @@ template("node_gn_build") { } if (node_enable_inspector) { deps += [ diff --git a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch index 134bd2a779163..d4dcdd86a2023 100644 --- a/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch +++ b/patches/node/fix_handle_boringssl_and_openssl_incompatibilities.patch @@ -172,7 +172,7 @@ index e5bf2b529bf23914677e25d7468aad58a4684557..9a3c6029ff3319cce58c79782a7bd5d1 }; // Check to see if the given public key is suitable for this DH instance. diff --git a/node.gni b/node.gni -index b4450e3dd17994d1eaf59eb5cff5912545e89793..203b4abbc44df9e58083c819f61f9025104abdc6 100644 +index 62cd49c6a87074912a1cb6792576c8d4f239b669..165b26a79a7f2b74d2a2252dc2350b2e10c091e6 100644 --- a/node.gni +++ b/node.gni @@ -11,7 +11,7 @@ declare_args() { From 8c747d82e174a36fb15032bc50b22fc22d5a9df7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 12:03:41 -0700 Subject: [PATCH 309/339] test: deflake clipboard read/write specs (#47786) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- spec/chromium-spec.ts | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index da0a183d1e7b1..f7db52bf31ba5 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3271,7 +3271,12 @@ describe('navigator.clipboard.read', () => { await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); }); - const readClipboard: any = () => { + const readClipboard = async () => { + if (!w.webContents.isFocused()) { + const focus = once(w.webContents, 'focus'); + w.webContents.focus(); + await focus; + } return w.webContents.executeJavaScript(` navigator.clipboard.read().then(clipboard => clipboard.toString()).catch(err => err.message); `, true); @@ -3289,11 +3294,7 @@ describe('navigator.clipboard.read', () => { it('returns an error when permission denied', async () => { session.defaultSession.setPermissionRequestHandler((wc, permission, callback) => { - if (permission === 'clipboard-read') { - callback(false); - } else { - callback(true); - } + callback(permission !== 'clipboard-read'); }); const clipboard = await readClipboard(); expect(clipboard).to.contain('Read permission denied.'); @@ -3301,11 +3302,7 @@ describe('navigator.clipboard.read', () => { it('returns clipboard contents when permission is granted', async () => { session.defaultSession.setPermissionRequestHandler((wc, permission, callback) => { - if (permission === 'clipboard-read') { - callback(true); - } else { - callback(false); - } + callback(permission === 'clipboard-read'); }); const clipboard = await readClipboard(); expect(clipboard).to.not.contain('Read permission denied.'); @@ -3319,7 +3316,12 @@ describe('navigator.clipboard.write', () => { await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); }); - const writeClipboard: any = () => { + const writeClipboard = async () => { + if (!w.webContents.isFocused()) { + const focus = once(w.webContents, 'focus'); + w.webContents.focus(); + await focus; + } return w.webContents.executeJavaScript(` navigator.clipboard.writeText('Hello World!').catch(err => err.message); `, true); @@ -3361,7 +3363,13 @@ describe('navigator.clipboard.write', () => { }); describe('paste execCommand', () => { - const readClipboard: any = (w: BrowserWindow) => { + const readClipboard = async (w: BrowserWindow) => { + if (!w.webContents.isFocused()) { + const focus = once(w.webContents, 'focus'); + w.webContents.focus(); + await focus; + } + return w.webContents.executeJavaScript(` new Promise((resolve) => { const timeout = setTimeout(() => { From 9d7adf22af8238bc06a2fac3631117bbeb233ece Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 12:19:51 -0700 Subject: [PATCH 310/339] fix: deprecation warning crash when no Node.js environment available (#47770) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/common/node_util.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index 7f6ffa93ea636..ba0d4437b87e5 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -65,8 +65,13 @@ void EmitWarning(const std::string_view warning_msg, void EmitWarning(v8::Isolate* isolate, const std::string_view warning_msg, const std::string_view warning_type) { - node::ProcessEmitWarningGeneric(node::Environment::GetCurrent(isolate), - warning_msg, warning_type); + node::Environment* env = node::Environment::GetCurrent(isolate); + if (!env) { + // No Node.js environment available, fall back to console logging. + LOG(WARNING) << "[" << warning_type << "] " << warning_msg; + return; + } + node::ProcessEmitWarningGeneric(env, warning_msg, warning_type); } void EmitDeprecationWarning(const std::string_view warning_msg, @@ -78,8 +83,14 @@ void EmitDeprecationWarning(const std::string_view warning_msg, void EmitDeprecationWarning(v8::Isolate* isolate, const std::string_view warning_msg, const std::string_view deprecation_code) { - node::ProcessEmitWarningGeneric(node::Environment::GetCurrent(isolate), - warning_msg, "DeprecationWarning", + node::Environment* env = node::Environment::GetCurrent(isolate); + if (!env) { + // No Node.js environment available, fall back to console logging. + LOG(WARNING) << "[DeprecationWarning] " << warning_msg + << " (code: " << deprecation_code << ")"; + return; + } + node::ProcessEmitWarningGeneric(env, warning_msg, "DeprecationWarning", deprecation_code); } From 3c75d600bf060f4158551fea0b7ea269d9124d0e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:03:46 +0200 Subject: [PATCH 311/339] test: cleanup RenderFrame lifespan tests (#47796) * test: cleanup RenderFrame lifespan tests Co-authored-by: John Kleinschmidt * test: disable navigator.serial tests on arm64 mac debug the hang test: disable navigator.bluetooth on arm64 mac Revert "test: disable navigator.bluetooth on arm64 mac" This reverts commit 4b53a8485a5ff391832c7da93d859f1aa8722e70. Revert "debug the hang" This reverts commit 00338f0d49a7918224822087b4510fa9db0686c3. Revert "test: disable navigator.serial tests on arm64 mac" This reverts commit fb515ce447a9d42185e84b17b460e4fb6d1bf71d. Reapply "test: disable navigator.serial tests on arm64 mac" This reverts commit 0e5608108ffebbe8b8b27af9ea06aadae2ea85dd. Reapply "test: disable navigator.bluetooth on arm64 mac" This reverts commit f4c7d3fc0624a22421cba5d3d75df8c5d4367eea. fixup Co-authored-by: John Kleinschmidt * test: add waitUntil for flaky test Co-authored-by: John Kleinschmidt --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- spec/api-web-frame-main-spec.ts | 5 ++++- spec/chromium-spec.ts | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/api-web-frame-main-spec.ts b/spec/api-web-frame-main-spec.ts index 236140780a884..9f583786f2157 100644 --- a/spec/api-web-frame-main-spec.ts +++ b/spec/api-web-frame-main-spec.ts @@ -313,6 +313,7 @@ describe('webFrameMain module', () => { beforeEach(async () => { w = new BrowserWindow({ show: false }); }); + afterEach(closeAllWindows); // TODO(jkleinsc) fix this flaky test on linux ifit(process.platform !== 'linux')('throws upon accessing properties when disposed', async () => { @@ -373,7 +374,9 @@ describe('webFrameMain module', () => { await w.webContents.loadURL(server.crossOriginUrl); // senderFrame now points to a disposed RenderFrameHost. It should // be null when attempting to access the lazily evaluated property. - expect(event.senderFrame).to.be.null(); + waitUntil(() => { + return event.senderFrame === null; + }); }); it('is detached when unload handler sends IPC', async () => { diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index f7db52bf31ba5..635ac6889c257 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3089,7 +3089,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); }); -describe('navigator.serial', () => { +ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.serial', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ @@ -3629,7 +3629,7 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se }); }); -describe('navigator.bluetooth', () => { +ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.bluetooth', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ From d342296f81969fda36813131337e0eb4f8974f2e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:15:16 +0200 Subject: [PATCH 312/339] test: fix extensions console flake (#47790) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- spec/extensions-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/extensions-spec.ts b/spec/extensions-spec.ts index fe246b05ad0ca..52e31132c07c8 100644 --- a/spec/extensions-spec.ts +++ b/spec/extensions-spec.ts @@ -18,7 +18,7 @@ const uuid = require('uuid'); const fixtures = path.join(__dirname, 'fixtures'); describe('chrome extensions', () => { - const emptyPage = ''; + const emptyPage = '

EMPTY PAGE

'; // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default. let server: http.Server; From d4ccd312642ffe71eab125341015298bcc1d5b30 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:35:24 -0400 Subject: [PATCH 313/339] build(dev-deps): drop unused @types/webpack dep (#47807) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- package.json | 1 - yarn.lock | 11 +---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/package.json b/package.json index 8f6c837ddfa47..a7fbb1b712097 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@types/semver": "^7.5.8", "@types/stream-json": "^1.7.7", "@types/temp": "^0.9.4", - "@types/webpack": "^5.28.5", "@types/webpack-env": "^1.18.5", "@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/parser": "^8.7.0", diff --git a/yarn.lock b/yarn.lock index 4ff9dd5afc01d..521ca8561992b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1063,15 +1063,6 @@ resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.5.tgz#eccda0b04fe024bed505881e2e532f9c119169bf" integrity sha512-wz7kjjRRj8/Lty4B+Kr0LN6Ypc/3SymeCCGSbaXp2leH0ZVg/PriNiOwNj4bD4uphI7A8NXS4b6Gl373sfO5mA== -"@types/webpack@^5.28.5": - version "5.28.5" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-5.28.5.tgz#0e9d9a15efa09bbda2cef41356ca4ac2031ea9a2" - integrity sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw== - dependencies: - "@types/node" "*" - tapable "^2.2.0" - webpack "^5" - "@types/yauzl@^2.9.1": version "2.10.0" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" @@ -8125,7 +8116,7 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5, webpack@^5.95.0: +webpack@^5.95.0: version "5.95.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0" integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q== From ea4d43d59eb64d3887438863bbec3e5a11649848 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:38:15 -0400 Subject: [PATCH 314/339] build: deep update brace-expansion to resolve an audit alert (#47718) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 521ca8561992b..05eef81f0ca3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1660,17 +1660,17 @@ boolean@^3.0.1: integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" From 8c974c022a64fce6c3a0cec8a36f4cd0766093d2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:52:27 +0200 Subject: [PATCH 315/339] test: re-enable native module tests (#47804) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../pipeline-segment-electron-test.yml | 1 - spec/api-browser-window-spec.ts | 47 ------------------- 2 files changed, 48 deletions(-) diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index fe2c7fb62fec6..f71459ac21b48 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -194,7 +194,6 @@ jobs: MOCHA_REPORTER: mocha-multi-reporters MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap ELECTRON_DISABLE_SECURITY_WARNINGS: 1 - ELECTRON_SKIP_NATIVE_MODULE_TESTS: true DISPLAY: ':99.0' NPM_CONFIG_MSVS_VERSION: '2022' run: | diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index c69bef807708d..1d92d8abf9473 100755 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -1,4 +1,3 @@ -import { nativeImage } from 'electron'; import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, net, protocol, screen, webContents, webFrameMain, session, WebContents, WebFrameMain } from 'electron/main'; import { expect } from 'chai'; @@ -6676,52 +6675,6 @@ describe('BrowserWindow module', () => { }); }); - describe('offscreen rendering image', () => { - afterEach(closeAllWindows); - - const imagePath = path.join(fixtures, 'assets', 'osr.png'); - const targetImage = nativeImage.createFromPath(imagePath); - const nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; - ifit(nativeModulesEnabled && ['win32'].includes(process.platform))('use shared texture, hardware acceleration enabled', (done) => { - const { ExtractPixels, InitializeGpu } = require('@electron-ci/osr-gpu'); - - try { - InitializeGpu(); - } catch (e) { - console.log('Failed to initialize GPU, this spec needs a valid GPU device. Skipping...'); - console.error(e); - done(); - return; - } - - const w = new BrowserWindow({ - show: false, - webPreferences: { - offscreen: { - useSharedTexture: true - } - }, - transparent: true, - frame: false, - width: 128, - height: 128 - }); - - w.webContents.once('paint', async (e, dirtyRect) => { - try { - expect(e.texture).to.be.not.null(); - const pixels = ExtractPixels(e.texture!.textureInfo); - const img = nativeImage.createFromBitmap(pixels, { width: dirtyRect.width, height: dirtyRect.height, scaleFactor: 1 }); - expect(img.toBitmap().equals(targetImage.toBitmap())).to.equal(true); - done(); - } catch (e) { - done(e); - } - }); - w.loadFile(imagePath); - }); - }); - describe('"transparent" option', () => { afterEach(closeAllWindows); From 440fb9a73eb2227458b5b5074a0926181cfb23f9 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:09:40 +0200 Subject: [PATCH 316/339] fix: handle missing `NativeWindowMac` in `ElectronNSWindow` (#47811) fix: handle missing NativeWindowMac in ElectronNSWindow Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/cocoa/electron_ns_window.mm | 31 ++++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/shell/browser/ui/cocoa/electron_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm index e657539adf497..40cd21029503b 100644 --- a/shell/browser/ui/cocoa/electron_ns_window.mm +++ b/shell/browser/ui/cocoa/electron_ns_window.mm @@ -176,10 +176,8 @@ - (void)cleanup { } - (id)accessibilityFocusedUIElement { - views::Widget* widget = shell_->widget(); - id superFocus = [super accessibilityFocusedUIElement]; - if (!widget || shell_->IsFocused()) - return superFocus; + if (!shell_ || !shell_->widget() || shell_->IsFocused()) + return [super accessibilityFocusedUIElement]; return nil; } - (NSRect)originalContentRectForFrameRect:(NSRect)frameRect { @@ -187,7 +185,7 @@ - (NSRect)originalContentRectForFrameRect:(NSRect)frameRect { } - (NSTouchBar*)makeTouchBar { - if (shell_->touch_bar()) + if (shell_ && shell_->touch_bar()) return [shell_->touch_bar() makeTouchBar]; else return nil; @@ -217,7 +215,8 @@ - (void)sendEvent:(NSEvent*)event { } - (void)rotateWithEvent:(NSEvent*)event { - shell_->NotifyWindowRotateGesture(event.rotation); + if (shell_) + shell_->NotifyWindowRotateGesture(event.rotation); } - (NSRect)contentRectForFrameRect:(NSRect)frameRect { @@ -241,7 +240,7 @@ - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen { // // If there's no frame, put the window wherever the developer // wanted it to go - if (shell_->has_frame()) { + if (shell_ && shell_->has_frame()) { result.size = frameRect.size; } else { result = frameRect; @@ -288,7 +287,7 @@ - (id)accessibilityAttributeValue:(NSString*)attribute { } - (NSString*)accessibilityTitle { - return base::SysUTF8ToNSString(shell_->GetTitle()); + return base::SysUTF8ToNSString(shell_ ? shell_->GetTitle() : ""); } - (BOOL)canBecomeMainWindow { @@ -308,7 +307,7 @@ - (BOOL)validateUserInterfaceItem:(id)item { // support closing a window without title we need to manually do menu item // validation. This code path is used by the "roundedCorners" option. if ([item action] == @selector(performClose:)) - return shell_->IsClosable(); + return shell_ && shell_->IsClosable(); return [super validateUserInterfaceItem:item]; } @@ -328,7 +327,8 @@ - (void)disableHeadlessMode { // shown, but we don't want the headless behavior of allowing the window to // be placed unconstrained. self.isHeadless = false; - shell_->widget()->DisableHeadlessMode(); + if (shell_->widget()) + shell_->widget()->DisableHeadlessMode(); } } @@ -378,6 +378,9 @@ - (void)performClose:(id)sender { } - (BOOL)toggleFullScreenMode:(id)sender { + if (!shell_) + return NO; + bool is_simple_fs = shell_->IsSimpleFullScreen(); bool always_simple_fs = shell_->always_simple_fullscreen(); @@ -411,11 +414,13 @@ - (BOOL)toggleFullScreenMode:(id)sender { } - (void)performMiniaturize:(id)sender { - if (shell_->title_bar_style() == - electron::NativeWindowMac::TitleBarStyle::kCustomButtonsOnHover) + if (shell_ && + shell_->title_bar_style() == + electron::NativeWindowMac::TitleBarStyle::kCustomButtonsOnHover) { [self miniaturize:self]; - else + } else { [super performMiniaturize:sender]; + } } @end From 1c0cec1872a9415253616627da4f79e52d050dc7 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:44:55 +0200 Subject: [PATCH 317/339] ci: remove `kTCCServiceMicrophone` change (#47821) ci: remove kTCCServiceMicrophone change Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/workflows/pipeline-segment-electron-test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index f71459ac21b48..76a603b7b389d 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -100,7 +100,6 @@ jobs: } userValuesArray=( - "'kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" From ef9212a112358f62896ec1efe3d0df54b22b1f66 Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:42:10 -0400 Subject: [PATCH 318/339] chore: bump node to v22.17.1 (36-x-y) (#47772) * chore: bump node in DEPS to v22.17.1 * chore: update patches --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: patchup[bot] <73610968+patchup[bot]@users.noreply.github.com> --- DEPS | 2 +- patches/node/build_compile_with_c_20_support.patch | 2 +- ...ure_native_module_compilation_fails_if_not_using_a_new.patch | 2 +- .../node/build_restore_clang_as_default_compiler_on_macos.patch | 2 +- .../fix_add_default_values_for_variables_in_common_gypi.patch | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index 493c1dfdf9b7c..404c579aa3b8b 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.177', 'node_version': - 'v22.17.0', + 'v22.17.1', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/patches/node/build_compile_with_c_20_support.patch b/patches/node/build_compile_with_c_20_support.patch index 83862862dcd55..b16baaf70f9e5 100644 --- a/patches/node/build_compile_with_c_20_support.patch +++ b/patches/node/build_compile_with_c_20_support.patch @@ -10,7 +10,7 @@ V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8 This can be removed when Electron upgrades to a version of Node.js containing the required V8 version. diff --git a/common.gypi b/common.gypi -index 03fefab4b0a9727925411b95310831ffdc33e8d9..f9b5e47f1d67807435529c99d12f419d0fd4269f 100644 +index acfc02510ee1ce34a3f410a7a4ce53adb42abd35..b9264bfb1170928431848bb2b99e4f0dfbe8f95a 100644 --- a/common.gypi +++ b/common.gypi @@ -538,7 +538,7 @@ diff --git a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch index 259c40e0aeb53..8118eaac97d4b 100644 --- a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch +++ b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch @@ -7,7 +7,7 @@ Subject: build: ensure native module compilation fails if not using a new This should not be upstreamed, it is a quality-of-life patch for downstream module builders. diff --git a/common.gypi b/common.gypi -index bfe567e016cf102d2087f7647e64cc051116ab8d..03fefab4b0a9727925411b95310831ffdc33e8d9 100644 +index cf643bcd0bc9080b80bf12afeebc69f2db74bb54..acfc02510ee1ce34a3f410a7a4ce53adb42abd35 100644 --- a/common.gypi +++ b/common.gypi @@ -89,6 +89,8 @@ diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index d7262f8fc5c5a..7901c967e9195 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,7 +11,7 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index f9b5e47f1d67807435529c99d12f419d0fd4269f..c99f583d3674347dd6eb9a5eea1ed5588e376d31 100644 +index b9264bfb1170928431848bb2b99e4f0dfbe8f95a..836d96a1bd9c1d5568f0045bbddddca1edb1a0ce 100644 --- a/common.gypi +++ b/common.gypi @@ -128,6 +128,7 @@ diff --git a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch index e2f7f19bc3655..c460fa71649eb 100644 --- a/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch +++ b/patches/node/fix_add_default_values_for_variables_in_common_gypi.patch @@ -7,7 +7,7 @@ common.gypi is a file that's included in the node header bundle, despite the fact that we do not build node with gyp. diff --git a/common.gypi b/common.gypi -index a73d4401f26d8493802d3ecec3e015a77717720a..bfe567e016cf102d2087f7647e64cc051116ab8d 100644 +index b88371ec13da5a287e18f7224bb4970fc94dce66..cf643bcd0bc9080b80bf12afeebc69f2db74bb54 100644 --- a/common.gypi +++ b/common.gypi @@ -91,6 +91,23 @@ From 4a81ae79540384ddac3340a95a365e45aba23b26 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:26:59 -0400 Subject: [PATCH 319/339] refactor: reduce scope of temporaries when getting dictionary values (#47831) refactor: reduce scale of temporaries when getting dictionary values Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr --- shell/browser/native_window.cc | 78 +++++++++++----------------- shell/browser/native_window_mac.mm | 9 ++-- shell/browser/native_window_views.cc | 49 ++++++----------- 3 files changed, 48 insertions(+), 88 deletions(-) diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index d1d5e8b7689f2..e13c5fc552ba1 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -130,19 +130,17 @@ NativeWindow::~NativeWindow() { void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { // Setup window from options. - int x = -1, y = -1; - bool center; - if (options.Get(options::kX, &x) && options.Get(options::kY, &y)) { - SetPosition(gfx::Point(x, y)); + if (int x, y; options.Get(options::kX, &x) && options.Get(options::kY, &y)) { + SetPosition(gfx::Point{x, y}); #if BUILDFLAG(IS_WIN) // FIXME(felixrieseberg): Dirty, dirty workaround for // https://github.com/electron/electron/issues/10862 // Somehow, we need to call `SetBounds` twice to get // usable results. The root cause is still unknown. - SetPosition(gfx::Point(x, y)); + SetPosition(gfx::Point{x, y}); #endif - } else if (options.Get(options::kCenter, ¢er) && center) { + } else if (bool center; options.Get(options::kCenter, ¢er) && center) { Center(); } @@ -181,27 +179,21 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { SetSizeConstraints(size_constraints); } #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) - bool closable; - if (options.Get(options::kClosable, &closable)) { - SetClosable(closable); - } + if (bool val; options.Get(options::kClosable, &val)) + SetClosable(val); #endif - bool movable; - if (options.Get(options::kMovable, &movable)) { - SetMovable(movable); - } - bool has_shadow; - if (options.Get(options::kHasShadow, &has_shadow)) { - SetHasShadow(has_shadow); - } - double opacity; - if (options.Get(options::kOpacity, &opacity)) { - SetOpacity(opacity); - } - bool top; - if (options.Get(options::kAlwaysOnTop, &top) && top) { + + if (bool val; options.Get(options::kMovable, &val)) + SetMovable(val); + + if (bool val; options.Get(options::kHasShadow, &val)) + SetHasShadow(val); + + if (double val; options.Get(options::kOpacity, &val)) + SetOpacity(val); + + if (bool val; options.Get(options::kAlwaysOnTop, &val) && val) SetAlwaysOnTop(ui::ZOrderLevel::kFloatingWindow); - } bool fullscreenable = true; bool fullscreen = false; @@ -218,29 +210,21 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { if (fullscreen) SetFullScreen(true); - bool resizable; - if (options.Get(options::kResizable, &resizable)) { - SetResizable(resizable); - } + if (bool val; options.Get(options::kResizable, &val)) + SetResizable(val); + + if (bool val; options.Get(options::kSkipTaskbar, &val)) + SetSkipTaskbar(val); + + if (bool val; options.Get(options::kKiosk, &val) && val) + SetKiosk(val); - bool skip; - if (options.Get(options::kSkipTaskbar, &skip)) { - SetSkipTaskbar(skip); - } - bool kiosk; - if (options.Get(options::kKiosk, &kiosk) && kiosk) { - SetKiosk(kiosk); - } #if BUILDFLAG(IS_MAC) - std::string type; - if (options.Get(options::kVibrancyType, &type)) { - SetVibrancy(type, 0); - } + if (std::string val; options.Get(options::kVibrancyType, &val)) + SetVibrancy(val, 0); #elif BUILDFLAG(IS_WIN) - std::string material; - if (options.Get(options::kBackgroundMaterial, &material)) { - SetBackgroundMaterial(material); - } + if (std::string val; options.Get(options::kBackgroundMaterial, &val)) + SetBackgroundMaterial(val); #endif SkColor background_color = SK_ColorWHITE; @@ -251,9 +235,7 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { } SetBackgroundColor(background_color); - std::string title(Browser::Get()->GetName()); - options.Get(options::kTitle, &title); - SetTitle(title); + SetTitle(options.ValueOrDefault(options::kTitle, Browser::Get()->GetName())); // Then show it. if (options.ValueOrDefault(options::kShow, true)) diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 6826f7ea80a2f..871db08901010 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -238,8 +238,7 @@ static bool FromV8(v8::Isolate* isolate, [window_ setLevel:NSFloatingWindowLevel]; } - bool focusable; - if (options.Get(options::kFocusable, &focusable) && !focusable) + if (bool val; options.Get(options::kFocusable, &val) && !val) [window_ setDisableKeyOrMainWindow:YES]; if (transparent() || !has_frame()) { @@ -284,12 +283,10 @@ static bool FromV8(v8::Isolate* isolate, // NOTE(@mlaurencin) Spec requirements can be found here: // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#width constexpr int kMinSizeReqdBySpec = 100; - int inner_width = 0; - int inner_height = 0; + const int inner_width = options.ValueOrDefault(options::kinnerWidth, 0); + const int inner_height = options.ValueOrDefault(options::kinnerHeight, 0); bool use_content_size = options.ValueOrDefault(options::kUseContentSize, false); - options.Get(options::kinnerWidth, &inner_width); - options.Get(options::kinnerHeight, &inner_height); if (inner_width || inner_height) { use_content_size = true; if (inner_width) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index f81918bd8c6c9..99792dafebb8e 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -196,9 +196,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, : NativeWindow(options, parent) { options.Get(options::kTitle, &title_); - bool menu_bar_autohide; - if (options.Get(options::kAutoHideMenuBar, &menu_bar_autohide)) - root_view_.SetAutoHideMenuBar(menu_bar_autohide); + if (bool val; options.Get(options::kAutoHideMenuBar, &val)) + root_view_.SetAutoHideMenuBar(val); #if BUILDFLAG(IS_WIN) // On Windows we rely on the CanResize() to indicate whether window can be @@ -215,35 +214,20 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, overlay_button_color_ = color_utils::GetSysSkColor(COLOR_BTNFACE); overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT); - bool accent_color = true; - std::string accent_color_string; - if (options.Get(options::kAccentColor, &accent_color_string)) { - accent_color_ = ParseCSSColor(accent_color_string); - } else if (options.Get(options::kAccentColor, &accent_color)) { - accent_color_ = accent_color; + if (std::string str; options.Get(options::kAccentColor, &str)) { + accent_color_ = ParseCSSColor(str); + } else if (bool flag; options.Get(options::kAccentColor, &flag)) { + accent_color_ = flag; } #endif - v8::Local titlebar_overlay; - if (options.Get(options::ktitleBarOverlay, &titlebar_overlay) && - titlebar_overlay->IsObject()) { - auto titlebar_overlay_obj = - gin_helper::Dictionary::CreateEmpty(options.isolate()); - options.Get(options::ktitleBarOverlay, &titlebar_overlay_obj); - - std::string overlay_color_string; - if (titlebar_overlay_obj.Get(options::kOverlayButtonColor, - &overlay_color_string)) { - bool success = content::ParseCssColorString(overlay_color_string, - &overlay_button_color_); + if (gin_helper::Dictionary od; options.Get(options::ktitleBarOverlay, &od)) { + if (std::string val; od.Get(options::kOverlayButtonColor, &val)) { + bool success = content::ParseCssColorString(val, &overlay_button_color_); DCHECK(success); } - - std::string overlay_symbol_color_string; - if (titlebar_overlay_obj.Get(options::kOverlaySymbolColor, - &overlay_symbol_color_string)) { - bool success = content::ParseCssColorString(overlay_symbol_color_string, - &overlay_symbol_color_); + if (std::string val; od.Get(options::kOverlaySymbolColor, &val)) { + bool success = content::ParseCssColorString(val, &overlay_symbol_color_); DCHECK(success); } } @@ -289,8 +273,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, if (IsTranslucent() && !has_frame()) params.shadow_type = InitParams::ShadowType::kNone; - bool focusable; - if (options.Get(options::kFocusable, &focusable) && !focusable) + if (bool val; options.Get(options::kFocusable, &val) && !val) params.activatable = InitParams::Activatable::kNo; #if BUILDFLAG(IS_WIN) @@ -413,11 +396,9 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, // NOTE(@mlaurencin) Spec requirements can be found here: // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#width - int kMinSizeReqdBySpec = 100; - int inner_width = 0; - int inner_height = 0; - options.Get(options::kinnerWidth, &inner_width); - options.Get(options::kinnerHeight, &inner_height); + constexpr int kMinSizeReqdBySpec = 100; + const int inner_width = options.ValueOrDefault(options::kinnerWidth, 0); + const int inner_height = options.ValueOrDefault(options::kinnerHeight, 0); if (inner_width || inner_height) { use_content_size_ = true; if (inner_width) From b180cfee6c3544434197838dbdf84f7031c5a54b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:29:48 +0200 Subject: [PATCH 320/339] fix: dialog file filters and macOS app bundles (#47840) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/file_dialog_mac.mm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index db361b848942b..793ca1a970b06 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -122,6 +122,18 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { if (ext == "*") { [content_types_set addObject:[UTType typeWithFilenameExtension:@"*"]]; break; + } else if (ext == "app") { + // This handles a bug on macOS where the "app" extension by default + // maps to "com.apple.application-file", which is for an Application + // file (older single-file carbon based apps), and not modern + // Application Bundles (multi file packages as you'd see for all modern + // applications). + UTType* superType = + [UTType typeWithIdentifier:@"com.apple.application-bundle"]; + if (UTType* utt = [UTType typeWithFilenameExtension:@"app" + conformingToType:superType]) { + [content_types_set addObject:utt]; + } } else { if (UTType* utt = [UTType typeWithFilenameExtension:@(ext.c_str())]) [content_types_set addObject:utt]; From a2d43f4a39ec75deb66c80b11b8e585b91e8198a Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:18:57 -0400 Subject: [PATCH 321/339] build: fix ffmpeg generation on Windows non-x64 (#47847) * build: fix ffmpeg generation on Windows non-x64 Co-authored-by: Shelley Vohr * test: ffmpeg artifact Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .github/actions/build-electron/action.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 350866638b46d..50b2decbcdcfd 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -38,6 +38,12 @@ runs: run: | GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"x64\" v8_snapshot_toolchain=\"//build/toolchain/mac:clang_x64\"" echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV + - name: Set GN_EXTRA_ARGS for Windows + shell: bash + if: ${{inputs.target-arch != 'x64' && inputs.target-platform == 'win' }} + run: | + GN_APPENDED_ARGS="$GN_EXTRA_ARGS target_cpu=\"${{ inputs.target-arch }}\"" + echo "GN_EXTRA_ARGS=$GN_APPENDED_ARGS" >> $GITHUB_ENV - name: Add Clang problem matcher shell: bash run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json" @@ -184,8 +190,8 @@ runs: electron/script/zip-symbols.py -b $BUILD_PATH fi - name: Generate FFMpeg ${{ inputs.step-suffix }} - shell: bash if: ${{ inputs.is-release == 'true' }} + shell: bash run: | cd src gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true $GN_EXTRA_ARGS" From 0af4b6c1ed01201e0c2299e3d43ab2eb440bcefa Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:50:49 +0200 Subject: [PATCH 322/339] build: improve `check-zip-manifest` (#47851) * build: improve check-zip-manifest Co-authored-by: Shelley Vohr * fix: unicode on Windows Co-authored-by: Shelley Vohr --------- Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- script/zip_manifests/check-zip-manifest.py | 91 +++++++++++++++++----- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/script/zip_manifests/check-zip-manifest.py b/script/zip_manifests/check-zip-manifest.py index ee744422d847e..6e5773b0567e5 100755 --- a/script/zip_manifests/check-zip-manifest.py +++ b/script/zip_manifests/check-zip-manifest.py @@ -2,24 +2,79 @@ import zipfile import sys +import os -def main(zip_path, manifest_in): - with open(manifest_in, 'r', encoding='utf-8') as manifest, \ - zipfile.ZipFile(zip_path, 'r', allowZip64=True) as z: - files_in_zip = set(z.namelist()) - files_in_manifest = {l.strip() for l in manifest.readlines()} - added_files = files_in_zip - files_in_manifest - removed_files = files_in_manifest - files_in_zip - if added_files: - print("Files added to bundle:") - for f in sorted(list(added_files)): - print('+' + f) - if removed_files: - print("Files removed from bundle:") - for f in sorted(list(removed_files)): - print('-' + f) - - return 1 if added_files or removed_files else 0 +def main(zip_path, manifest_path): + """ + Compare a zip file's contents against a manifest file. + Returns 0 if they match, 1 if there are differences. + """ + + if not os.path.exists(zip_path): + print(f"ERROR: Zip file not found: {zip_path}", file=sys.stderr) + return 1 + + if not os.path.exists(manifest_path): + print(f"ERROR: Manifest file not found: {manifest_path}", file=sys.stderr) + return 1 + + try: + with zipfile.ZipFile(zip_path, 'r', allowZip64=True) as z: + files_in_zip = set(z.namelist()) + except zipfile.BadZipFile: + print(f"ERROR: Invalid zip file: {zip_path}", file=sys.stderr) + return 1 + except Exception as e: + print(f"ERROR: Failed to read zip file {zip_path}: {e}", file=sys.stderr) + return 1 + + try: + with open(manifest_path, 'r', encoding='utf-8') as manifest: + files_in_manifest = {line.strip() for line in manifest.readlines() if line.strip()} + except Exception as e: + print(f"ERROR: Failed to read manifest file {manifest_path}: {e}", file=sys.stderr) + return 1 + + added_files = files_in_zip - files_in_manifest + removed_files = files_in_manifest - files_in_zip + + if not added_files and not removed_files: + print("OK: Zip contents match manifest - no differences found") + return 0 + + print("ERROR: Zip contents do not match manifest!") + print(f"Zip file: {zip_path}") + print(f"Manifest: {manifest_path}") + print() + + if added_files: + print(f"Files in zip but NOT in manifest ({len(added_files)} files):") + for f in sorted(added_files): + print(f" + {f}") + print() + + if removed_files: + print(f"Files in manifest but NOT in zip ({len(removed_files)} files):") + for f in sorted(removed_files): + print(f" - {f}") + print() + + print("ACTION REQUIRED:") + if added_files: + print("- Add the new files to the manifest, or") + print("- Remove them from the zip if they shouldn't be included") + if removed_files: + print("- Remove the missing files from the manifest, or") + print("- Add them to the zip if they should be included") + + return 1 if __name__ == '__main__': - sys.exit(main(sys.argv[1], sys.argv[2])) + if len(sys.argv) != 3: + print("Usage: check-zip-manifest.py ", file=sys.stderr) + print("", file=sys.stderr) + print("Compares the contents of a zip file against a manifest file.", file=sys.stderr) + print("Returns 0 if they match, 1 if there are differences or errors.", file=sys.stderr) + sys.exit(1) + + sys.exit(main(sys.argv[1], sys.argv[2])) From c41d692473a0319e6c79f3d5bcdffaa9400774d2 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:22:30 +0200 Subject: [PATCH 323/339] fix: `webContents.downloadURL()` did not support referer header (#47866) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: xufuhang <576484918@qq.com> --- .../browser/api/electron_api_web_contents.cc | 7 +++ spec/api-session-spec.ts | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 6d22baefc8bb6..e2e34234420fc 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2409,6 +2409,13 @@ void WebContents::DownloadURL(const GURL& url, gin::Arguments* args) { content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( web_contents(), url, MISSING_TRAFFIC_ANNOTATION)); for (const auto& [name, value] : headers) { + if (base::ToLowerASCII(name) == + base::ToLowerASCII(net::HttpRequestHeaders::kReferer)) { + // Setting a Referer header with HTTPS scheme while the download URL's + // scheme is HTTP might lead to download failure. + download_params->set_referrer(GURL(value)); + continue; + } download_params->add_request_header(name, value); } diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts index afa34738ebd32..ea632a38abcd6 100644 --- a/spec/api-session-spec.ts +++ b/spec/api-session-spec.ts @@ -1130,6 +1130,53 @@ describe('session module', () => { expect(item.getContentDisposition()).to.equal(contentDisposition); }); + it('can perform a download with referer header', async () => { + const server = http.createServer((req, res) => { + const { referer } = req.headers; + if (!referer || !referer.startsWith('http://www.electronjs.org')) { + res.statusCode = 403; + res.end(); + } else { + res.writeHead(200, { + 'Content-Length': mockPDF.length, + 'Content-Type': 'application/pdf', + 'Content-Disposition': req.url === '/?testFilename' ? 'inline' : contentDisposition + }); + res.end(mockPDF); + } + }); + + const { port } = await listen(server); + + const w = new BrowserWindow({ show: false }); + const downloadDone: Promise = new Promise((resolve) => { + w.webContents.session.once('will-download', (e, item) => { + item.savePath = downloadFilePath; + item.on('done', () => { + try { + resolve(item); + } catch { } + }); + }); + }); + + w.webContents.downloadURL(`${url}:${port}`, { + headers: { + // Setting a Referer header with HTTPS scheme while the download URL's + // scheme is HTTP might lead to download failure. + referer: 'http://www.electronjs.org' + } + }); + + const item = await downloadDone; + expect(item.getState()).to.equal('completed'); + expect(item.getFilename()).to.equal('mock.pdf'); + expect(item.getMimeType()).to.equal('application/pdf'); + expect(item.getReceivedBytes()).to.equal(mockPDF.length); + expect(item.getTotalBytes()).to.equal(mockPDF.length); + expect(item.getContentDisposition()).to.equal(contentDisposition); + }); + it('throws when called with invalid headers', () => { const w = new BrowserWindow({ show: false }); expect(() => { From d2af972af41ed02d7c5e8d3f0925397b860a3aa6 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:42:20 -0400 Subject: [PATCH 324/339] fix: window content protection on older Windows versions (#47888) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/native_window_views.cc | 17 +++++++++++------ shell/browser/native_window_views.h | 1 + .../electron_desktop_window_tree_host_win.cc | 5 +++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 99792dafebb8e..2b84e7fdd7df2 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1263,12 +1263,7 @@ void NativeWindowViews::SetOpacity(const double opacity) { #if BUILDFLAG(IS_WIN) const double boundedOpacity = std::clamp(opacity, 0.0, 1.0); HWND hwnd = GetAcceleratedWidget(); - if (!layered_) { - LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); - ex_style |= WS_EX_LAYERED; - ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); - layered_ = true; - } + SetLayered(); ::SetLayeredWindowAttributes(hwnd, 0, boundedOpacity * 255, LWA_ALPHA); opacity_ = boundedOpacity; #else @@ -1702,6 +1697,16 @@ void NativeWindowViews::UpdateThickFrame() { FlipWindowStyle(GetAcceleratedWidget(), resizable_, WS_THICKFRAME); } } + +void NativeWindowViews::SetLayered() { + HWND hwnd = GetAcceleratedWidget(); + if (!layered_) { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style |= WS_EX_LAYERED; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + layered_ = true; + } +} #endif void NativeWindowViews::OnWidgetActivationChanged(views::Widget* changed_widget, diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index ab75b7ad76d56..4a3a0d827e455 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -170,6 +170,7 @@ class NativeWindowViews : public NativeWindow, #if BUILDFLAG(IS_WIN) TaskbarHost& taskbar_host() { return taskbar_host_; } void UpdateThickFrame(); + void SetLayered(); #endif SkColor overlay_button_color() const { return overlay_button_color_; } diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index 7ce790b566e61..6eed0a338390b 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -167,6 +167,11 @@ void ElectronDesktopWindowTreeHostWin::UpdateAllowScreenshots() { if (allowed == allow_screenshots_) return; + // On some older Windows versions, setting the display affinity + // to WDA_EXCLUDEFROMCAPTURE won't prevent the window from being + // captured - setting WS_EX_LAYERED mitigates this issue. + if (base::win::GetVersion() < base::win::Version::WIN11_22H2) + native_window_view_->SetLayered(); ::SetWindowDisplayAffinity( GetAcceleratedWidget(), allow_screenshots_ ? WDA_NONE : WDA_EXCLUDEFROMCAPTURE); From 1ac47ee8cf5f960a99f329c948d0d118148841bf Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:41:10 -0400 Subject: [PATCH 325/339] ci: fixup mac runners (#47899) ci: make sure camera and bluetooth are available on macos ci: turn off spotlight indexing Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/free-space-macos/action.yml | 5 ++++- .github/workflows/pipeline-segment-electron-test.yml | 2 ++ spec/chromium-spec.ts | 5 +++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/actions/free-space-macos/action.yml b/.github/actions/free-space-macos/action.yml index 75350ca796bad..166f7877d32c7 100644 --- a/.github/actions/free-space-macos/action.yml +++ b/.github/actions/free-space-macos/action.yml @@ -6,6 +6,8 @@ runs: - name: Free Space on MacOS shell: bash run: | + echo "Disk usage before cleanup:" + df -h sudo mkdir -p $TMPDIR/del-target tmpify() { @@ -62,4 +64,5 @@ runs: # lipo off some huge binaries arm64 versions to save space strip_universal_deep $(xcode-select -p)/../SharedFrameworks - # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr \ No newline at end of file + # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr + sudo mdutil -a -i off diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 76a603b7b389d..54d7a84680fd9 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -103,6 +103,8 @@ jobs: "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceCamera',' /opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceBluetoothAlways',' /opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" ) for values in "${userValuesArray[@]}"; do # Sonoma and higher have a few extra values diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 635ac6889c257..8bddd5fd537dd 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3089,7 +3089,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); }); -ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.serial', () => { +describe('navigator.serial', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ @@ -3113,6 +3113,7 @@ ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator }); it('does not return a port if select-serial-port event is not defined', async () => { + // Take screenshot to verify the test is running w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); const port = await getPorts(); expect(port).to.equal(notFoundError); @@ -3629,7 +3630,7 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se }); }); -ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.bluetooth', () => { +describe('navigator.bluetooth', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ From e32dc4fa369e0537f30d36439452695f0ce65e54 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:14:00 -0400 Subject: [PATCH 326/339] ci: use new arc cluster (#47914) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/build-electron/action.yml | 5 +++ .github/actions/checkout/action.yml | 2 +- .../actions/restore-cache-azcopy/action.yml | 15 ++++++--- .github/workflows/build-git-cache.yml | 6 ++-- .github/workflows/build.yml | 32 +++++++++---------- .github/workflows/clean-src-cache.yml | 2 +- .github/workflows/linux-publish.yml | 8 ++--- .github/workflows/macos-publish.yml | 2 +- .../workflows/pipeline-electron-docs-only.yml | 2 +- .github/workflows/pipeline-electron-lint.yml | 2 +- .../pipeline-segment-node-nan-test.yml | 4 +-- .github/workflows/windows-publish.yml | 8 ++--- 12 files changed, 49 insertions(+), 39 deletions(-) diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 50b2decbcdcfd..807895f737552 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -47,6 +47,11 @@ runs: - name: Add Clang problem matcher shell: bash run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json" + - name: Enable long paths for Windows + shell: powershell + if: ${{ inputs.target-platform == 'win' }} + run: | + Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Type DWord - name: Build Electron ${{ inputs.step-suffix }} shell: bash run: | diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index 98fc18bb566b5..e0d66fa041f21 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -40,7 +40,7 @@ runs: if: ${{ inputs.generate-sas-token == 'true' }} shell: bash run: | - curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}" > sas-token + curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token - name: Save SAS Key if: ${{ inputs.generate-sas-token == 'true' }} uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index 2272ee6e76876..c731eb59ef6d9 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -36,18 +36,23 @@ runs: echo "SAS Token not found; exiting src cache download early..." exit 1 else + echo "const fs = require('fs');" > gettoken.js + echo "const fileContents = fs.readFileSync('sas-token', 'utf8');" >> gettoken.js + echo "const token = JSON.parse(fileContents);" >> gettoken.js + echo "console.log(token[process.argv[2]])" >> gettoken.js + sas_token=$(node ./gettoken.js sasToken) + account_name=$(node ./gettoken.js accountName) if [ "${{ inputs.target-platform }}" = "win" ]; then azcopy copy --log-level=ERROR \ - "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_WIN_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + "https://$account_name.file.core.windows.net/${{ env.AZURE_AKS_WIN_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar else azcopy copy --log-level=ERROR \ - "https://${{ env.AZURE_AKS_CACHE_STORAGE_ACCOUNT }}.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar + "https://$account_name.file.core.windows.net/${{ env.AZURE_AKS_CACHE_SHARE_NAME }}/${{ env.CACHE_PATH }}?$sas_token" $DEPSHASH.tar fi fi env: - AZURE_AKS_CACHE_STORAGE_ACCOUNT: f723719aa87a34622b5f7f3 - AZURE_AKS_CACHE_SHARE_NAME: pvc-f6a4089f-b082-4bee-a3f9-c3e1c0c02d8f - AZURE_AKS_WIN_CACHE_SHARE_NAME: pvc-71dec4f2-0d44-4fd1-a2c3-add049d70bdf + AZURE_AKS_CACHE_SHARE_NAME: linux-cache + AZURE_AKS_WIN_CACHE_SHARE_NAME: windows-cache - name: Clean SAS Key shell: bash run: rm -f sas-token diff --git a/.github/workflows/build-git-cache.yml b/.github/workflows/build-git-cache.yml index 0fddbd4522a58..43daf56e5aa36 100644 --- a/.github/workflows/build-git-cache.yml +++ b/.github/workflows/build-git-cache.yml @@ -8,7 +8,7 @@ on: jobs: build-git-cache-linux: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 options: --user root @@ -29,7 +29,7 @@ jobs: target-platform: linux build-git-cache-windows: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 options: --user root --device /dev/fuse --cap-add SYS_ADMIN @@ -51,7 +51,7 @@ jobs: target-platform: win build-git-cache-macos: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core # This job updates the same git cache as linux, so it needs to run after the linux one. needs: build-git-cache-linux container: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0eaa4c7d87295..4c15823e382a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -92,7 +92,7 @@ jobs: checkout-macos: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-macos}} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root @@ -120,7 +120,7 @@ jobs: checkout-linux: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-linux}} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root @@ -146,7 +146,7 @@ jobs: checkout-windows: needs: setup if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }} options: --user root --device /dev/fuse --cap-add SYS_ADMIN @@ -191,7 +191,7 @@ jobs: with: target-platform: linux target-archs: x64 arm arm64 - check-runs-on: electron-arc-linux-amd64-8core + check-runs-on: electron-arc-centralus-linux-amd64-8core check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' gn-build-type: testing secrets: inherit @@ -202,7 +202,7 @@ jobs: with: target-platform: win target-archs: x64 x86 arm64 - check-runs-on: electron-arc-linux-amd64-8core + check-runs-on: electron-arc-centralus-linux-amd64-8core check-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}' gn-build-type: testing secrets: inherit @@ -252,8 +252,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test-and-nan.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-amd64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-amd64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -272,8 +272,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-amd64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-amd64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -293,8 +293,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-arm64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-arm64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}' target-platform: linux @@ -313,8 +313,8 @@ jobs: uses: ./.github/workflows/pipeline-electron-build-and-test.yml needs: checkout-linux with: - build-runs-on: electron-arc-linux-amd64-32core - test-runs-on: electron-arc-linux-arm64-4core + build-runs-on: electron-arc-centralus-linux-amd64-32core + test-runs-on: electron-arc-centralus-linux-arm64-4core build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' test-container: '{"image":"ghcr.io/electron/test:arm64v8-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}' target-platform: linux @@ -334,7 +334,7 @@ jobs: needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: windows-latest target-platform: win target-arch: x64 @@ -353,7 +353,7 @@ jobs: needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: windows-latest target-platform: win target-arch: x86 @@ -372,7 +372,7 @@ jobs: needs: checkout-windows if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }} with: - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core test-runs-on: electron-hosted-windows-arm64-4core target-platform: win target-arch: arm64 diff --git a/.github/workflows/clean-src-cache.yml b/.github/workflows/clean-src-cache.yml index 0c4c5919a0ca3..9a1bfddccc888 100644 --- a/.github/workflows/clean-src-cache.yml +++ b/.github/workflows/clean-src-cache.yml @@ -10,7 +10,7 @@ on: jobs: clean-src-cache: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 options: --user root diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index 8cadd26d23bcc..7bf533f8eeae3 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -19,7 +19,7 @@ on: jobs: checkout-linux: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root @@ -43,7 +43,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: x64 @@ -59,7 +59,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: arm @@ -75,7 +75,7 @@ jobs: needs: checkout-linux with: environment: production-release - build-runs-on: electron-arc-linux-amd64-32core + build-runs-on: electron-arc-centralus-linux-amd64-32core build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' target-platform: linux target-arch: arm64 diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index c7241b6a3bb00..3c156fa2a3f61 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -20,7 +20,7 @@ on: jobs: checkout-macos: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root diff --git a/.github/workflows/pipeline-electron-docs-only.yml b/.github/workflows/pipeline-electron-docs-only.yml index eb5441d148222..062f3af2f57e7 100644 --- a/.github/workflows/pipeline-electron-docs-only.yml +++ b/.github/workflows/pipeline-electron-docs-only.yml @@ -15,7 +15,7 @@ concurrency: jobs: docs-only: name: Docs Only Compile - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 20 container: ${{ fromJSON(inputs.container) }} steps: diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index 6cdbff0259952..7c1b27d0a33b8 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -18,7 +18,7 @@ env: jobs: lint: name: Lint - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 20 container: ${{ fromJSON(inputs.container) }} steps: diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index ee381add0dbb4..b33c31ab99556 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -38,7 +38,7 @@ env: jobs: node-tests: name: Run Node.js Tests - runs-on: electron-arc-linux-amd64-8core + runs-on: electron-arc-centralus-linux-amd64-8core timeout-minutes: 30 env: TARGET_ARCH: ${{ inputs.target-arch }} @@ -92,7 +92,7 @@ jobs: done nan-tests: name: Run Nan Tests - runs-on: electron-arc-linux-amd64-4core + runs-on: electron-arc-centralus-linux-amd64-4core timeout-minutes: 30 env: TARGET_ARCH: ${{ inputs.target-arch }} diff --git a/.github/workflows/windows-publish.yml b/.github/workflows/windows-publish.yml index e8b7c6172fdd8..407ee819dc753 100644 --- a/.github/workflows/windows-publish.yml +++ b/.github/workflows/windows-publish.yml @@ -20,7 +20,7 @@ on: jobs: checkout-windows: - runs-on: electron-arc-linux-amd64-32core + runs-on: electron-arc-centralus-linux-amd64-32core container: image: ghcr.io/electron/build:${{ inputs.build-image-sha }} options: --user root --device /dev/fuse --cap-add SYS_ADMIN @@ -51,7 +51,7 @@ jobs: needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: x64 is-release: true @@ -65,7 +65,7 @@ jobs: needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: arm64 is-release: true @@ -79,7 +79,7 @@ jobs: needs: checkout-windows with: environment: production-release - build-runs-on: electron-arc-windows-amd64-16core + build-runs-on: electron-arc-centralus-windows-amd64-16core target-platform: win target-arch: x86 is-release: true From 6a9ff4df34d24e94d318ff8f36e37f76ce6d4f2b Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:15:47 -0400 Subject: [PATCH 327/339] chore: revert "ci: fixup mac runners" (#47926) This reverts commit 06d7a51a586fda203db91e21963c8fe15fb368d3. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/free-space-macos/action.yml | 5 +---- .github/workflows/pipeline-segment-electron-test.yml | 2 -- spec/chromium-spec.ts | 5 ++--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/actions/free-space-macos/action.yml b/.github/actions/free-space-macos/action.yml index 166f7877d32c7..75350ca796bad 100644 --- a/.github/actions/free-space-macos/action.yml +++ b/.github/actions/free-space-macos/action.yml @@ -6,8 +6,6 @@ runs: - name: Free Space on MacOS shell: bash run: | - echo "Disk usage before cleanup:" - df -h sudo mkdir -p $TMPDIR/del-target tmpify() { @@ -64,5 +62,4 @@ runs: # lipo off some huge binaries arm64 versions to save space strip_universal_deep $(xcode-select -p)/../SharedFrameworks - # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr - sudo mdutil -a -i off + # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr \ No newline at end of file diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 54d7a84680fd9..76a603b7b389d 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -103,8 +103,6 @@ jobs: "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" - "'kTCCServiceCamera',' /opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" - "'kTCCServiceBluetoothAlways',' /opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" ) for values in "${userValuesArray[@]}"; do # Sonoma and higher have a few extra values diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 8bddd5fd537dd..635ac6889c257 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3089,7 +3089,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); }); -describe('navigator.serial', () => { +ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.serial', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ @@ -3113,7 +3113,6 @@ describe('navigator.serial', () => { }); it('does not return a port if select-serial-port event is not defined', async () => { - // Take screenshot to verify the test is running w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); const port = await getPorts(); expect(port).to.equal(notFoundError); @@ -3630,7 +3629,7 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se }); }); -describe('navigator.bluetooth', () => { +ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.bluetooth', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ From 61ba91a254cdd8a403a0c5dc35c35501287d6bfc Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:42:55 +0200 Subject: [PATCH 328/339] fix: crash on `window.close()` with `webContents` on blur (#47953) fix: crash on window.close with WebContentsView on blur Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- .../browser/api/electron_api_web_contents.cc | 22 ++++++++++++------- spec/api-web-contents-view-spec.ts | 20 +++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index e2e34234420fc..c1b9aff5109e0 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2758,15 +2758,17 @@ void WebContents::CloseDevTools() { if (type_ == Type::kRemote) return; - DCHECK(inspectable_web_contents_); - inspectable_web_contents_->CloseDevTools(); + if (inspectable_web_contents_) + inspectable_web_contents_->CloseDevTools(); } bool WebContents::IsDevToolsOpened() { if (type_ == Type::kRemote) return false; - DCHECK(inspectable_web_contents_); + if (!inspectable_web_contents_) + return false; + return inspectable_web_contents_->IsDevToolsViewShowing(); } @@ -2774,19 +2776,24 @@ std::u16string WebContents::GetDevToolsTitle() { if (type_ == Type::kRemote) return {}; - DCHECK(inspectable_web_contents_); + if (!inspectable_web_contents_) + return {}; + return inspectable_web_contents_->GetDevToolsTitle(); } void WebContents::SetDevToolsTitle(const std::u16string& title) { - inspectable_web_contents_->SetDevToolsTitle(title); + if (inspectable_web_contents_) + inspectable_web_contents_->SetDevToolsTitle(title); } bool WebContents::IsDevToolsFocused() { if (type_ == Type::kRemote) return false; - DCHECK(inspectable_web_contents_); + if (!inspectable_web_contents_) + return false; + return inspectable_web_contents_->GetView()->IsDevToolsViewFocused(); } @@ -2834,10 +2841,9 @@ void WebContents::InspectElement(int x, int y) { if (type_ == Type::kRemote) return; - if (!enable_devtools_) + if (!enable_devtools_ || !inspectable_web_contents_) return; - DCHECK(inspectable_web_contents_); if (!inspectable_web_contents_->GetDevToolsWebContents()) OpenDevTools(nullptr); inspectable_web_contents_->InspectElement(x, y); diff --git a/spec/api-web-contents-view-spec.ts b/spec/api-web-contents-view-spec.ts index 63f45c140edd1..1d55c82531727 100644 --- a/spec/api-web-contents-view-spec.ts +++ b/spec/api-web-contents-view-spec.ts @@ -167,6 +167,26 @@ describe('WebContentsView', () => { }); }); + it('does not crash when closed via window.close()', async () => { + const bw = new BrowserWindow(); + const wcv = new WebContentsView(); + + await bw.loadURL('data:text/html,

Main Window

'); + bw.contentView.addChildView(wcv); + + const dto = new Promise((resolve) => { + wcv.webContents.on('blur', () => { + const devToolsOpen = wcv.webContents.isDevToolsOpened(); + resolve(devToolsOpen); + }); + }); + + wcv.webContents.loadURL('data:text/html,'); + + const open = await dto; + expect(open).to.be.false(); + }); + it('can be fullscreened', async () => { const w = new BaseWindow(); const v = new WebContentsView(); From c787127e9111c436eb9196de769bb671164c4f4e Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:08:55 +0200 Subject: [PATCH 329/339] fix: default to system accent color on invalid user color (#47921) fix: default to system accent color on invalid user color" Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/api/electron_api_base_window.cc | 2 +- .../api/electron_api_browser_window.cc | 4 +- .../browser/api/electron_api_web_contents.cc | 7 +-- shell/browser/native_window.cc | 2 +- shell/browser/native_window_views.cc | 4 +- shell/browser/native_window_views_win.cc | 47 ++++++++----------- shell/browser/ui/cocoa/electron_touch_bar.mm | 2 +- shell/browser/web_contents_preferences.cc | 2 +- shell/common/color_util.cc | 4 +- shell/common/color_util.h | 3 +- shell/common/gin_converters/gfx_converter.cc | 2 +- 11 files changed, 38 insertions(+), 41 deletions(-) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 81ada7acf9294..a51fab12c25f4 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -656,7 +656,7 @@ bool BaseWindow::IsTabletMode() const { } void BaseWindow::SetBackgroundColor(const std::string& color_name) { - SkColor color = ParseCSSColor(color_name); + SkColor color = ParseCSSColor(color_name).value_or(SK_ColorWHITE); window_->SetBackgroundColor(color); } diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index c352ee79a5de7..4af43407d8453 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -234,7 +234,7 @@ void BrowserWindow::Blur() { void BrowserWindow::SetBackgroundColor(const std::string& color_name) { BaseWindow::SetBackgroundColor(color_name); - SkColor color = ParseCSSColor(color_name); + SkColor color = ParseCSSColor(color_name).value_or(SK_ColorWHITE); if (api_web_contents_) { api_web_contents_->SetBackgroundColor(color); // Also update the web preferences object otherwise the view will be reset @@ -242,7 +242,7 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) { auto* web_preferences = WebContentsPreferences::From(api_web_contents_->web_contents()); if (web_preferences) { - web_preferences->SetBackgroundColor(ParseCSSColor(color_name)); + web_preferences->SetBackgroundColor(color); } } } diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index c1b9aff5109e0..0b7e37bd8c5c9 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -864,9 +864,10 @@ WebContents::WebContents(v8::Isolate* isolate, // webPreferences does not have a transparent option, so if the window needs // to be transparent, that will be set at electron_api_browser_window.cc#L57 // and we then need to pull it back out and check it here. - std::string background_color; - options.GetHidden(options::kBackgroundColor, &background_color); - bool transparent = ParseCSSColor(background_color) == SK_ColorTRANSPARENT; + std::string background_color_str; + options.GetHidden(options::kBackgroundColor, &background_color_str); + SkColor bc = ParseCSSColor(background_color_str).value_or(SK_ColorWHITE); + bool transparent = bc == SK_ColorTRANSPARENT; content::WebContents::CreateParams params(session->browser_context()); auto* view = new OffScreenWebContentsView( diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index e13c5fc552ba1..9a9736a67b5bf 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -229,7 +229,7 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) { SkColor background_color = SK_ColorWHITE; if (std::string color; options.Get(options::kBackgroundColor, &color)) { - background_color = ParseCSSColor(color); + background_color = ParseCSSColor(color).value_or(SK_ColorWHITE); } else if (IsTranslucent()) { background_color = SK_ColorTRANSPARENT; } diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 2b84e7fdd7df2..29dd83c8d2360 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -215,7 +215,9 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, overlay_symbol_color_ = color_utils::GetSysSkColor(COLOR_BTNTEXT); if (std::string str; options.Get(options::kAccentColor, &str)) { - accent_color_ = ParseCSSColor(str); + std::optional parsed_color = ParseCSSColor(str); + if (parsed_color.has_value()) + accent_color_ = parsed_color.value(); } else if (bool flag; options.Get(options::kAccentColor, &flag)) { accent_color_ = flag; } diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 159e77a9c0a18..cefd84e658112 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -572,43 +572,36 @@ void NativeWindowViews::UpdateWindowAccentColor() { if (base::win::GetVersion() < base::win::Version::WIN11) return; - COLORREF border_color; + std::optional border_color; bool should_apply_accent = false; - if (std::holds_alternative(accent_color_)) { - bool force_accent = std::get(accent_color_); - if (!force_accent) { - should_apply_accent = false; - } else { - std::optional accent_color = GetAccentColor(); - if (accent_color.has_value()) { - border_color = RGB(GetRValue(accent_color.value()), - GetGValue(accent_color.value()), - GetBValue(accent_color.value())); - should_apply_accent = true; - } - } - } else if (std::holds_alternative(accent_color_)) { + if (std::holds_alternative(accent_color_)) { + // If the user has explicitly set an accent color, use it + // regardless of whether the system accent color is enabled. SkColor color = std::get(accent_color_); border_color = RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); should_apply_accent = true; + } else if (std::holds_alternative(accent_color_)) { + // Allow the user to optionally force system color on/off. + should_apply_accent = std::get(accent_color_); } else if (std::holds_alternative(accent_color_)) { - if (IsAccentColorOnTitleBarsEnabled()) { - std::optional accent_color = GetAccentColor(); - if (accent_color.has_value()) { - border_color = RGB(GetRValue(accent_color.value()), - GetGValue(accent_color.value()), - GetBValue(accent_color.value())); - should_apply_accent = true; - } + // If no explicit color was set, default to the system accent color. + should_apply_accent = IsAccentColorOnTitleBarsEnabled(); + } + + // Use system accent color as fallback if no explicit color was set. + if (!border_color.has_value() && should_apply_accent) { + std::optional system_accent_color = GetAccentColor(); + if (system_accent_color.has_value()) { + border_color = RGB(GetRValue(system_accent_color.value()), + GetGValue(system_accent_color.value()), + GetBValue(system_accent_color.value())); } } - // Reset to default system colors when accent color should not be applied. - if (!should_apply_accent) - border_color = DWMWA_COLOR_DEFAULT; - SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), border_color); + COLORREF final_color = border_color.value_or(DWMWA_COLOR_DEFAULT); + SetWindowBorderAndCaptionColor(GetAcceleratedWidget(), final_color); } void NativeWindowViews::ResetWindowControls() { diff --git a/shell/browser/ui/cocoa/electron_touch_bar.mm b/shell/browser/ui/cocoa/electron_touch_bar.mm index ee198098f3baf..8fb63c7c823a8 100644 --- a/shell/browser/ui/cocoa/electron_touch_bar.mm +++ b/shell/browser/ui/cocoa/electron_touch_bar.mm @@ -338,7 +338,7 @@ - (bool)hasItemWithID:(const std::string&)item_id { } - (NSColor*)colorFromHexColorString:(const std::string&)colorString { - SkColor color = electron::ParseCSSColor(colorString); + SkColor color = electron::ParseCSSColor(colorString).value_or(SK_ColorWHITE); return skia::SkColorToDeviceNSColor(color); } diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index 7096c8bc5f81f..f4601a08bd9bc 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -216,7 +216,7 @@ void WebContentsPreferences::SetFromDictionary( } std::string background_color; if (web_preferences.GetHidden(options::kBackgroundColor, &background_color)) - background_color_ = ParseCSSColor(background_color); + background_color_ = ParseCSSColor(background_color).value_or(SK_ColorWHITE); std::string safe_dialogs_message; if (web_preferences.Get("safeDialogsMessage", &safe_dialogs_message)) safe_dialogs_message_ = safe_dialogs_message; diff --git a/shell/common/color_util.cc b/shell/common/color_util.cc index d8b169a4e4d42..66669a38c5bb2 100644 --- a/shell/common/color_util.cc +++ b/shell/common/color_util.cc @@ -28,7 +28,7 @@ bool IsHexFormatWithAlpha(const std::string& str) { namespace electron { -SkColor ParseCSSColor(const std::string& color_string) { +std::optional ParseCSSColor(const std::string& color_string) { // ParseCssColorString expects RGBA and we historically use ARGB // so we need to convert before passing to ParseCssColorString. std::string converted_color_str; @@ -42,7 +42,7 @@ SkColor ParseCSSColor(const std::string& color_string) { SkColor color; if (!content::ParseCssColorString(converted_color_str, &color)) - color = SK_ColorWHITE; + return std::nullopt; return color; } diff --git a/shell/common/color_util.h b/shell/common/color_util.h index 8ea422e75b1b8..c1e880d8fe4af 100644 --- a/shell/common/color_util.h +++ b/shell/common/color_util.h @@ -5,6 +5,7 @@ #ifndef ELECTRON_SHELL_COMMON_COLOR_UTIL_H_ #define ELECTRON_SHELL_COMMON_COLOR_UTIL_H_ +#include #include #include "third_party/skia/include/core/SkColor.h" @@ -22,7 +23,7 @@ namespace electron { // Parses a CSS-style color string from hex, rgb(), rgba(), // hsl(), hsla(), or color name formats. -SkColor ParseCSSColor(const std::string& color_string); +std::optional ParseCSSColor(const std::string& color_string); // Convert color to RGB hex value like "#RRGGBB". std::string ToRGBHex(SkColor color); diff --git a/shell/common/gin_converters/gfx_converter.cc b/shell/common/gin_converters/gfx_converter.cc index c54ec7062eabd..2af4e8a097e85 100644 --- a/shell/common/gin_converters/gfx_converter.cc +++ b/shell/common/gin_converters/gfx_converter.cc @@ -226,7 +226,7 @@ bool Converter::FromV8(v8::Isolate* isolate, std::string str; if (!gin::ConvertFromV8(isolate, val, &str)) return false; - *out = electron::ParseCSSColor(str); + *out = electron::ParseCSSColor(str).value_or(SK_ColorWHITE); return true; } From e6ac03d0eeab466841b5ced44ddb53dce3b26d3d Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Tue, 5 Aug 2025 16:34:17 +0200 Subject: [PATCH 330/339] fix: abnormal behavior of windows background material (#47957) --- patches/chromium/.patches | 2 +- ...ivate_background_material_on_windows.patch | 68 ----------- ..._material_update_issue_on_windows_11.patch | 115 ++++++++++++++++++ shell/browser/api/electron_api_base_window.cc | 4 +- shell/browser/api/electron_api_base_window.h | 2 +- .../api/electron_api_browser_window.cc | 13 ++ .../browser/api/electron_api_browser_window.h | 1 + shell/browser/native_window.h | 4 + shell/browser/native_window_views.cc | 84 +++++++------ .../electron_desktop_window_tree_host_win.cc | 9 ++ 10 files changed, 195 insertions(+), 107 deletions(-) delete mode 100644 patches/chromium/fix_activate_background_material_on_windows.patch create mode 100644 patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index ddb8f6e1976bb..19a987b9e4791 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -110,7 +110,6 @@ fix_use_delegated_generic_capturer_when_available.patch expose_webblob_path_to_allow_embedders_to_get_file_paths.patch fix_move_autopipsettingshelper_behind_branding_buildflag.patch revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch -fix_activate_background_material_on_windows.patch feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch chore_remove_check_is_test_on_script_injection_tracker.patch fix_restore_original_resize_performance_on_macos.patch @@ -149,3 +148,4 @@ fix_osr_stutter_fix_backport_for_electron.patch do_not_check_the_order_of_display_id_order_on_windows.patch make_focus_methods_in_webcontentsviewchildframe_notimplemented.patch cherry-pick-f1e6422a355c.patch +fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch diff --git a/patches/chromium/fix_activate_background_material_on_windows.patch b/patches/chromium/fix_activate_background_material_on_windows.patch deleted file mode 100644 index 2b561ab7cce82..0000000000000 --- a/patches/chromium/fix_activate_background_material_on_windows.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: clavin -Date: Mon, 11 Dec 2023 20:43:34 -0300 -Subject: fix: activate background material on windows - -This patch adds a condition to the HWND message handler to allow windows -with translucent background materials to become activated. - -It also ensures the lParam of WM_NCACTIVATE is set to -1 so as to not repaint -the client area, which can lead to a title bar incorrectly being displayed in -frameless windows. - -This patch likely can't be upstreamed as-is, as Chromium doesn't have -this use case in mind currently. - -diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..5c74a092920cdb3b7e98d218c75874cac2d0542f 100644 ---- a/ui/views/win/hwnd_message_handler.cc -+++ b/ui/views/win/hwnd_message_handler.cc -@@ -954,13 +954,13 @@ void HWNDMessageHandler::FrameTypeChanged() { - - void HWNDMessageHandler::PaintAsActiveChanged() { - if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || -- !delegate_->HasFrame() || -+ (!delegate_->HasFrame() && !is_translucent_) || - (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) { - return; - } - - DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(), -- 0); -+ delegate_->HasFrame() ? 0 : -1); - } - - void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, -@@ -1757,7 +1757,7 @@ void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) { - if (delegate_->HasNonClientView() && !active && - thread_id != GetCurrentThreadId()) { - // Update the native frame if it is rendering the non-client area. -- if (HasSystemFrame()) { -+ if (is_translucent_ || HasSystemFrame()) { - DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0); - } - } -@@ -2365,17 +2365,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, - delegate_->SchedulePaint(); - } - -- // Calling DefWindowProc is only necessary if there's a system frame being -- // drawn. Otherwise it can draw an incorrect title bar and cause visual -- // corruption. -- if (!delegate_->HasFrame() || -+ // If the window is translucent, it may have the Mica background. -+ // In that case, it's necessary to call |DefWindowProc|, but we can -+ // pass -1 in the lParam to prevent any non-client area elements from -+ // being displayed. -+ if ((!delegate_->HasFrame() && !is_translucent_) || - delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { - SetMsgHandled(TRUE); - return TRUE; - } - - return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active, -- 0); -+ delegate_->HasFrame() ? 0 : -1); - } - - LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { diff --git a/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch b/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch new file mode 100644 index 0000000000000..2800abe2e38ca --- /dev/null +++ b/patches/chromium/fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: zoy +Date: Mon, 5 May 2025 23:28:53 +0800 +Subject: fix: resolve dynamic background material update issue on Windows 11 + +This patch addresses issues with background materials on Windows 11, +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 4780229a4453ad5ca2c89b786459f64c58bf2272..9074b7883f2fcf1341683c68ab7a398acefb0559 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 +@@ -169,6 +169,10 @@ void DesktopWindowTreeHostWin::FinishTouchDrag(gfx::Point screen_point) { + } + } + ++void DesktopWindowTreeHostWin::SetIsTranslucent(bool is_translucent) { ++ message_handler_->set_is_translucent(is_translucent); ++} ++ + //////////////////////////////////////////////////////////////////////////////// + // DesktopWindowTreeHostWin, WidgetObserver implementation: + void DesktopWindowTreeHostWin::OnWidgetThemeChanged(Widget* widget) { +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 37109b8d3d439073b5c9e2ea3597c36f32de5704..888fa89d3641ea08a413c0351262cdc967ec12f8 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 : public DesktopWindowTreeHost, + // false. + void FinishTouchDrag(gfx::Point screen_point); + ++ void SetIsTranslucent(bool is_translucent); ++ + protected: + // Overridden from DesktopWindowTreeHost: + void Init(const Widget::InitParams& params) override; +diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc +index ecab7d7f95998a055f01d7b5180a8fb80eddb32c..303c186b0a7fbf6b9f7a40e00fc708fecbf68e8d 100644 +--- a/ui/views/win/hwnd_message_handler.cc ++++ b/ui/views/win/hwnd_message_handler.cc +@@ -954,13 +954,13 @@ void HWNDMessageHandler::FrameTypeChanged() { + + void HWNDMessageHandler::PaintAsActiveChanged() { + if (!delegate_->HasNonClientView() || !delegate_->CanActivate() || +- !delegate_->HasFrame() || ++ (!delegate_->HasFrame() && !is_translucent_) || + (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) { + return; + } + + DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(), +- 0); ++ delegate_->HasFrame() ? 0 : -1); + } + + void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon, +@@ -1055,7 +1055,14 @@ void HWNDMessageHandler::SizeConstraintsChanged() { + // allowing ui::GetResizableFrameThickness() to be used consistently when + // removing the visible system frame. + const bool had_caption_on_init = window_style() & WS_CAPTION; +- const bool can_resize = !is_translucent_ && delegate_->CanResize(); ++ ++ // In Chromium, the !is_translucent_ check was introduced for Glic-specific ++ // behavior. Since Electron does not use Glic, this restriction can be safely ++ // removed. Keeping the is_translucent_ check disables maximization for ++ // translucent framed windows. Original code: !is_translucent_ && ++ // delegate_->CanResize() See: ++ // https://chromium-review.googlesource.com/c/chromium/src/+/6372329 ++ const bool can_resize = delegate_->CanResize(); + const bool can_maximize = can_resize && delegate_->CanMaximize(); + + auto set_style_func = [&style](LONG bit, bool should_set) { +@@ -1647,11 +1654,16 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { + // through, but that isn't the case when using Direct3D to draw transparent + // windows. So we route translucent windows throught to the delegate to + // allow for a custom hit mask. +- if (!is_translucent_ && !custom_window_region_.is_valid() && ++ // patch: fix_resolve_dynamic_background_material_update_issue_on_windows_11 ++ // Our translucent windows use the native frame by default, and we should not ++ // set a custom region when the window is maximized; otherwise, it will cause ++ // a white title bar to appear under Windows 11. ++ if (!custom_window_region_.is_valid() && + (IsFrameSystemDrawn() || !delegate_->HasNonClientView())) { + if (force) { + SetWindowRgn(hwnd(), nullptr, redraw); + } ++ + return; + } + +@@ -2365,17 +2377,18 @@ LRESULT HWNDMessageHandler::OnNCActivate(UINT message, + delegate_->SchedulePaint(); + } + +- // Calling DefWindowProc is only necessary if there's a system frame being +- // drawn. Otherwise it can draw an incorrect title bar and cause visual +- // corruption. +- if (!delegate_->HasFrame() || ++ // If the window is translucent, it may have the Mica background. ++ // In that case, it's necessary to call |DefWindowProc|, but we can ++ // pass -1 in the lParam to prevent any non-client area elements from ++ // being displayed. ++ if ((!delegate_->HasFrame() && !is_translucent_) || + delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { + SetMsgHandled(TRUE); + return TRUE; + } + + return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active, +- 0); ++ delegate_->HasFrame() ? 0 : -1); + } + + LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index a51fab12c25f4..0f121f9822126 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -850,8 +850,8 @@ void BaseWindow::SetVibrancy(v8::Isolate* isolate, window_->SetVibrancy(type, animation_duration_ms); } -void BaseWindow::SetBackgroundMaterial(const std::string& material_type) { - window_->SetBackgroundMaterial(material_type); +void BaseWindow::SetBackgroundMaterial(const std::string& material) { + window_->SetBackgroundMaterial(material); } #if BUILDFLAG(IS_MAC) diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index e516bf278d9a6..103f33c357cbf 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -196,7 +196,7 @@ class BaseWindow : public gin_helper::TrackableObject, virtual void SetVibrancy(v8::Isolate* isolate, v8::Local value, gin_helper::Arguments* args); - void SetBackgroundMaterial(const std::string& vibrancy); + virtual void SetBackgroundMaterial(const std::string& material); #if BUILDFLAG(IS_MAC) std::string GetAlwaysOnTopLevel() const; diff --git a/shell/browser/api/electron_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc index 4af43407d8453..f4af58e8b1bec 100644 --- a/shell/browser/api/electron_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -4,6 +4,7 @@ #include "shell/browser/api/electron_api_browser_window.h" +#include "base/containers/fixed_flat_set.h" #include "content/browser/renderer_host/render_widget_host_owner_delegate.h" // nogncheck #include "content/browser/web_contents/web_contents_impl.h" // nogncheck #include "content/public/browser/render_process_host.h" @@ -247,6 +248,18 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) { } } +void BrowserWindow::SetBackgroundMaterial(const std::string& material) { + BaseWindow::SetBackgroundMaterial(material); + static constexpr auto materialTypes = + base::MakeFixedFlatSet({"tabbed", "mica", "acrylic"}); + + if (materialTypes.contains(material)) { + SetBackgroundColor(ToRGBAHex(SK_ColorTRANSPARENT)); + } else if (material == "none") { + SetBackgroundColor(ToRGBAHex(SK_ColorWHITE)); + } +} + void BrowserWindow::FocusOnWebView() { web_contents()->GetRenderViewHost()->GetWidget()->Focus(); } diff --git a/shell/browser/api/electron_api_browser_window.h b/shell/browser/api/electron_api_browser_window.h index 4e8822e487cf4..b13ffda8f8a18 100644 --- a/shell/browser/api/electron_api_browser_window.h +++ b/shell/browser/api/electron_api_browser_window.h @@ -64,6 +64,7 @@ class BrowserWindow : public BaseWindow, void Focus() override; void Blur() override; void SetBackgroundColor(const std::string& color_name) override; + void SetBackgroundMaterial(const std::string& material) override; void OnWindowShow() override; void OnWindowHide() override; diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 34b7cdf959fa9..8f84cf759a878 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -231,6 +231,10 @@ class NativeWindow : public base::SupportsUserData, // Vibrancy API virtual void SetVibrancy(const std::string& type, int duration); + const std::string& background_material() const { + return background_material_; + } + virtual void SetBackgroundMaterial(const std::string& type); // Traffic Light API diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index 29dd83c8d2360..aac6c2f07a43f 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -79,6 +79,7 @@ #include "base/win/windows_version.h" #include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/ui/win/electron_desktop_native_widget_aura.h" +#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h" #include "shell/common/color_util.h" #include "skia/ext/skia_utils_win.h" #include "ui/display/win/screen_win.h" @@ -353,6 +354,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, if (!has_frame()) { // Set Window style so that we get a minimize and maximize animation when // frameless. + DWORD frame_style = WS_CAPTION | WS_OVERLAPPED; if (resizable_) frame_style |= WS_THICKFRAME; @@ -648,13 +650,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { void NativeWindowViews::Maximize() { #if BUILDFLAG(IS_WIN) - if (IsTranslucent()) { - // Semi-transparent windows with backgroundMaterial not set to 'none', and - // not fully transparent, require manual handling of rounded corners when - // maximized. - if (rounded_corner_) - SetRoundedCorners(false); - + if (transparent()) { restore_bounds_ = GetBounds(); auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( GetNativeWindow()); @@ -678,15 +674,10 @@ void NativeWindowViews::Unmaximize() { return; #if BUILDFLAG(IS_WIN) - if (IsTranslucent()) { + if (transparent()) { SetBounds(restore_bounds_, false); NotifyWindowUnmaximize(); - if (transparent()) { - UpdateThickFrame(); - } - if (rounded_corner_) { - SetRoundedCorners(true); - } + UpdateThickFrame(); return; } #endif @@ -703,7 +694,7 @@ bool NativeWindowViews::IsMaximized() const { return true; #if BUILDFLAG(IS_WIN) - if (IsTranslucent() && !IsMinimized()) { + if (transparent() && !IsMinimized()) { // If the window is the same dimensions and placement as the // display, we consider it maximized. auto display = display::Screen::GetScreen()->GetDisplayNearestWindow( @@ -725,15 +716,10 @@ void NativeWindowViews::Minimize() { void NativeWindowViews::Restore() { #if BUILDFLAG(IS_WIN) - if (IsMaximized() && IsTranslucent()) { + if (IsMaximized() && transparent()) { SetBounds(restore_bounds_, false); NotifyWindowRestore(); - if (transparent()) { - UpdateThickFrame(); - } - if (rounded_corner_) { - SetRoundedCorners(true); - } + UpdateThickFrame(); return; } #endif @@ -879,7 +865,7 @@ gfx::Size NativeWindowViews::GetContentSize() const { gfx::Rect NativeWindowViews::GetNormalBounds() const { #if BUILDFLAG(IS_WIN) - if (IsMaximized() && IsTranslucent()) + if (IsMaximized() && transparent()) return restore_bounds_; #endif return widget()->GetRestoredBounds(); @@ -1519,25 +1505,53 @@ void NativeWindowViews::SetBackgroundMaterial(const std::string& material) { return; DWM_SYSTEMBACKDROP_TYPE backdrop_type = GetBackdropFromString(material); - HRESULT result = - DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_SYSTEMBACKDROP_TYPE, - &backdrop_type, sizeof(backdrop_type)); + const bool is_translucent = backdrop_type != DWMSBT_NONE && + backdrop_type != DWMSBT_AUTO && !has_frame(); + + HWND hwnd = GetAcceleratedWidget(); + + // We need to update margins ourselves since Chromium won't. + // See: ui/views/widget/widget_hwnd_utils.cc#157 + // See: src/ui/views/win/hwnd_message_handler.cc#1793 + MARGINS m = {0, 0, 0, 0}; + if (is_translucent) + m = {-1, -1, -1, -1}; + + HRESULT result = DwmExtendFrameIntoClientArea(hwnd, &m); + if (FAILED(result)) + LOG(WARNING) << "Failed to extend frame into client area"; + + result = DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, + &backdrop_type, sizeof(backdrop_type)); if (FAILED(result)) LOG(WARNING) << "Failed to set background material to " << material; + auto* desktop_window_tree_host = + static_cast( + GetNativeWindow()->GetHost()); + + // Synchronize the internal state; otherwise, the background material may not + // work properly. + if (desktop_window_tree_host) { + desktop_window_tree_host->SetIsTranslucent(is_translucent); + } + + auto* desktop_native_widget_aura = + static_cast(widget()->native_widget()); + desktop_native_widget_aura->UpdateWindowTransparency(); + // For frameless windows with a background material set, we also need to // remove the caption color so it doesn't render a caption bar (since the // window is frameless) - COLORREF caption_color = DWMWA_COLOR_DEFAULT; - if (backdrop_type != DWMSBT_NONE && backdrop_type != DWMSBT_AUTO && - !has_frame()) { - caption_color = DWMWA_COLOR_NONE; - } - result = DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_CAPTION_COLOR, - &caption_color, sizeof(caption_color)); - + COLORREF caption_color = + is_translucent ? DWMWA_COLOR_NONE : DWMWA_COLOR_DEFAULT; + result = DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &caption_color, + sizeof(caption_color)); if (FAILED(result)) LOG(WARNING) << "Failed to set caption color to transparent"; + + // Activate the non-client area of the window + DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, has_frame() ? 0 : -1); #endif } @@ -1870,7 +1884,7 @@ ui::mojom::WindowShowState NativeWindowViews::GetRestoredState() { if (IsMaximized()) { #if BUILDFLAG(IS_WIN) // Restore maximized state for windows that are not translucent. - if (!IsTranslucent()) { + if (!transparent()) { return ui::mojom::WindowShowState::kMaximized; } #else diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc index 6eed0a338390b..d0e4ca23bf8a6 100644 --- a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -64,7 +64,16 @@ bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels( gfx::Insets* insets) const { // Set DWMFrameInsets to prevent maximized frameless window from bleeding // into other monitors. + if (IsMaximized() && !native_window_view_->has_frame()) { + // We avoid doing this when the window is translucent (e.g. using + // backgroundMaterial effects), because setting zero insets can interfere + // with DWM rendering of blur or acrylic, potentially causing visual + // glitches. + const std::string& bg_material = native_window_view_->background_material(); + if (!bg_material.empty() && bg_material != "none") { + return false; + } // This would be equivalent to calling: // DwmExtendFrameIntoClientArea({0, 0, 0, 0}); // From 7133cbab7f71924e18a4b648d1002c7f7b9e1631 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:35:00 +0200 Subject: [PATCH 331/339] feat: add `app.getRecentDocuments()` (#47925) feat: add app.getRecentDocuments() Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- docs/api/app.md | 16 ++++++++ docs/tutorial/recent-documents.md | 6 +++ shell/browser/api/electron_api_app.cc | 2 + shell/browser/browser.h | 3 ++ shell/browser/browser_linux.cc | 4 ++ shell/browser/browser_mac.mm | 24 +++++++++--- shell/browser/browser_win.cc | 53 +++++++++++++++++++++++++-- spec/api-app-spec.ts | 45 ++++++++++++++++++++++- 8 files changed, 142 insertions(+), 11 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 119c867abef9b..0ff66e518e1fe 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -775,6 +775,22 @@ bar, and on macOS, you can visit it from dock menu. Clears the recent documents list. +### `app.getRecentDocuments()` _macOS_ _Windows_ + +Returns `string[]` - An array containing documents in the most recent documents list. + +```js +const { app } = require('electron') + +const path = require('node:path') + +const file = path.join(app.getPath('desktop'), 'foo.txt') +app.addRecentDocument(file) + +const recents = app.getRecentDocuments() +console.log(recents) // ['/path/to/desktop/foo.txt'} +``` + ### `app.setAsDefaultProtocolClient(protocol[, path, args])` * `protocol` string - The name of your protocol, without `://`. For example, diff --git a/docs/tutorial/recent-documents.md b/docs/tutorial/recent-documents.md index 8bbe27aad952c..072d8a12e170b 100644 --- a/docs/tutorial/recent-documents.md +++ b/docs/tutorial/recent-documents.md @@ -77,6 +77,11 @@ To clear the list of recent documents, use the In this guide, the list of documents is cleared once all windows have been closed. +#### Accessing the list of recent documents + +To access the list of recent documents, use the +[app.getRecentDocuments][getrecentdocuments] API. + ## Additional information ### Windows Notes @@ -138,5 +143,6 @@ of `app` module will be emitted for it. [dock-menu-image]: https://cloud.githubusercontent.com/assets/639601/5069610/2aa80758-6e97-11e4-8cfb-c1a414a10774.png [addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-macos-windows [clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-macos-windows +[getrecentdocuments]: ../api/app.md#appgetrecentdocuments-macos-windows [app-registration]: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration [menu-item-image]: https://user-images.githubusercontent.com/3168941/33003655-ea601c3a-cd70-11e7-97fa-7c062149cfb1.png diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc index b394fb0dbee2a..323aa6a3399d0 100644 --- a/shell/browser/api/electron_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -1718,6 +1718,8 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) { base::BindRepeating(&Browser::AddRecentDocument, browser)) .SetMethod("clearRecentDocuments", base::BindRepeating(&Browser::ClearRecentDocuments, browser)) + .SetMethod("getRecentDocuments", + base::BindRepeating(&Browser::GetRecentDocuments, browser)) #if BUILDFLAG(IS_WIN) .SetMethod("setAppUserModelId", base::BindRepeating(&Browser::SetAppUserModelID, browser)) diff --git a/shell/browser/browser.h b/shell/browser/browser.h index aa5c8207c56be..f4edd1d7d77aa 100644 --- a/shell/browser/browser.h +++ b/shell/browser/browser.h @@ -125,6 +125,9 @@ class Browser : private WindowListObserver { // Clear the recent documents list. void ClearRecentDocuments(); + // Return the recent documents list. + std::vector GetRecentDocuments(); + #if BUILDFLAG(IS_WIN) // Set the application user model ID. void SetAppUserModelID(const std::wstring& name); diff --git a/shell/browser/browser_linux.cc b/shell/browser/browser_linux.cc index 99804abe37865..2610ffe80af20 100644 --- a/shell/browser/browser_linux.cc +++ b/shell/browser/browser_linux.cc @@ -94,6 +94,10 @@ bool SetDefaultWebClient(const std::string& protocol) { void Browser::AddRecentDocument(const base::FilePath& path) {} +std::vector Browser::GetRecentDocuments() { + return std::vector(); +} + void Browser::ClearRecentDocuments() {} bool Browser::SetAsDefaultProtocolClient(const std::string& protocol, diff --git a/shell/browser/browser_mac.mm b/shell/browser/browser_mac.mm index 1a78da0a650db..c4b67ed5cc142 100644 --- a/shell/browser/browser_mac.mm +++ b/shell/browser/browser_mac.mm @@ -162,19 +162,31 @@ LoginItemSettings GetLoginItemSettingsDeprecated() { } void Browser::AddRecentDocument(const base::FilePath& path) { - NSString* path_string = base::apple::FilePathToNSString(path); - if (!path_string) + NSURL* url = base::apple::FilePathToNSURL(path); + if (!url) { + LOG(WARNING) << "Failed to convert file path " << path.value() + << " to NSURL"; return; - NSURL* u = [NSURL fileURLWithPath:path_string]; - if (!u) - return; - [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:u]; + } + + [[NSDocumentController sharedDocumentController] + noteNewRecentDocumentURL:url]; } void Browser::ClearRecentDocuments() { [[NSDocumentController sharedDocumentController] clearRecentDocuments:nil]; } +std::vector Browser::GetRecentDocuments() { + NSArray* recentURLs = + [[NSDocumentController sharedDocumentController] recentDocumentURLs]; + std::vector documents; + documents.reserve([recentURLs count]); + for (NSURL* url in recentURLs) + documents.push_back(std::string([url.path UTF8String])); + return documents; +} + bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol, gin::Arguments* args) { NSString* identifier = [base::apple::MainBundle() bundleIdentifier]; diff --git a/shell/browser/browser_win.cc b/shell/browser/browser_win.cc index fb75895404eb2..3b08b32cf6add 100644 --- a/shell/browser/browser_win.cc +++ b/shell/browser/browser_win.cc @@ -17,6 +17,7 @@ #include "base/base_paths.h" #include "base/command_line.h" #include "base/file_version_info.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/path_service.h" @@ -314,14 +315,33 @@ void GetApplicationInfoForProtocolUsingAssocQuery( app_display_name, std::move(promise)); } +std::string ResolveShortcut(const base::FilePath& lnk_path) { + std::string target_path; + + CComPtr shell_link; + if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&shell_link)))) { + CComPtr persist_file; + if (SUCCEEDED(shell_link->QueryInterface(IID_PPV_ARGS(&persist_file)))) { + if (SUCCEEDED(persist_file->Load(lnk_path.value().c_str(), STGM_READ))) { + WCHAR resolved_path[MAX_PATH]; + if (SUCCEEDED( + shell_link->GetPath(resolved_path, MAX_PATH, nullptr, 0))) { + target_path = base::FilePath(resolved_path).MaybeAsASCII(); + } + } + } + } + + return target_path; +} + void Browser::AddRecentDocument(const base::FilePath& path) { CComPtr item; HRESULT hr = SHCreateItemFromParsingName(path.value().c_str(), nullptr, IID_PPV_ARGS(&item)); if (SUCCEEDED(hr)) { - SHARDAPPIDINFO info; - info.psi = item; - info.pszAppID = GetAppUserModelID(); + SHARDAPPIDINFO info = {item, GetAppUserModelID()}; SHAddToRecentDocs(SHARD_APPIDINFO, &info); } } @@ -330,6 +350,33 @@ void Browser::ClearRecentDocuments() { SHAddToRecentDocs(SHARD_APPIDINFO, nullptr); } +std::vector Browser::GetRecentDocuments() { + ScopedAllowBlockingForElectron allow_blocking; + std::vector docs; + + PWSTR recent_path_ptr = nullptr; + HRESULT hr = + SHGetKnownFolderPath(FOLDERID_Recent, 0, nullptr, &recent_path_ptr); + if (SUCCEEDED(hr) && recent_path_ptr) { + base::FilePath recent_folder(recent_path_ptr); + CoTaskMemFree(recent_path_ptr); + + base::FileEnumerator enumerator(recent_folder, /*recursive=*/false, + base::FileEnumerator::FILES, + FILE_PATH_LITERAL("*.lnk")); + + for (base::FilePath file = enumerator.Next(); !file.empty(); + file = enumerator.Next()) { + std::string resolved_path = ResolveShortcut(file); + if (!resolved_path.empty()) { + docs.push_back(resolved_path); + } + } + } + + return docs; +} + void Browser::SetAppUserModelID(const std::wstring& name) { electron::SetAppUserModelID(name); } diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index 1aa4daae04e07..e6f21f34850ad 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -11,6 +11,7 @@ import * as http from 'node:http'; import * as https from 'node:https'; import * as net from 'node:net'; import * as path from 'node:path'; +import { setTimeout } from 'node:timers/promises'; import { promisify } from 'node:util'; import { collectStreamBody, getResponse } from './lib/net-helpers'; @@ -19,6 +20,8 @@ import { closeWindow, closeAllWindows } from './lib/window-helpers'; const fixturesPath = path.resolve(__dirname, 'fixtures'); +const isMacOSx64 = process.platform === 'darwin' && process.arch === 'x64'; + describe('electron module', () => { it('does not expose internal modules to require', () => { expect(() => { @@ -356,6 +359,44 @@ describe('app module', () => { }); }); + // GitHub Actions macOS-13 runners used for x64 seem to have a problem with this test. + ifdescribe(process.platform !== 'linux' && !isMacOSx64)('app.{add|get|clear}RecentDocument(s)', () => { + const tempFiles = [ + path.join(fixturesPath, 'foo.txt'), + path.join(fixturesPath, 'bar.txt'), + path.join(fixturesPath, 'baz.txt') + ]; + + afterEach(() => { + app.clearRecentDocuments(); + for (const file of tempFiles) { + fs.unlinkSync(file); + } + }); + + beforeEach(() => { + for (const file of tempFiles) { + fs.writeFileSync(file, 'Lorem Ipsum'); + } + }); + + it('can add a recent document', async () => { + app.addRecentDocument(tempFiles[0]); + await setTimeout(2000); + expect(app.getRecentDocuments()).to.include.members([tempFiles[0]]); + }); + + it('can clear recent documents', async () => { + app.addRecentDocument(tempFiles[1]); + app.addRecentDocument(tempFiles[2]); + await setTimeout(2000); + expect(app.getRecentDocuments()).to.include.members([tempFiles[1], tempFiles[2]]); + app.clearRecentDocuments(); + await setTimeout(2000); + expect(app.getRecentDocuments()).to.deep.equal([]); + }); + }); + describe('app.relaunch', () => { let server: net.Server | null = null; const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; @@ -553,8 +594,8 @@ describe('app module', () => { describe('app.badgeCount', () => { const platformIsNotSupported = - (process.platform === 'win32') || - (process.platform === 'linux' && !app.isUnityRunning()); + (process.platform === 'win32') || + (process.platform === 'linux' && !app.isUnityRunning()); const expectedBadgeCount = 42; From 8b161f0b338f23253b8f75621e084cd320a3f65c Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:16:30 -0500 Subject: [PATCH 332/339] test: add TS smoke test for electron/utility (#47978) chore: add TS smoke test for electron/utility Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- spec/ts-smoke/electron/utility.ts | 67 +++++++++++++++++++++++++++++++ spec/ts-smoke/tsconfig.json | 1 + 2 files changed, 68 insertions(+) create mode 100644 spec/ts-smoke/electron/utility.ts diff --git a/spec/ts-smoke/electron/utility.ts b/spec/ts-smoke/electron/utility.ts new file mode 100644 index 0000000000000..090e1131e3b3c --- /dev/null +++ b/spec/ts-smoke/electron/utility.ts @@ -0,0 +1,67 @@ +/* eslint-disable */ + +import { net, systemPreferences } from 'electron/utility'; + +process.parentPort.on('message', (e) => { + if (e.data === 'Hello from parent!') { + process.parentPort.postMessage('Hello from child!'); + } +}); + +// net +// https://github.com/electron/electron/blob/main/docs/api/net.md + +const request = net.request('https://github.com'); +request.setHeader('Some-Custom-Header-Name', 'Some-Custom-Header-Value'); +const header = request.getHeader('Some-Custom-Header-Name'); +console.log('header', header); +request.removeHeader('Some-Custom-Header-Name'); +request.on('response', (response) => { + console.log(`Status code: ${response.statusCode}`); + console.log(`Status message: ${response.statusMessage}`); + console.log(`Headers: ${JSON.stringify(response.headers)}`); + console.log(`Http version: ${response.httpVersion}`); + console.log(`Major Http version: ${response.httpVersionMajor}`); + console.log(`Minor Http version: ${response.httpVersionMinor}`); + response.on('data', (chunk) => { + console.log(`BODY: ${chunk}`); + }); + response.on('end', () => { + console.log('No more data in response.'); + }); + response.on('error', () => { + console.log('"error" event emitted'); + }); + response.on('aborted', () => { + console.log('"aborted" event emitted'); + }); +}); +request.on('login', (authInfo, callback) => { + callback('username', 'password'); +}); +request.on('finish', () => { + console.log('"finish" event emitted'); +}); +request.on('abort', () => { + console.log('"abort" event emitted'); +}); +request.on('error', () => { + console.log('"error" event emitted'); +}); +request.write('Hello World!', 'utf-8'); +request.end('Hello World!', 'utf-8'); +request.abort(); + +// systemPreferences +// https://github.com/electron/electron/blob/main/docs/api/system-preferences.md + +if (process.platform === 'win32') { + systemPreferences.on('color-changed', () => { console.log('color changed'); }); +} + +if (process.platform === 'darwin') { + const value = systemPreferences.getUserDefault('Foo', 'string'); + console.log(value); + const value2 = systemPreferences.getUserDefault('Foo', 'boolean'); + console.log(value2); +} diff --git a/spec/ts-smoke/tsconfig.json b/spec/ts-smoke/tsconfig.json index 35f95b765351b..2e19b963c1a9a 100644 --- a/spec/ts-smoke/tsconfig.json +++ b/spec/ts-smoke/tsconfig.json @@ -15,6 +15,7 @@ "files": [ "electron/main.ts", "electron/renderer.ts", + "electron/utility.ts", "../../electron.d.ts" ] } \ No newline at end of file From ce6d4b0d64a24ba81e23893a3d84981ccb708b87 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 10:28:27 -0400 Subject: [PATCH 333/339] ci: fixup mac runner hang (#47991) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt --- .github/actions/free-space-macos/action.yml | 5 ++++- .github/workflows/pipeline-segment-electron-test.yml | 2 ++ spec/chromium-spec.ts | 5 +++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/actions/free-space-macos/action.yml b/.github/actions/free-space-macos/action.yml index 75350ca796bad..166f7877d32c7 100644 --- a/.github/actions/free-space-macos/action.yml +++ b/.github/actions/free-space-macos/action.yml @@ -6,6 +6,8 @@ runs: - name: Free Space on MacOS shell: bash run: | + echo "Disk usage before cleanup:" + df -h sudo mkdir -p $TMPDIR/del-target tmpify() { @@ -62,4 +64,5 @@ runs: # lipo off some huge binaries arm64 versions to save space strip_universal_deep $(xcode-select -p)/../SharedFrameworks - # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr \ No newline at end of file + # strip_arm_deep /System/Volumes/Data/Library/Developer/CommandLineTools/usr + sudo mdutil -a -i off diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 76a603b7b389d..c620d80c1d6d4 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -103,6 +103,8 @@ jobs: "'kTCCServiceCamera','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceCamera','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceBluetoothAlways','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" ) for values in "${userValuesArray[@]}"; do # Sonoma and higher have a few extra values diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 635ac6889c257..8bddd5fd537dd 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -3089,7 +3089,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); }); -ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.serial', () => { +describe('navigator.serial', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ @@ -3113,6 +3113,7 @@ ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator }); it('does not return a port if select-serial-port event is not defined', async () => { + // Take screenshot to verify the test is running w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); const port = await getPorts(); expect(port).to.equal(notFoundError); @@ -3629,7 +3630,7 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se }); }); -ifdescribe(process.platform !== 'darwin' || process.arch !== 'arm64')('navigator.bluetooth', () => { +describe('navigator.bluetooth', () => { let w: BrowserWindow; before(async () => { w = new BrowserWindow({ From 1072c4830fd0b7d0b1dba242e18b9919680ae8c3 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 11:27:21 -0400 Subject: [PATCH 334/339] fix: allow importing from electron/utility at runtime (#47987) Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: David Sanders --- lib/common/init.ts | 11 +++++++---- lib/utility/api/net.ts | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/common/init.ts b/lib/common/init.ts index d6ad120eb931d..a8d266d7f3349 100644 --- a/lib/common/init.ts +++ b/lib/common/init.ts @@ -93,20 +93,23 @@ makeElectronModule('electron'); makeElectronModule('electron/common'); if (process.type === 'browser') { makeElectronModule('electron/main'); -} -if (process.type === 'renderer') { +} else if (process.type === 'renderer') { makeElectronModule('electron/renderer'); +} else if (process.type === 'utility') { + makeElectronModule('electron/utility'); } const originalResolveFilename = Module._resolveFilename; -// 'electron/main', 'electron/renderer' and 'electron/common' are module aliases +// 'electron/{common,main,renderer,utility}' are module aliases // of the 'electron' module for TypeScript purposes, i.e., the types for // 'electron/main' consist of only main process modules, etc. It is intentional // that these can be `require()`-ed from both the main process as well as the // renderer process regardless of the names, they're superficial for TypeScript // only. -const electronModuleNames = new Set(['electron', 'electron/main', 'electron/renderer', 'electron/common']); +const electronModuleNames = new Set([ + 'electron', 'electron/main', 'electron/renderer', 'electron/common', 'electron/utility' +]); Module._resolveFilename = function (request, parent, isMain, options) { if (electronModuleNames.has(request)) { return 'electron'; diff --git a/lib/utility/api/net.ts b/lib/utility/api/net.ts index 70228ee0e5f46..8ef93d0e282ff 100644 --- a/lib/utility/api/net.ts +++ b/lib/utility/api/net.ts @@ -1,8 +1,7 @@ import { fetchWithSession } from '@electron/internal/browser/api/net-fetch'; import { ClientRequest } from '@electron/internal/common/api/net-client-request'; -import { IncomingMessage } from 'electron/utility'; -import type { ClientRequestConstructorOptions } from 'electron/utility'; +import type { ClientRequestConstructorOptions, IncomingMessage } from 'electron/utility'; const { isOnline, resolveHost } = process._linkedBinding('electron_common_net'); From ec2c6a4498e974fcdd8c9c3f381663b617583d7b Mon Sep 17 00:00:00 2001 From: "electron-roller[bot]" <84116207+electron-roller[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:35:26 -0400 Subject: [PATCH 335/339] chore: bump node to v22.18.0 (36-x-y) (#47934) * chore: bump node to v22.18.0 Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr * chore: fixup files --------- Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- DEPS | 2 +- lib/node/asar-fs-wrapper.ts | 18 ++--- patches/node/.patches | 3 - patches/node/build_add_gn_build_files.patch | 24 +++---- ...w_unbundling_of_node_js_dependencies.patch | 4 +- ...etraits_signatures_to_avoid_conflict.patch | 4 +- .../build_compile_with_c_20_support.patch | 6 +- patches/node/build_enable_perfetto.patch | 6 +- ...compilation_fails_if_not_using_a_new.patch | 8 +-- ...f_original-fs_and_custom_embedder_js.patch | 6 +- ...o_use_custom_inspector_protocol_path.patch | 60 ----------------- ...e_clang_as_default_compiler_on_macos.patch | 2 +- ...de_entrypoint_to_be_a_builtin_module.patch | 2 +- ...deprecation_ftbfs_in_simdjson_header.patch | 8 +-- ...e_expose_importmoduledynamically_and.patch | 18 ++--- ...move_protocol_maybe_from_node_string.patch | 33 ---------- ...cli_move_--trace-atomics-wait_to_eol.patch | 50 ++++++++++++-- .../node/cli_remove_deprecated_v8_flag.patch | 8 +-- ...enable_crashpad_linux_node_processes.patch | 13 ++-- ...matched-new-delete_in_debug_utils_cc.patch | 31 --------- ..._values_for_variables_in_common_gypi.patch | 2 +- ...g_fileexists_fn_to_legacymainresolve.patch | 14 ++-- ...ssert_module_in_the_renderer_process.patch | 10 +-- .../fix_crypto_tests_to_run_with_bssl.patch | 10 +-- ..._do_not_resolve_electron_entrypoints.patch | 4 +- ...se_readfilesync_override_for_modules.patch | 4 +- ...n_electron_module_via_the_esm_loader.patch | 34 +++++----- ...ingssl_and_openssl_incompatibilities.patch | 27 ++------ ...in_esm_loaders_to_apply_asar_patches.patch | 24 ++----- .../fix_remove_fastapitypedarray_usage.patch | 66 ++----------------- .../pass_all_globals_through_require.patch | 4 +- ...ch_cppgc_heap_on_v8_isolate_creation.patch | 4 +- ..._on_wrapper-descriptor-based_cppheap.patch | 2 +- ...ted_fields_of_fastapicallbackoptions.patch | 28 ++------ .../node/support_v8_sandboxed_pointers.patch | 2 +- ...st_formally_mark_some_tests_as_flaky.patch | 4 +- script/node-disabled-tests.json | 1 + 37 files changed, 182 insertions(+), 364 deletions(-) delete mode 100644 patches/node/build_option_to_use_custom_inspector_protocol_path.patch delete mode 100644 patches/node/chore_remove_protocol_maybe_from_node_string.patch delete mode 100644 patches/node/fix_-wmismatched-new-delete_in_debug_utils_cc.patch diff --git a/DEPS b/DEPS index 404c579aa3b8b..dc1d62f7dd5d0 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,7 @@ vars = { 'chromium_version': '136.0.7103.177', 'node_version': - 'v22.17.1', + 'v22.18.0', 'nan_version': 'e14bdcd1f72d62bca1d541b66da43130384ec213', 'squirrel.mac_version': diff --git a/lib/node/asar-fs-wrapper.ts b/lib/node/asar-fs-wrapper.ts index 90d256342a060..2457fb0f2dd1f 100644 --- a/lib/node/asar-fs-wrapper.ts +++ b/lib/node/asar-fs-wrapper.ts @@ -742,7 +742,7 @@ export const wrapFsWithAsar = (fs: Record) => { } const dirent = getDirent(currentPath, result[0][i], type); - const stat = internalBinding('fs').internalModuleStat(binding, resultPath); + const stat = internalBinding('fs').internalModuleStat(resultPath); context.readdirResults.push(dirent); if (dirent.isDirectory() || stat === 1) { @@ -755,7 +755,7 @@ export const wrapFsWithAsar = (fs: Record) => { for (let i = 0; i < result.length; i++) { const resultPath = path.join(currentPath, result[i]); const relativeResultPath = path.relative(context.basePath, resultPath); - const stat = internalBinding('fs').internalModuleStat(binding, resultPath); + const stat = internalBinding('fs').internalModuleStat(resultPath); context.readdirResults.push(relativeResultPath); if (stat === 1) { @@ -825,7 +825,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (context.withFileTypes) { readdirResult = [ [...readdirResult], readdirResult.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(pathArg, p)); + return internalBinding('fs').internalModuleStat(path.join(pathArg, p)); }) ]; } @@ -1010,9 +1010,9 @@ export const wrapFsWithAsar = (fs: Record) => { }); const { internalModuleStat } = binding; - internalBinding('fs').internalModuleStat = (receiver: unknown, pathArgument: string) => { + internalBinding('fs').internalModuleStat = (pathArgument: string) => { const pathInfo = splitPath(pathArgument); - if (!pathInfo.isAsar) return internalModuleStat(receiver, pathArgument); + if (!pathInfo.isAsar) return internalModuleStat(pathArgument); const { asarPath, filePath } = pathInfo; // -ENOENT @@ -1047,7 +1047,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (withFileTypes) { initialItem = [ [...initialItem], initialItem.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(originalPath, p)); + return internalBinding('fs').internalModuleStat(path.join(originalPath, p)); }) ]; } @@ -1080,7 +1080,7 @@ export const wrapFsWithAsar = (fs: Record) => { readdirResult = [ [...files], files.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(direntPath, p)); + return internalBinding('fs').internalModuleStat(path.join(direntPath, p)); }) ]; } else { @@ -1101,7 +1101,7 @@ export const wrapFsWithAsar = (fs: Record) => { const { 0: pathArg, 1: readDir } = queue.pop(); for (const ent of readDir) { const direntPath = path.join(pathArg, ent); - const stat = internalBinding('fs').internalModuleStat(binding, direntPath); + const stat = internalBinding('fs').internalModuleStat(direntPath); result.push(path.relative(originalPath, direntPath)); if (stat === 1) { @@ -1155,7 +1155,7 @@ export const wrapFsWithAsar = (fs: Record) => { if (context.withFileTypes) { readdirResult = [ [...readdirResult], readdirResult.map((p: string) => { - return internalBinding('fs').internalModuleStat(binding, path.join(pathArg, p)); + return internalBinding('fs').internalModuleStat(path.join(pathArg, p)); }) ]; } diff --git a/patches/node/.patches b/patches/node/.patches index ecbe186e500f9..17e363ed7928b 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -37,7 +37,6 @@ test_use_static_method_names_in_call_stacks.patch fix_remove_fastapitypedarray_usage.patch test_handle_explicit_resource_management_globals.patch build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch -build_option_to_use_custom_inspector_protocol_path.patch fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch chore_add_createexternalizabletwobytestring_to_globals.patch refactor_attach_cppgc_heap_on_v8_isolate_creation.patch @@ -46,5 +45,3 @@ cli_move_--trace-atomics-wait_to_eol.patch fix_cppgc_initializing_twice.patch fix_task_starvation_in_inspector_context_test.patch fix_expose_readfilesync_override_for_modules.patch -chore_remove_protocol_maybe_from_node_string.patch -fix_-wmismatched-new-delete_in_debug_utils_cc.patch diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 86ea0937ab059..5e0111ef8ca4d 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -11,7 +11,7 @@ really in 20/21. We have to wait until 22 is released to be able to build with upstream GN files. diff --git a/configure.py b/configure.py -index 4560bac7b8e3c707ecea5a425f642efb9de9ed36..e9c2a4391f4058a21a259cacaac4fde5d199288e 100755 +index 2415940835036226799a7ea14c6687cc0d56c523..0feb07afbccad97a92cee00954443407eb20ac67 100755 --- a/configure.py +++ b/configure.py @@ -1722,7 +1722,7 @@ def configure_v8(o, configs): @@ -24,7 +24,7 @@ index 4560bac7b8e3c707ecea5a425f642efb9de9ed36..e9c2a4391f4058a21a259cacaac4fde5 o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 diff --git a/node.gni b/node.gni -index 35ccd0487f20cece033d58827ecb7ed016908ee4..62cd49c6a87074912a1cb6792576c8d4f239b669 100644 +index b049f0692980c3e26771c3209c3bdd2e9a4d637b..e2407027ab05e59b2f0f1c213b98ea469db7a91b 100644 --- a/node.gni +++ b/node.gni @@ -5,10 +5,10 @@ @@ -40,7 +40,7 @@ index 35ccd0487f20cece033d58827ecb7ed016908ee4..62cd49c6a87074912a1cb6792576c8d4 # The location of OpenSSL - use the one from node's deps by default. node_openssl_path = "$node_path/deps/openssl" -@@ -39,12 +39,15 @@ declare_args() { +@@ -42,12 +42,15 @@ declare_args() { # The variable is called "openssl" for parity with node's GYP build. node_use_openssl = true @@ -57,7 +57,7 @@ index 35ccd0487f20cece033d58827ecb7ed016908ee4..62cd49c6a87074912a1cb6792576c8d4 # Custom build tag. node_tag = "" -@@ -64,10 +67,16 @@ declare_args() { +@@ -67,10 +70,16 @@ declare_args() { # TODO(zcbenz): There are few broken things for now: # 1. cross-os compilation is not supported. # 2. node_mksnapshot crashes when cross-compiling for x64 from arm64. @@ -76,10 +76,10 @@ index 35ccd0487f20cece033d58827ecb7ed016908ee4..62cd49c6a87074912a1cb6792576c8d4 assert(!node_enable_inspector || node_use_openssl, diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index 092341dbfbabe15b15ed43057d399f754505f6fd..f14b45850e42585f5686b7201e2b8281ed8c24e1 100644 +index abf1583cdac9f139056cf4809f14e28e62f6d24c..8b104e175ccf8de90c138337f83f8f6ce1348ac7 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc -@@ -788,6 +788,7 @@ void BuiltinLoader::RegisterExternalReferences( +@@ -789,6 +789,7 @@ void BuiltinLoader::RegisterExternalReferences( registry->Register(GetNatives); RegisterExternalReferencesForInternalizedBuiltinCode(registry); @@ -279,7 +279,7 @@ index 856878c33681a73d41016729dabe48b0a6a80589..91a11852d206b65485fe90fd037a0bd1 if sys.platform == 'win32': files = [ x.replace('\\', '/') for x in files ] diff --git a/unofficial.gni b/unofficial.gni -index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3c1c8bd1b 100644 +index da565473f1ae96b4d009935f7733e6ab15ea9de2..26ebc811272ef2990f8d090c54e7f5294aab9d37 100644 --- a/unofficial.gni +++ b/unofficial.gni @@ -22,6 +22,11 @@ template("node_gn_build") { @@ -354,8 +354,8 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3 + } if (node_enable_inspector) { deps += [ - "src/inspector:crdtp", -@@ -214,6 +232,10 @@ template("node_gn_build") { + "$node_inspector_protocol_path:crdtp", +@@ -215,6 +233,10 @@ template("node_gn_build") { } } @@ -366,7 +366,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3 executable(target_name) { forward_variables_from(invoker, "*") -@@ -288,6 +310,7 @@ template("node_gn_build") { +@@ -289,6 +311,7 @@ template("node_gn_build") { } executable("node_js2c") { @@ -374,7 +374,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3 deps = [ "deps/uv", "$node_simdutf_path", -@@ -298,26 +321,75 @@ template("node_gn_build") { +@@ -299,26 +322,75 @@ template("node_gn_build") { "src/embedded_data.cc", "src/embedded_data.h", ] @@ -460,7 +460,7 @@ index 44641b92678ab2f28e6f5de75a92878f9f3d322d..a6cfd45b109c7b38fcf1529468ff64d3 outputs = [ "$target_gen_dir/node_javascript.cc" ] # Get the path to node_js2c executable of the host toolchain. -@@ -331,11 +403,11 @@ template("node_gn_build") { +@@ -332,11 +404,11 @@ template("node_gn_build") { get_label_info(":node_js2c($host_toolchain)", "name") + host_executable_suffix diff --git a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch index d93e9bef3bc21..48d4362fb6792 100644 --- a/patches/node/build_allow_unbundling_of_node_js_dependencies.patch +++ b/patches/node/build_allow_unbundling_of_node_js_dependencies.patch @@ -14,7 +14,7 @@ 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 a6cfd45b109c7b38fcf1529468ff64d3c1c8bd1b..332c9ee7262108ae9616e9bc8bd950a4940a858c 100644 +index 26ebc811272ef2990f8d090c54e7f5294aab9d37..8886f2a79ae77614789d6ae0defd4f18fc756456 100644 --- a/unofficial.gni +++ b/unofficial.gni @@ -160,7 +160,6 @@ template("node_gn_build") { @@ -44,7 +44,7 @@ index a6cfd45b109c7b38fcf1529468ff64d3c1c8bd1b..332c9ee7262108ae9616e9bc8bd950a4 if (v8_enable_i18n_support) { deps += [ "//third_party/icu" ] } -@@ -230,6 +239,19 @@ template("node_gn_build") { +@@ -231,6 +240,19 @@ template("node_gn_build") { sources += node_inspector.node_inspector_sources + node_inspector.node_inspector_generated_sources } diff --git a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch index 3054af71962d5..7f669c5b0bd7d 100644 --- a/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch +++ b/patches/node/build_change_crdtp_protocoltypetraits_signatures_to_avoid_conflict.patch @@ -38,10 +38,10 @@ index 8521730bd03cdfce47e9b5d0f5d68a568bc3de8c..28f4598aa7ea0e93350f79566c06d0f0 } diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index 94ec9b2301998c4c5aad9ca3dae72ecf323fa0bb..a0d19a592d7bf9b00d6b98ef1ae931626ebb945c 100644 +index bcdedaa2ae4ab1d3267d7a1347f15e0405261277..ddedca4a5b9b35258050f8b4cb446ceeba956896 100644 --- a/src/inspector/node_string.h +++ b/src/inspector/node_string.h -@@ -19,8 +19,8 @@ namespace crdtp { +@@ -18,8 +18,8 @@ namespace crdtp { template <> struct ProtocolTypeTraits { diff --git a/patches/node/build_compile_with_c_20_support.patch b/patches/node/build_compile_with_c_20_support.patch index b16baaf70f9e5..52a25cc54cd5c 100644 --- a/patches/node/build_compile_with_c_20_support.patch +++ b/patches/node/build_compile_with_c_20_support.patch @@ -10,10 +10,10 @@ V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8 This can be removed when Electron upgrades to a version of Node.js containing the required V8 version. diff --git a/common.gypi b/common.gypi -index acfc02510ee1ce34a3f410a7a4ce53adb42abd35..b9264bfb1170928431848bb2b99e4f0dfbe8f95a 100644 +index 679633dc6b4ce2a1f5f88e93d1a1c1feb4bbadb4..2caa183213d5632be81b763e894e37c09384391f 100644 --- a/common.gypi +++ b/common.gypi -@@ -538,7 +538,7 @@ +@@ -539,7 +539,7 @@ '-fno-rtti', '-fno-exceptions', '-fno-strict-aliasing', @@ -22,7 +22,7 @@ index acfc02510ee1ce34a3f410a7a4ce53adb42abd35..b9264bfb1170928431848bb2b99e4f0d ], 'defines': [ '__STDC_FORMAT_MACROS' ], 'ldflags': [ '-rdynamic' ], -@@ -708,7 +708,7 @@ +@@ -709,7 +709,7 @@ ['clang==1', { 'xcode_settings': { 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0', diff --git a/patches/node/build_enable_perfetto.patch b/patches/node/build_enable_perfetto.patch index 1e2e8823fe0cb..1bb292dd46878 100644 --- a/patches/node/build_enable_perfetto.patch +++ b/patches/node/build_enable_perfetto.patch @@ -64,10 +64,10 @@ index 251f51ec454f9cba4023b8b6729241ee753aac13..1de8cac6e3953ce9cab9db03530da327 module.exports = { diff --git a/node.gyp b/node.gyp -index 0434887c363a586cbfa0438765fc8800d4237057..20fbf03cee24e66f9ad0d394dbcfa3ad03348890 100644 +index 442c1e7a6ddafbb7a7ec7a42a97ec04b28ea4d93..3a66c11d39dd2fd129c8f54098a9607e080ecca0 100644 --- a/node.gyp +++ b/node.gyp -@@ -175,7 +175,6 @@ +@@ -176,7 +176,6 @@ 'src/timers.cc', 'src/timer_wrap.cc', 'src/tracing/agent.cc', @@ -75,7 +75,7 @@ index 0434887c363a586cbfa0438765fc8800d4237057..20fbf03cee24e66f9ad0d394dbcfa3ad 'src/tracing/node_trace_writer.cc', 'src/tracing/trace_event.cc', 'src/tracing/traced_value.cc', -@@ -302,7 +301,6 @@ +@@ -304,7 +303,6 @@ 'src/tcp_wrap.h', 'src/timers.h', 'src/tracing/agent.h', diff --git a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch index 8118eaac97d4b..3f5e9b39ae14b 100644 --- a/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch +++ b/patches/node/build_ensure_native_module_compilation_fails_if_not_using_a_new.patch @@ -7,7 +7,7 @@ Subject: build: ensure native module compilation fails if not using a new This should not be upstreamed, it is a quality-of-life patch for downstream module builders. diff --git a/common.gypi b/common.gypi -index cf643bcd0bc9080b80bf12afeebc69f2db74bb54..acfc02510ee1ce34a3f410a7a4ce53adb42abd35 100644 +index 33af43cd768c24b26d523f3db66eb8b9eb26859a..679633dc6b4ce2a1f5f88e93d1a1c1feb4bbadb4 100644 --- a/common.gypi +++ b/common.gypi @@ -89,6 +89,8 @@ @@ -19,7 +19,7 @@ index cf643bcd0bc9080b80bf12afeebc69f2db74bb54..acfc02510ee1ce34a3f410a7a4ce53ad ##### end V8 defaults ##### # When building native modules using 'npm install' with the system npm, -@@ -297,7 +299,8 @@ +@@ -298,7 +300,8 @@ '_GLIBCXX_USE_CXX11_ABI=1', # This help forks when building Node.js on a 32-bit arch as # libuv is always compiled with _FILE_OFFSET_BITS=64 @@ -29,7 +29,7 @@ index cf643bcd0bc9080b80bf12afeebc69f2db74bb54..acfc02510ee1ce34a3f410a7a4ce53ad ], # Forcibly disable -Werror. We support a wide range of compilers, it's -@@ -454,6 +457,11 @@ +@@ -455,6 +458,11 @@ }], ], }], @@ -42,7 +42,7 @@ index cf643bcd0bc9080b80bf12afeebc69f2db74bb54..acfc02510ee1ce34a3f410a7a4ce53ad # list in v8/BUILD.gn. ['v8_enable_v8_checks == 1', { diff --git a/configure.py b/configure.py -index e9c2a4391f4058a21a259cacaac4fde5d199288e..7821a0d3a7179a9e7fa9e48a062c2b0e7705ca6f 100755 +index 0feb07afbccad97a92cee00954443407eb20ac67..5eccced7cf0212f229db68c76cc824a37e4a29bc 100755 --- a/configure.py +++ b/configure.py @@ -1704,6 +1704,7 @@ def configure_library(lib, output, pkgname=None): diff --git a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch index 025e06ca2388a..8b7051b7f036c 100644 --- a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch +++ b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch @@ -10,10 +10,10 @@ JS errors and ensures embedder JS is loaded via LoadEmbedderJavaScriptSource. That method is generated by our modifications to js2c.cc in the BUILD.gn patch diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js -index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b50ccca43 100644 +index 0244a214b187e67e0cb89f26cd019855963ec93a..b65a3be6bcb0e28f7f43367d0fa9da533db9d0d1 100644 --- a/lib/internal/fs/watchers.js +++ b/lib/internal/fs/watchers.js -@@ -292,12 +292,13 @@ function emitCloseNT(self) { +@@ -299,12 +299,13 @@ function emitCloseNT(self) { } // Legacy alias on the C++ wrapper object. This is not public API, so we may @@ -34,7 +34,7 @@ index 411eab8136d5957ae8a491bc38ffbdc88e59f5da..63c93b5be09692d0d4b6bfbb214b173b let kResistStopPropagation; diff --git a/src/node_builtins.cc b/src/node_builtins.cc -index f14b45850e42585f5686b7201e2b8281ed8c24e1..915b8cba6d512096e6090272ab3fbc63d5c61ce8 100644 +index 8b104e175ccf8de90c138337f83f8f6ce1348ac7..35cf42a5e533cb799bf129df0c8370bfe8310233 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -35,6 +35,7 @@ using v8::Value; diff --git a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch b/patches/node/build_option_to_use_custom_inspector_protocol_path.patch deleted file mode 100644 index 339cddb7a744e..0000000000000 --- a/patches/node/build_option_to_use_custom_inspector_protocol_path.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Mon, 17 Feb 2025 20:57:05 +0900 -Subject: build: option to use custom inspector_protocol path - -This allows building against //third_party/inspector_protocol -which would align us when building with chromium shared dependencies. - -The span changes will be auto-removed when Node.js bumps their -protocol deps to contain https://chromium-review.googlesource.com/c/v8/v8/+/5996636 - -Rest of the changes can be upstreamed. - -diff --git a/node.gni b/node.gni -index 165b26a79a7f2b74d2a2252dc2350b2e10c091e6..c64761b730e61edcdc0e46a48699f2fd5bb1c0a6 100644 ---- a/node.gni -+++ b/node.gni -@@ -16,6 +16,9 @@ declare_args() { - # The location of simdutf - use the one from node's deps by default. - node_simdutf_path = "//third_party/simdutf" - -+ # The location of inspector_protocol - use the one from node's deps by default. -+ node_inspector_protocol_path = "//third_party/inspector_protocol" -+ - # The NODE_MODULE_VERSION defined in node_version.h. - node_module_version = exec_script("$node_path/tools/getmoduleversion.py", [], "value") - -diff --git a/src/inspector/unofficial.gni b/src/inspector/unofficial.gni -index 3d7aa148678b2646b88fa7c32abec91791b02b82..4810d93eb971b253f7dadff7011a632f6dbe6a2b 100644 ---- a/src/inspector/unofficial.gni -+++ b/src/inspector/unofficial.gni -@@ -13,7 +13,7 @@ template("inspector_gn_build") { - } - - node_gen_dir = get_label_info("../..", "target_gen_dir") -- protocol_tool_path = "../../deps/inspector_protocol" -+ protocol_tool_path = "$node_inspector_protocol_path" - - gypi_values = exec_script( - "../../tools/gypi_to_gn.py", -diff --git a/unofficial.gni b/unofficial.gni -index 332c9ee7262108ae9616e9bc8bd950a4940a858c..8886f2a79ae77614789d6ae0defd4f18fc756456 100644 ---- a/unofficial.gni -+++ b/unofficial.gni -@@ -222,13 +222,14 @@ template("node_gn_build") { - } - if (node_enable_inspector) { - deps += [ -- "src/inspector:crdtp", -+ "$node_inspector_protocol_path:crdtp", - "src/inspector:node_protocol_generated_sources", - "src/inspector:v8_inspector_compress_protocol_json", - ] - include_dirs = [ - "$target_gen_dir/src", - "$target_gen_dir/src/inspector", -+ "$node_inspector_protocol_path", - ] - node_inspector = exec_script( - "./tools/gypi_to_gn.py", diff --git a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch index 7901c967e9195..88f7586d58c69 100644 --- a/patches/node/build_restore_clang_as_default_compiler_on_macos.patch +++ b/patches/node/build_restore_clang_as_default_compiler_on_macos.patch @@ -11,7 +11,7 @@ node-gyp will use the result of `process.config` that reflects the environment in which the binary got built. diff --git a/common.gypi b/common.gypi -index b9264bfb1170928431848bb2b99e4f0dfbe8f95a..836d96a1bd9c1d5568f0045bbddddca1edb1a0ce 100644 +index 2caa183213d5632be81b763e894e37c09384391f..2cce436c4a9e3d942f957f6c94a4ef9e3db391ce 100644 --- a/common.gypi +++ b/common.gypi @@ -128,6 +128,7 @@ diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch index ae864ce06db1d..a2901455347a2 100644 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch @@ -8,7 +8,7 @@ they use themselves as the entry point. We should try to upstream some form of this. diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js -index 4e7be0594ca1e1ceaf1963debbce46783893ed77..a6df0672bf6ae6e9a74ebbb0e4debff63599cc99 100644 +index 0cda54fd85e1e0bff13d4718a269eb3e7c60312a..6b165062a5eaa40f6e5614bca50bc33ccbdb85cc 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -245,12 +245,14 @@ function patchProcessObject(expandArgv1) { diff --git a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch index 0bde5d3fc9c81..b91313c8345e4 100644 --- a/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch +++ b/patches/node/chore_disable_deprecation_ftbfs_in_simdjson_header.patch @@ -11,10 +11,10 @@ Without this patch, building with simdjson fails with This patch can be removed once this is fixed upstream in simdjson. diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h -index a0d449975224a3e0db5c05de79b290763d6e390c..e77e47f972b4609e38aa8b68ab0d81ed1575effb 100644 +index 8f52a4331d59996786450eec982659da9244cac1..74729673d87b068dff5f24166bbb77d844f15f42 100644 --- a/deps/simdjson/simdjson.h +++ b/deps/simdjson/simdjson.h -@@ -3868,12 +3868,17 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result padded_string::load(std::string_view filen +@@ -4304,6 +4309,9 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson @@ -42,7 +42,7 @@ index a0d449975224a3e0db5c05de79b290763d6e390c..e77e47f972b4609e38aa8b68ab0d81ed inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } -@@ -4281,6 +4289,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len +@@ -4312,6 +4320,8 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len return simdjson::padded_string(reinterpret_cast(str), len); } #endif diff --git a/patches/node/chore_expose_importmoduledynamically_and.patch b/patches/node/chore_expose_importmoduledynamically_and.patch index 497ea5842d984..3f44ec26449ef 100644 --- a/patches/node/chore_expose_importmoduledynamically_and.patch +++ b/patches/node/chore_expose_importmoduledynamically_and.patch @@ -11,7 +11,7 @@ its own blended handler between Node and Blink. Not upstreamable. diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js -index 9d6f850f667c5186efe6855bc3d5f5af332bdaa7..8521759e20adf53024e5893dbf3cb36e1752085e 100644 +index 9b41db8b0714b7408f79cbd5b4c460d9bc08f239..35ecfb9bbaf2c8e7351e1c69da84c82a4a7cb049 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -30,7 +30,7 @@ const { @@ -23,7 +23,7 @@ index 9d6f850f667c5186efe6855bc3d5f5af332bdaa7..8521759e20adf53024e5893dbf3cb36e const { loadPreloadModules, initializeFrozenIntrinsics, -@@ -280,12 +280,13 @@ let _forceDefaultLoader = false; +@@ -281,12 +281,13 @@ let _forceDefaultLoader = false; * @param {boolean} [forceDefaultLoader=false] - A boolean indicating disabling custom loaders. */ function initializeESM(forceDefaultLoader = false) { @@ -40,10 +40,10 @@ index 9d6f850f667c5186efe6855bc3d5f5af332bdaa7..8521759e20adf53024e5893dbf3cb36e /** diff --git a/src/module_wrap.cc b/src/module_wrap.cc -index cdd0ba00eb0cafbc79b816017423f9021ca2979d..6916497f6feb14e482cf5080b57d639ae7292d20 100644 +index e317a84e55714af0a93719336d02ac26410ad724..e3880111172363feafb53b51deb08c93596cd4f4 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc -@@ -875,7 +875,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( +@@ -895,7 +895,7 @@ MaybeLocal ModuleWrap::ResolveModuleCallback( return module->module_.Get(isolate); } @@ -52,7 +52,7 @@ index cdd0ba00eb0cafbc79b816017423f9021ca2979d..6916497f6feb14e482cf5080b57d639a Local context, Local host_defined_options, Local resource_name, -@@ -947,12 +947,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( +@@ -967,12 +967,13 @@ void ModuleWrap::SetImportModuleDynamicallyCallback( Realm* realm = Realm::GetCurrent(args); HandleScope handle_scope(isolate); @@ -68,7 +68,7 @@ index cdd0ba00eb0cafbc79b816017423f9021ca2979d..6916497f6feb14e482cf5080b57d639a } void ModuleWrap::HostInitializeImportMetaObjectCallback( -@@ -994,13 +995,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( +@@ -1014,13 +1015,14 @@ void ModuleWrap::SetInitializeImportMetaObjectCallback( Realm* realm = Realm::GetCurrent(args); Isolate* isolate = realm->isolate(); @@ -87,7 +87,7 @@ index cdd0ba00eb0cafbc79b816017423f9021ca2979d..6916497f6feb14e482cf5080b57d639a MaybeLocal ModuleWrap::SyntheticModuleEvaluationStepsCallback( diff --git a/src/module_wrap.h b/src/module_wrap.h -index ef4dfd1d6b091d2b0f71b946904a47415b6435ba..862f946a75f2a2949d7eeb7f97e96289beab8078 100644 +index 9363ce73e51cde3d3a94f9912f072d532d0f8560..c0e972ed293157726efc5fa76dfa62d3da51c22a 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -8,6 +8,7 @@ @@ -114,7 +114,7 @@ index ef4dfd1d6b091d2b0f71b946904a47415b6435ba..862f946a75f2a2949d7eeb7f97e96289 public: enum InternalFields { kModuleSlot = BaseObject::kInternalFieldCount, -@@ -91,6 +99,8 @@ class ModuleWrap : public BaseObject { +@@ -92,6 +100,8 @@ class ModuleWrap : public BaseObject { static void CreateRequiredModuleFacade( const v8::FunctionCallbackInfo& args); @@ -123,7 +123,7 @@ index ef4dfd1d6b091d2b0f71b946904a47415b6435ba..862f946a75f2a2949d7eeb7f97e96289 private: ModuleWrap(Realm* realm, v8::Local object, -@@ -130,7 +140,6 @@ class ModuleWrap : public BaseObject { +@@ -131,7 +141,6 @@ class ModuleWrap : public BaseObject { v8::Local specifier, v8::Local import_attributes, v8::Local referrer); diff --git a/patches/node/chore_remove_protocol_maybe_from_node_string.patch b/patches/node/chore_remove_protocol_maybe_from_node_string.patch deleted file mode 100644 index 12352945185c9..0000000000000 --- a/patches/node/chore_remove_protocol_maybe_from_node_string.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Thu, 26 Jun 2025 09:20:43 +0000 -Subject: chore: remove protocol::Maybe from node_string - -It was removed upstream in https://chromium-review.googlesource.com/c/chromium/src/+/6049967. - -This should be upstreamed. - -diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h -index a0d19a592d7bf9b00d6b98ef1ae931626ebb945c..ddedca4a5b9b35258050f8b4cb446ceeba956896 100644 ---- a/src/inspector/node_string.h -+++ b/src/inspector/node_string.h -@@ -6,7 +6,6 @@ - #include - #include - #include --#include "crdtp/maybe.h" - #include "crdtp/protocol_core.h" - #include "util.h" - #include "v8-inspector.h" -@@ -31,11 +30,6 @@ struct ProtocolTypeTraits { - std::vector* bytes); - }; - --template <> --struct detail::MaybeTypedef { -- typedef ValueMaybe type; --}; -- - } // namespace crdtp - - namespace node { diff --git a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch index 681ea40add6e0..dae7ed917b98d 100644 --- a/patches/node/cli_move_--trace-atomics-wait_to_eol.patch +++ b/patches/node/cli_move_--trace-atomics-wait_to_eol.patch @@ -15,10 +15,50 @@ Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli diff --git a/doc/api/cli.md b/doc/api/cli.md -index d924287df3ca29681cf71e2fbd402314ce8edd97..f2f4d25a838b9758234cd667b0fb537d0d0fcced 100644 +index 404e87e6d1237b5ee79cafd8a959c1b6d9d23fe5..7deda572c940f7b2e8c6813f1826796a13e4db38 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md -@@ -3386,7 +3386,6 @@ one is included in the list below. +@@ -2709,39 +2709,6 @@ added: v12.0.0 + Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.3'. Use to disable support + for TLSv1.2, which is not as secure as TLSv1.3. + +-### `--trace-atomics-wait` +- +- +- +-> Stability: 0 - Deprecated +- +-Print short summaries of calls to [`Atomics.wait()`][] to stderr. +-The output could look like this: +- +-```text +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the values mismatched +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) started +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) started +-(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread +-(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread +-``` +- +-The fields here correspond to: +- +-* The thread id as given by [`worker_threads.threadId`][] +-* The base address of the `SharedArrayBuffer` in question, as well as the +- byte offset corresponding to the index passed to `Atomics.wait()` +-* The expected value that was passed to `Atomics.wait()` +-* The timeout passed to `Atomics.wait` +- + ### `--trace-deprecation` + +

gkv;zsug*VvHZGS5!EW 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