mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
5 Commits
main
...
robo/blink
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc24fde4bf | ||
|
|
2e78fd640f | ||
|
|
22739efc7d | ||
|
|
676ce61e76 | ||
|
|
676605bdf3 |
19
BUILD.gn
19
BUILD.gn
@@ -147,6 +147,25 @@ config("branding") {
|
||||
|
||||
config("electron_lib_config") {
|
||||
include_dirs = [ "." ]
|
||||
cflags = []
|
||||
if (is_clang && clang_use_chrome_plugins) {
|
||||
# The plugin is built directly into clang, so there's no need to load it
|
||||
# dynamically.
|
||||
cflags += [
|
||||
"-Xclang",
|
||||
"-add-plugin",
|
||||
"-Xclang",
|
||||
"blink-gc-plugin",
|
||||
"-Xclang",
|
||||
"-plugin-arg-blink-gc-plugin",
|
||||
"-Xclang",
|
||||
"check-directory=electron/shell/",
|
||||
"-Xclang",
|
||||
"-plugin-arg-blink-gc-plugin",
|
||||
"-Xclang",
|
||||
"check-directory=gin/",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# We generate the definitions twice here, once in //electron/electron.d.ts
|
||||
|
||||
@@ -151,3 +151,4 @@ fix_pulseaudio_stream_and_icon_names.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
gin_mark_argumentholder_as_cppgc_stack_allocated.patch
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Tue, 7 Apr 2026 02:34:49 +0900
|
||||
Subject: [gin] Mark ArgumentHolder as CPPGC_STACK_ALLOCATED
|
||||
|
||||
ArgumentHolder is always stack-allocated from Invoker.
|
||||
When ArgType is a pointer to a cppgc-managed type, the blink-gc plugin
|
||||
reports an error for the raw pointer `value` field:
|
||||
|
||||
[blink-gc] Class 'ArgumentHolder<0, T*>' contains invalid fields.
|
||||
[blink-gc] Raw pointer field 'value' to a GC managed class.
|
||||
|
||||
Disallow any heap allocations and make the lifetime explicit.
|
||||
|
||||
Backports https://chromium-review.googlesource.com/c/chromium/src/+/7728865
|
||||
|
||||
diff --git a/gin/function_template.h b/gin/function_template.h
|
||||
index 873015289db9709c00c32080e5387d9cdfea1f69..bc84040952b79f02df783833005cc9583f3378a5 100644
|
||||
--- a/gin/function_template.h
|
||||
+++ b/gin/function_template.h
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "gin/gin_export.h"
|
||||
#include "gin/per_isolate_data.h"
|
||||
#include "gin/public/gin_embedders.h"
|
||||
+#include "v8/include/cppgc/macros.h"
|
||||
#include "v8/include/v8-external.h"
|
||||
#include "v8/include/v8-forward.h"
|
||||
#include "v8/include/v8-persistent-handle.h"
|
||||
@@ -162,6 +163,9 @@ GIN_EXPORT void ThrowConversionError(Arguments* args,
|
||||
// at position |index|.
|
||||
template <size_t index, typename ArgType, typename = void>
|
||||
struct ArgumentHolder {
|
||||
+ CPPGC_STACK_ALLOCATED();
|
||||
+
|
||||
+ public:
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
@@ -184,6 +188,9 @@ template <size_t index, typename ArgType>
|
||||
std::is_constructible_v<typename CallbackParamTraits<ArgType>::LocalType,
|
||||
v8::Isolate*>)
|
||||
struct ArgumentHolder<index, ArgType> {
|
||||
+ CPPGC_STACK_ALLOCATED();
|
||||
+
|
||||
+ public:
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
@@ -76,6 +76,7 @@
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
#include "shell/common/language_util.h"
|
||||
|
||||
@@ -732,10 +732,10 @@ bool BaseWindow::IsFocusable() const {
|
||||
|
||||
void BaseWindow::SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
auto context = isolate->GetCurrentContext();
|
||||
gin_helper::Handle<Menu> menu;
|
||||
Menu* menu = nullptr;
|
||||
v8::Local<v8::Object> object;
|
||||
if (value->IsObject() && value->ToObject(context).ToLocal(&object) &&
|
||||
gin::ConvertFromV8(isolate, value, &menu) && !menu.IsEmpty()) {
|
||||
gin::ConvertFromV8(isolate, value, &menu) && menu) {
|
||||
// We only want to update the menu if the menu has a non-zero item count,
|
||||
// or we risk crashes.
|
||||
if (menu->model()->GetItemCount() == 0) {
|
||||
@@ -744,7 +744,7 @@ void BaseWindow::SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
window_->SetMenu(menu->model());
|
||||
}
|
||||
|
||||
menu_.Reset(isolate, menu.ToV8());
|
||||
menu_ = menu;
|
||||
} else if (value->IsNull()) {
|
||||
RemoveMenu();
|
||||
} else {
|
||||
@@ -754,7 +754,7 @@ void BaseWindow::SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||
}
|
||||
|
||||
void BaseWindow::RemoveMenu() {
|
||||
menu_.Reset();
|
||||
menu_.Clear();
|
||||
window_->SetMenu(nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "shell/browser/native_window_observer.h"
|
||||
#include "shell/common/api/electron_api_native_image.h"
|
||||
#include "shell/common/gin_helper/trackable_object.h"
|
||||
#include "v8/include/cppgc/persistent.h"
|
||||
|
||||
namespace gin {
|
||||
class Arguments;
|
||||
@@ -34,6 +35,7 @@ class NativeWindow;
|
||||
|
||||
namespace api {
|
||||
|
||||
class Menu;
|
||||
class View;
|
||||
|
||||
class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
@@ -284,7 +286,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
#endif
|
||||
|
||||
v8::Global<v8::Value> content_view_;
|
||||
v8::Global<v8::Value> menu_;
|
||||
cppgc::Persistent<Menu> menu_;
|
||||
v8::Global<v8::Value> parent_window_;
|
||||
|
||||
std::unique_ptr<NativeWindow> window_;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/cpp/data_element.h"
|
||||
#include "services/network/public/mojom/data_pipe_getter.mojom.h"
|
||||
#include "shell/common/gc_plugin.h"
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
@@ -45,6 +46,8 @@ class DataPipeHolder final : public gin::Wrappable<DataPipeHolder> {
|
||||
|
||||
private:
|
||||
std::string id_;
|
||||
GC_PLUGIN_IGNORE(
|
||||
"Context tracking of remote is not needed in the browser process.")
|
||||
mojo::Remote<network::mojom::DataPipeGetter> data_pipe_;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "ui/base/models/image_model.h"
|
||||
#include "v8/include/cppgc/persistent.h"
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
|
||||
@@ -71,6 +72,11 @@ Menu::~Menu() {
|
||||
RemoveModelObserver();
|
||||
}
|
||||
|
||||
void Menu::Trace(cppgc::Visitor* visitor) const {
|
||||
gin::Wrappable<Menu>::Trace(visitor);
|
||||
visitor->Trace(parent_);
|
||||
}
|
||||
|
||||
void Menu::RemoveModelObserver() {
|
||||
if (model_) {
|
||||
model_->RemoveObserver(this);
|
||||
@@ -189,20 +195,11 @@ void Menu::OnMenuWillShow(ui::SimpleMenuModel* source) {
|
||||
}
|
||||
|
||||
base::OnceClosure Menu::BindSelfToClosure(base::OnceClosure callback) {
|
||||
// return ((callback, ref) => { callback() }).bind(null, callback, this)
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Object> self;
|
||||
if (GetWrapper(isolate).ToLocal(&self)) {
|
||||
v8::Global<v8::Value> ref(isolate, self);
|
||||
return base::BindOnce(
|
||||
[](base::OnceClosure callback, v8::Global<v8::Value> ref) {
|
||||
std::move(callback).Run();
|
||||
},
|
||||
std::move(callback), std::move(ref));
|
||||
} else {
|
||||
return base::DoNothing();
|
||||
}
|
||||
return base::BindOnce(
|
||||
[](base::OnceClosure callback, cppgc::Persistent<Menu> prevent_gc) {
|
||||
std::move(callback).Run();
|
||||
},
|
||||
std::move(callback), cppgc::Persistent<Menu>(this));
|
||||
}
|
||||
|
||||
void Menu::InsertItemAt(int index,
|
||||
@@ -271,12 +268,10 @@ std::u16string Menu::GetAcceleratorTextAtForTesting(int index) const {
|
||||
}
|
||||
|
||||
void Menu::OnMenuWillClose() {
|
||||
keep_alive_.Clear();
|
||||
Emit("menu-will-close");
|
||||
}
|
||||
|
||||
void Menu::OnMenuWillShow() {
|
||||
keep_alive_ = this;
|
||||
Emit("menu-will-show");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/browser/ui/electron_menu_model.h"
|
||||
#include "shell/common/gin_helper/constructible.h"
|
||||
#include "shell/common/gin_helper/self_keep_alive.h"
|
||||
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
|
||||
#include "v8/include/cppgc/member.h"
|
||||
|
||||
namespace gin {
|
||||
class Arguments;
|
||||
@@ -45,6 +44,7 @@ class Menu : public gin::Wrappable<Menu>,
|
||||
static const gin::WrapperInfo kWrapperInfo;
|
||||
const gin::WrapperInfo* wrapper_info() const override;
|
||||
const char* GetHumanReadableName() const override;
|
||||
void Trace(cppgc::Visitor*) const override;
|
||||
|
||||
// gin_helper::Constructible
|
||||
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>);
|
||||
@@ -102,9 +102,6 @@ class Menu : public gin::Wrappable<Menu>,
|
||||
virtual void ClosePopupAt(int32_t window_id) = 0;
|
||||
virtual std::u16string GetAcceleratorTextAtForTesting(int index) const;
|
||||
|
||||
std::unique_ptr<ElectronMenuModel> model_;
|
||||
raw_ptr<Menu> parent_ = nullptr;
|
||||
|
||||
// Observable:
|
||||
void OnMenuWillClose() override;
|
||||
void OnMenuWillShow() override;
|
||||
@@ -132,7 +129,8 @@ class Menu : public gin::Wrappable<Menu>,
|
||||
int GetIndexOfCommandId(int command_id) const;
|
||||
int GetItemCount() const;
|
||||
|
||||
gin_helper::SelfKeepAlive<Menu> keep_alive_{nullptr};
|
||||
std::unique_ptr<ElectronMenuModel> model_;
|
||||
cppgc::Member<Menu> parent_;
|
||||
};
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "gin/weak_cell.h"
|
||||
#import "shell/browser/ui/cocoa/electron_menu_controller.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -23,6 +24,8 @@ class MenuMac : public Menu {
|
||||
explicit MenuMac(gin::Arguments* args);
|
||||
~MenuMac() override;
|
||||
|
||||
void Trace(cppgc::Visitor*) const override;
|
||||
|
||||
protected:
|
||||
// Menu
|
||||
void PopupAt(BaseWindow* window,
|
||||
@@ -53,7 +56,7 @@ class MenuMac : public Menu {
|
||||
// window ID -> open context menu
|
||||
std::map<int32_t, ElectronMenuController*> popup_controllers_;
|
||||
|
||||
base::WeakPtrFactory<MenuMac> weak_factory_{this};
|
||||
gin::WeakCellFactory<MenuMac> weak_cell_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#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 "gin/persistent.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"
|
||||
@@ -57,6 +58,11 @@ MenuMac::~MenuMac() {
|
||||
RemoveModelObserver();
|
||||
}
|
||||
|
||||
void MenuMac::Trace(cppgc::Visitor* visitor) const {
|
||||
Menu::Trace(visitor);
|
||||
visitor->Trace(weak_cell_factory_);
|
||||
}
|
||||
|
||||
void MenuMac::PopupAt(BaseWindow* window,
|
||||
std::optional<WebFrameMain*> frame,
|
||||
int x,
|
||||
@@ -77,10 +83,13 @@ void MenuMac::PopupAt(BaseWindow* window,
|
||||
// has run.
|
||||
base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback));
|
||||
|
||||
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));
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
auto popup = base::BindOnce(
|
||||
&MenuMac::PopupOnUI,
|
||||
gin::WrapPersistent(weak_cell_factory_.GetWeakCell(
|
||||
isolate->GetCppHeap()->GetAllocationHandle())),
|
||||
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));
|
||||
}
|
||||
@@ -121,9 +130,12 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||
return;
|
||||
NSWindow* nswindow = native_window->GetNativeWindow().GetNativeNSWindow();
|
||||
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
base::OnceClosure close_callback =
|
||||
base::BindOnce(&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id,
|
||||
std::move(callback));
|
||||
base::BindOnce(&MenuMac::OnClosed,
|
||||
gin::WrapPersistent(weak_cell_factory_.GetWeakCell(
|
||||
isolate->GetCppHeap()->GetAllocationHandle())),
|
||||
window_id, std::move(callback));
|
||||
popup_controllers_[window_id] =
|
||||
[[ElectronMenuController alloc] initWithModel:model()
|
||||
useDefaultAccelerator:NO];
|
||||
@@ -204,8 +216,12 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||
}
|
||||
|
||||
void MenuMac::ClosePopupAt(int32_t window_id) {
|
||||
auto close_popup = base::BindOnce(&MenuMac::ClosePopupOnUI,
|
||||
weak_factory_.GetWeakPtr(), window_id);
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
auto close_popup =
|
||||
base::BindOnce(&MenuMac::ClosePopupOnUI,
|
||||
gin::WrapPersistent(weak_cell_factory_.GetWeakCell(
|
||||
isolate->GetCppHeap()->GetAllocationHandle())),
|
||||
window_id);
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE, std::move(close_popup));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "gin/persistent.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_views.h"
|
||||
@@ -23,6 +24,11 @@ MenuViews::MenuViews(gin::Arguments* args) : Menu(args) {}
|
||||
|
||||
MenuViews::~MenuViews() = default;
|
||||
|
||||
void MenuViews::Trace(cppgc::Visitor* visitor) const {
|
||||
Menu::Trace(visitor);
|
||||
visitor->Trace(weak_cell_factory_);
|
||||
}
|
||||
|
||||
void MenuViews::PopupAt(BaseWindow* window,
|
||||
std::optional<WebFrameMain*> frame,
|
||||
int x,
|
||||
@@ -55,8 +61,11 @@ void MenuViews::PopupAt(BaseWindow* window,
|
||||
// callback, it is fine passing OnceCallback to it because we reset the
|
||||
// menu runner immediately when the menu is closed.
|
||||
int32_t window_id = window->weak_map_id();
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
auto close_callback = electron::AdaptCallbackForRepeating(
|
||||
base::BindOnce(&MenuViews::OnClosed, weak_factory_.GetWeakPtr(),
|
||||
base::BindOnce(&MenuViews::OnClosed,
|
||||
gin::WrapPersistent(weak_cell_factory_.GetWeakCell(
|
||||
isolate->GetCppHeap()->GetAllocationHandle())),
|
||||
window_id, std::move(callback_with_ref)));
|
||||
auto& runner = menu_runners_[window_id] =
|
||||
std::make_unique<MenuRunner>(model(), flags, std::move(close_callback));
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "gin/weak_cell.h"
|
||||
#include "shell/browser/api/electron_api_menu.h"
|
||||
#include "ui/views/controls/menu/menu_runner.h"
|
||||
|
||||
@@ -19,6 +19,8 @@ class MenuViews : public Menu {
|
||||
explicit MenuViews(gin::Arguments* args);
|
||||
~MenuViews() override;
|
||||
|
||||
void Trace(cppgc::Visitor*) const override;
|
||||
|
||||
protected:
|
||||
// Menu
|
||||
void PopupAt(BaseWindow* window,
|
||||
@@ -36,7 +38,7 @@ class MenuViews : public Menu {
|
||||
// window ID -> open context menu
|
||||
base::flat_map<int32_t, std::unique_ptr<views::MenuRunner>> menu_runners_;
|
||||
|
||||
base::WeakPtrFactory<MenuViews> weak_factory_{this};
|
||||
gin::WeakCellFactory<MenuViews> weak_cell_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "electron/electron_version.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "gin/persistent.h"
|
||||
#include "net/log/net_log_capture_mode.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/net/system_network_context_manager.h"
|
||||
@@ -147,8 +148,9 @@ v8::Local<v8::Promise> NetLog::StartLogging(base::FilePath log_path,
|
||||
file_task_runner_->PostTaskAndReplyWithResult(
|
||||
FROM_HERE, base::BindOnce(OpenFileForWriting, log_path),
|
||||
base::BindOnce(&NetLog::StartNetLogAfterCreateFile,
|
||||
weak_ptr_factory_.GetWeakPtr(), capture_mode,
|
||||
max_file_size, std::move(custom_constants)));
|
||||
gin::WrapPersistent(weak_factory_.GetWeakCell(
|
||||
args->isolate()->GetCppHeap()->GetAllocationHandle())),
|
||||
capture_mode, max_file_size, std::move(custom_constants)));
|
||||
|
||||
return handle;
|
||||
}
|
||||
@@ -236,6 +238,11 @@ const char* NetLog::GetHumanReadableName() const {
|
||||
return "Electron / NetLog";
|
||||
}
|
||||
|
||||
void NetLog::Trace(cppgc::Visitor* visitor) const {
|
||||
gin::Wrappable<NetLog>::Trace(visitor);
|
||||
visitor->Trace(weak_factory_);
|
||||
}
|
||||
|
||||
// static
|
||||
NetLog* NetLog::Create(v8::Isolate* isolate,
|
||||
ElectronBrowserContext* browser_context) {
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
#include <optional>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "gin/weak_cell.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "net/log/net_log_capture_mode.h"
|
||||
#include "services/network/public/mojom/net_log.mojom.h"
|
||||
#include "shell/common/gc_plugin.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
|
||||
namespace base {
|
||||
@@ -56,6 +57,7 @@ class NetLog final : public gin::Wrappable<NetLog> {
|
||||
v8::Isolate* isolate) override;
|
||||
const gin::WrapperInfo* wrapper_info() const override;
|
||||
const char* GetHumanReadableName() const override;
|
||||
void Trace(cppgc::Visitor*) const override;
|
||||
|
||||
v8::Local<v8::Promise> StartLogging(base::FilePath log_path,
|
||||
gin::Arguments* args);
|
||||
@@ -74,13 +76,15 @@ class NetLog final : public gin::Wrappable<NetLog> {
|
||||
private:
|
||||
raw_ptr<ElectronBrowserContext> browser_context_;
|
||||
|
||||
GC_PLUGIN_IGNORE(
|
||||
"Context tracking of remote is not needed in the browser process.")
|
||||
mojo::Remote<network::mojom::NetLogExporter> net_log_exporter_;
|
||||
|
||||
std::optional<gin_helper::Promise<void>> pending_start_promise_;
|
||||
|
||||
scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
base::WeakPtrFactory<NetLog> weak_ptr_factory_{this};
|
||||
gin::WeakCellFactory<NetLog> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "base/power_monitor/power_observer.h"
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/common/gin_helper/self_keep_alive.h"
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
#include "shell/browser/lib/power_observer_linux.h"
|
||||
@@ -89,13 +88,14 @@ class PowerMonitor final : public gin::Wrappable<PowerMonitor>,
|
||||
|
||||
// The window used for processing events.
|
||||
HWND window_;
|
||||
|
||||
// Handle returned by RegisterSuspendResumeNotification.
|
||||
HPOWERNOTIFY power_notify_handle_ = nullptr;
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
PowerObserverLinux power_observer_linux_{this};
|
||||
#endif
|
||||
|
||||
gin_helper::SelfKeepAlive<PowerMonitor> keep_alive_{this};
|
||||
};
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shell/common/gc_plugin.h"
|
||||
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MacLockMonitor : NSObject {
|
||||
@private
|
||||
GC_PLUGIN_IGNORE("ObjC class cannot participate in cppgc tracing")
|
||||
std::vector<electron::api::PowerMonitor*> emitters;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,14 +45,16 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
|
||||
|
||||
// For Windows 8 and later, a new "connected standby" mode has been added and
|
||||
// we must explicitly register for its notifications.
|
||||
RegisterSuspendResumeNotification(static_cast<HANDLE>(window_),
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
power_notify_handle_ = RegisterSuspendResumeNotification(
|
||||
static_cast<HANDLE>(window_), DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
}
|
||||
|
||||
void PowerMonitor::DestroyPlatformSpecificMonitors() {
|
||||
if (window_) {
|
||||
WTSUnRegisterSessionNotification(window_);
|
||||
UnregisterSuspendResumeNotification(static_cast<HANDLE>(window_));
|
||||
if (power_notify_handle_) {
|
||||
UnregisterSuspendResumeNotification(power_notify_handle_);
|
||||
}
|
||||
gfx::SetWindowUserData(window_, nullptr);
|
||||
DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "services/device/public/mojom/wake_lock_provider.mojom.h"
|
||||
#include "services/service_manager/public/cpp/connector.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "v8/include/cppgc/allocation.h"
|
||||
#include "v8/include/v8-cppgc.h"
|
||||
@@ -53,11 +52,9 @@ PowerSaveBlocker::PowerSaveBlocker(v8::Isolate* isolate)
|
||||
PowerSaveBlocker::~PowerSaveBlocker() = default;
|
||||
|
||||
// static
|
||||
gin_helper::Handle<PowerSaveBlocker> PowerSaveBlocker::Create(
|
||||
v8::Isolate* isolate) {
|
||||
return gin_helper::CreateHandle(
|
||||
isolate, cppgc::MakeGarbageCollected<PowerSaveBlocker>(
|
||||
isolate->GetCppHeap()->GetAllocationHandle(), isolate));
|
||||
PowerSaveBlocker* PowerSaveBlocker::Create(v8::Isolate* isolate) {
|
||||
return cppgc::MakeGarbageCollected<PowerSaveBlocker>(
|
||||
isolate->GetCppHeap()->GetAllocationHandle(), isolate);
|
||||
}
|
||||
|
||||
const gin::WrapperInfo* PowerSaveBlocker::wrapper_info() const {
|
||||
|
||||
@@ -9,21 +9,17 @@
|
||||
#include "gin/wrappable.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/device/public/mojom/wake_lock.mojom.h"
|
||||
#include "shell/common/gc_plugin.h"
|
||||
|
||||
namespace gin {
|
||||
class ObjectTemplateBuilder;
|
||||
} // namespace gin
|
||||
|
||||
namespace gin_helper {
|
||||
template <typename T>
|
||||
class Handle;
|
||||
} // namespace gin_helper
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
class PowerSaveBlocker final : public gin::Wrappable<PowerSaveBlocker> {
|
||||
public:
|
||||
static gin_helper::Handle<PowerSaveBlocker> Create(v8::Isolate* isolate);
|
||||
static PowerSaveBlocker* Create(v8::Isolate* isolate);
|
||||
|
||||
// gin::Wrappable
|
||||
static const gin::WrapperInfo kWrapperInfo;
|
||||
@@ -57,6 +53,8 @@ class PowerSaveBlocker final : public gin::Wrappable<PowerSaveBlocker> {
|
||||
// Map from id to the corresponding blocker type for each request.
|
||||
base::flat_map<int, device::mojom::WakeLockType> wake_lock_types_;
|
||||
|
||||
GC_PLUGIN_IGNORE(
|
||||
"Context tracking of remote is not needed in the browser process.")
|
||||
mojo::Remote<device::mojom::WakeLock> wake_lock_;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "shell/common/gin_converters/image_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "v8/include/cppgc/allocation.h"
|
||||
#include "v8/include/v8-cppgc.h"
|
||||
@@ -345,7 +344,7 @@ void Tray::Focus() {
|
||||
void Tray::PopUpContextMenu(gin::Arguments* args) {
|
||||
if (!CheckAlive())
|
||||
return;
|
||||
gin_helper::Handle<Menu> menu;
|
||||
Menu* menu = nullptr;
|
||||
gfx::Point pos;
|
||||
|
||||
v8::Local<v8::Value> first_arg;
|
||||
@@ -363,8 +362,8 @@ void Tray::PopUpContextMenu(gin::Arguments* args) {
|
||||
}
|
||||
}
|
||||
|
||||
tray_icon_->PopUpContextMenu(
|
||||
pos, menu.IsEmpty() ? nullptr : menu->model()->GetWeakPtr());
|
||||
tray_icon_->PopUpContextMenu(pos,
|
||||
menu ? menu->model()->GetWeakPtr() : nullptr);
|
||||
}
|
||||
|
||||
void Tray::CloseContextMenu() {
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/gin_helper/event.h"
|
||||
#include "shell/common/gin_helper/event_emitter.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "extensions/browser/media_capture_util.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "shell/browser/extensions/api/management/electron_management_api_delegate.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "shell/browser/extensions/electron_messaging_delegate.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
|
||||
@@ -675,9 +675,9 @@ void ElectronURLLoaderFactory::StartLoadingHttp(
|
||||
request->method != net::HttpRequestHeaders::kHeadMethod)
|
||||
dict.Get("uploadData", &upload_data);
|
||||
|
||||
gin_helper::Handle<api::Session> session;
|
||||
api::Session* session = nullptr;
|
||||
auto* browser_context =
|
||||
dict.Get("session", &session) && !session.IsEmpty()
|
||||
dict.Get("session", &session) && session
|
||||
? session->browser_context()
|
||||
: ElectronBrowserContext::GetDefaultBrowserContext();
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
|
||||
#include "shell/common/application_info.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "third_party/abseil-cpp/absl/strings/str_format.h"
|
||||
#include "third_party/blink/public/common/logging/logging_utils.h"
|
||||
|
||||
19
shell/common/gc_plugin.h
Normal file
19
shell/common/gc_plugin.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2025 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_GC_PLUGIN_H_
|
||||
#define ELECTRON_SHELL_COMMON_GC_PLUGIN_H_
|
||||
|
||||
// GC_PLUGIN_IGNORE is used to make the Blink GC plugin ignore a particular
|
||||
// class or field when checking for proper usage. When using GC_PLUGIN_IGNORE
|
||||
// a reason must be provided as an argument.
|
||||
// Based on third_party/blink/renderer/platform/wtf/gc_plugin.h
|
||||
#if defined(__clang__)
|
||||
#define GC_PLUGIN_IGNORE(reason) \
|
||||
__attribute__((annotate("blink_gc_plugin_ignore")))
|
||||
#else
|
||||
#define GC_PLUGIN_IGNORE(reason)
|
||||
#endif
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_GC_PLUGIN_H_
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "shell/common/gin_converters/gfx_converter.h"
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h"
|
||||
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
|
||||
#include "ui/base/mojom/menu_source_type.mojom.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "shell/browser/api/electron_api_web_frame_main.h"
|
||||
#include "shell/common/gin_helper/accessor.h"
|
||||
#include "shell/common/gin_helper/handle.h"
|
||||
#include "shell/common/node_util.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "gin/public/gin_embedders.h"
|
||||
#include "shell/common/gin_helper/destroyable.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "v8/include/cppgc/macros.h"
|
||||
#include "v8/include/v8-external.h"
|
||||
#include "v8/include/v8-microtask-queue.h"
|
||||
#include "v8/include/v8-template.h"
|
||||
@@ -189,6 +190,9 @@ void ThrowConversionError(gin::Arguments* args,
|
||||
// at position |index|.
|
||||
template <size_t index, typename ArgType, typename = void>
|
||||
struct ArgumentHolder {
|
||||
CPPGC_STACK_ALLOCATED();
|
||||
|
||||
public:
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
@@ -222,6 +226,9 @@ struct ArgumentHolder<
|
||||
std::is_constructible_v<
|
||||
typename CallbackParamTraits<ArgType>::LocalType,
|
||||
v8::Isolate*>>> {
|
||||
CPPGC_STACK_ALLOCATED();
|
||||
|
||||
public:
|
||||
using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
|
||||
|
||||
ArgLocalType value;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "gin/converter.h"
|
||||
#include "v8/include/cppgc/type-traits.h"
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
@@ -16,8 +17,15 @@ namespace gin_helper {
|
||||
// C++ to V8 can cause memory leaks. Copied from
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/6734440 Should be
|
||||
// removed once https://github.com/electron/electron/issues/47922 is complete.
|
||||
//
|
||||
// This class must NOT be used with cppgc-managed types (gin::Wrappable).
|
||||
// For cppgc types, use T* directly and gin::Converter<T*> for V8 conversion.
|
||||
template <typename T>
|
||||
class Handle {
|
||||
static_assert(!cppgc::IsGarbageCollectedTypeV<T>,
|
||||
"gin_helper::Handle must not be used with cppgc "
|
||||
"garbage-collected types. Use T* directly instead.");
|
||||
|
||||
public:
|
||||
Handle() : object_(nullptr) {}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define ELECTRON_SHELL_COMMON_GIN_HELPER_SELF_KEEP_ALIVE_H_
|
||||
|
||||
#include "gin/weak_cell.h"
|
||||
#include "shell/common/gc_plugin.h"
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
@@ -28,6 +29,7 @@ class SelfKeepAlive final {
|
||||
explicit operator bool() const { return keep_alive_; }
|
||||
|
||||
private:
|
||||
GC_PLUGIN_IGNORE("Allowed to keep a Persistent to itself.")
|
||||
cppgc::Persistent<Self> keep_alive_;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_metrics.h"
|
||||
#include "shell/common/api/electron_bindings.h"
|
||||
#include "shell/common/gc_plugin.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
@@ -204,6 +205,9 @@ class PreloadRealmLifetimeController
|
||||
std::unique_ptr<base::ProcessMetrics> metrics_;
|
||||
raw_ptr<ServiceWorkerData> service_worker_data_;
|
||||
|
||||
GC_PLUGIN_IGNORE(
|
||||
"Intentional GC root to keep this object alive until the context is "
|
||||
"destroyed")
|
||||
blink::Persistent<PreloadRealmLifetimeController> self_;
|
||||
};
|
||||
|
||||
|
||||
@@ -290,6 +290,92 @@ describe('cpp heap', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('menu module', () => {
|
||||
// Regression test for https://github.com/electron/electron/issues/50791
|
||||
it('should not leak when rebuilding application menu', async () => {
|
||||
const { remotely } = await startRemoteControlApp(['--js-flags=--expose-gc']);
|
||||
const result = await remotely(async () => {
|
||||
const { Menu } = require('electron');
|
||||
const v8Util = (process as any)._linkedBinding('electron_common_v8_util');
|
||||
const { getCppHeapStatistics } = require('node:v8');
|
||||
|
||||
function buildLargeMenu () {
|
||||
return Menu.buildFromTemplate(
|
||||
Array.from({ length: 10 }, (_, i) => ({
|
||||
label: `Menu ${i}`,
|
||||
submenu: Array.from({ length: 20 }, (_, j) => ({
|
||||
label: `Item ${i}-${j}`,
|
||||
click: () => {}
|
||||
}))
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
async function rebuildAndMeasure (n: number) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
Menu.setApplicationMenu(buildLargeMenu());
|
||||
}
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
v8Util.requestGarbageCollectionForTesting();
|
||||
}
|
||||
return getCppHeapStatistics('brief').used_size_bytes;
|
||||
}
|
||||
|
||||
await rebuildAndMeasure(50);
|
||||
const after1 = await rebuildAndMeasure(50);
|
||||
const after2 = await rebuildAndMeasure(50);
|
||||
return { after1, after2 };
|
||||
});
|
||||
|
||||
const growth = result.after2 - result.after1;
|
||||
expect(growth).to.be.at.most(
|
||||
result.after1 * 0.1,
|
||||
`C++ heap grew by ${growth} bytes between two identical rounds of 100 menu rebuilds — likely a leak`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('powerMonitor module', () => {
|
||||
it('should retain native PowerMonitor via JS module reference', async () => {
|
||||
const rc = await startRemoteControlApp(['--expose-internals', '--js-flags=--expose-gc']);
|
||||
const result = await rc.remotely(async (heap: string) => {
|
||||
const { powerMonitor, app } = require('electron');
|
||||
const { recordState } = require(heap);
|
||||
const v8Util = (process as any)._linkedBinding('electron_common_v8_util');
|
||||
|
||||
// Register a listener to trigger native PowerMonitor creation.
|
||||
const listener = () => {};
|
||||
powerMonitor.on('suspend', listener);
|
||||
|
||||
// Add and remove several listeners to exercise the path,
|
||||
// then GC to ensure no duplicate native objects are created.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const tmp = () => {};
|
||||
powerMonitor.on('resume', tmp);
|
||||
powerMonitor.removeListener('resume', tmp);
|
||||
}
|
||||
|
||||
v8Util.requestGarbageCollectionForTesting();
|
||||
|
||||
const state = recordState();
|
||||
const nodes = state.snapshot.filter(
|
||||
(node: any) => node.name === 'Electron / PowerMonitor'
|
||||
);
|
||||
const found = nodes.length > 0;
|
||||
const noDuplicates = nodes.length === 1;
|
||||
|
||||
setTimeout(() => app.quit());
|
||||
return { found, noDuplicates };
|
||||
}, path.join(__dirname, '../../third_party/electron_node/test/common/heap'));
|
||||
|
||||
const [code] = await once(rc.process, 'exit');
|
||||
expect(code).to.equal(0);
|
||||
expect(result.found).to.equal(true, 'PowerMonitor should be in snapshot (held by JS module)');
|
||||
expect(result.noDuplicates).to.equal(true, 'should have exactly one PowerMonitor instance');
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal event', () => {
|
||||
it('should record as node in heap snapshot', async () => {
|
||||
const { remotely } = await startRemoteControlApp(['--expose-internals']);
|
||||
|
||||
Reference in New Issue
Block a user