refactor: allocate api::Session on cpp heap (#48141)

This commit is contained in:
Robo 2025-08-25 18:52:06 +09:00 committed by GitHub
commit 3ccb1bc0a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 632 additions and 293 deletions

View file

@ -8,16 +8,17 @@ electron objects that extend gin::Wrappable and gets
allocated on the cpp heap allocated on the cpp heap
diff --git a/gin/public/wrappable_pointer_tags.h b/gin/public/wrappable_pointer_tags.h diff --git a/gin/public/wrappable_pointer_tags.h b/gin/public/wrappable_pointer_tags.h
index a507d1d837ab3ec2b2d3ae7978d9d410ab2ec2d1..a547ecacad732f73512abbe5da81483208bb9e81 100644 index a507d1d837ab3ec2b2d3ae7978d9d410ab2ec2d1..5a69c5c7c5ed0e834e09ff3a2d0f0126ba4ccf99 100644
--- a/gin/public/wrappable_pointer_tags.h --- a/gin/public/wrappable_pointer_tags.h
+++ b/gin/public/wrappable_pointer_tags.h +++ b/gin/public/wrappable_pointer_tags.h
@@ -72,7 +72,8 @@ enum WrappablePointerTag : uint16_t { @@ -72,7 +72,9 @@ enum WrappablePointerTag : uint16_t {
kTextInputControllerBindings, // content::TextInputControllerBindings kTextInputControllerBindings, // content::TextInputControllerBindings
kWebAXObjectProxy, // content::WebAXObjectProxy kWebAXObjectProxy, // content::WebAXObjectProxy
kWrappedExceptionHandler, // extensions::WrappedExceptionHandler kWrappedExceptionHandler, // extensions::WrappedExceptionHandler
- kLastPointerTag = kWrappedExceptionHandler, - kLastPointerTag = kWrappedExceptionHandler,
+ kElectronApp, // electron::api::App + kElectronApp, // electron::api::App
+ kLastPointerTag = kElectronApp, + kElectronSession, // electron::api::Session
+ kLastPointerTag = kElectronSession,
}; };
static_assert(kLastPointerTag < static_assert(kLastPointerTag <

View file

@ -8,6 +8,30 @@ Restores part of https://chromium-review.googlesource.com/c/chromium/src/+/67991
Patch can be removed once cppgc migration is complete Patch can be removed once cppgc migration is complete
https://github.com/electron/electron/issues/47922 https://github.com/electron/electron/issues/47922
diff --git a/gin/function_template.h b/gin/function_template.h
index 84ab9585240a49048774811718f7ebd6f988e485..f062163cdd81def12fae7e507d18a9133dd0804d 100644
--- a/gin/function_template.h
+++ b/gin/function_template.h
@@ -77,6 +77,7 @@ class GIN_EXPORT CallbackHolderBase {
CallbackHolderBase* holder);
~DisposeObserver() override;
void OnBeforeDispose(v8::Isolate* isolate) override;
+ void OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) override {}
void OnDisposed() override;
private:
diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc
index bb1639d73070a99984b72eb61afd001dec5b08ff..b036f324309c46c53b74124da3ea830d39a973e3 100644
--- a/gin/isolate_holder.cc
+++ b/gin/isolate_holder.cc
@@ -224,6 +224,7 @@ void IsolateHolder::WillCreateMicrotasksRunner() {
void IsolateHolder::WillDestroyMicrotasksRunner() {
DCHECK(g_initialized_microtasks_runner);
+ isolate_data_->NotifyBeforeMicrotasksRunnerDispose();
g_destroyed_microtasks_runner = true;
}
diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc
index 5a31687bbd0fca61db3a7c41ed73d938340d6446..b84f5fd336bc4b61b2cd0b2fc92382b00e928701 100644 index 5a31687bbd0fca61db3a7c41ed73d938340d6446..b84f5fd336bc4b61b2cd0b2fc92382b00e928701 100644
--- a/gin/object_template_builder.cc --- a/gin/object_template_builder.cc
@ -22,10 +46,10 @@ index 5a31687bbd0fca61db3a7c41ed73d938340d6446..b84f5fd336bc4b61b2cd0b2fc92382b0
ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate, ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate,
diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc
index 884990426f13a6abca22a60dd8cc0685f8435b23..64ac0a64a05105532f3cda898aeac68c21338e60 100644 index 884990426f13a6abca22a60dd8cc0685f8435b23..d1014af4b63da244820ff865a8e824ddf68433a9 100644
--- a/gin/per_isolate_data.cc --- a/gin/per_isolate_data.cc
+++ b/gin/per_isolate_data.cc +++ b/gin/per_isolate_data.cc
@@ -68,12 +68,32 @@ PerIsolateData* PerIsolateData::From(Isolate* isolate) { @@ -68,12 +68,37 @@ PerIsolateData* PerIsolateData::From(Isolate* isolate) {
return static_cast<PerIsolateData*>(isolate->GetData(kEmbedderNativeGin)); return static_cast<PerIsolateData*>(isolate->GetData(kEmbedderNativeGin));
} }
@ -40,8 +64,13 @@ index 884990426f13a6abca22a60dd8cc0685f8435b23..64ac0a64a05105532f3cda898aeac68c
object_templates_[info] = Eternal<ObjectTemplate>(isolate_, templ); object_templates_[info] = Eternal<ObjectTemplate>(isolate_, templ);
} }
+void PerIsolateData::SetFunctionTemplate(DeprecatedWrapperInfo* info, +void PerIsolateData::DeprecatedSetFunctionTemplate(
+ Local<FunctionTemplate> templ) { + DeprecatedWrapperInfo* info, Local<FunctionTemplate> templ) {
+ deprecated_function_templates_[info] = Eternal<FunctionTemplate>(isolate_, templ);
+}
+
+void PerIsolateData::SetFunctionTemplate(
+ const WrapperInfo* info, Local<FunctionTemplate> templ) {
+ function_templates_[info] = Eternal<FunctionTemplate>(isolate_, templ); + function_templates_[info] = Eternal<FunctionTemplate>(isolate_, templ);
+} +}
+ +
@ -58,12 +87,22 @@ index 884990426f13a6abca22a60dd8cc0685f8435b23..64ac0a64a05105532f3cda898aeac68c
v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate( v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate(
const WrapperInfo* info) { const WrapperInfo* info) {
ObjectTemplateMap::iterator it = object_templates_.find(info); ObjectTemplateMap::iterator it = object_templates_.find(info);
@@ -83,6 +103,15 @@ v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate( @@ -83,6 +108,25 @@ v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate(
return it->second.Get(isolate_); return it->second.Get(isolate_);
} }
+v8::Local<v8::FunctionTemplate> PerIsolateData::GetFunctionTemplate( +v8::Local<v8::FunctionTemplate> PerIsolateData::DeprecatedGetFunctionTemplate(
+ DeprecatedWrapperInfo* info) { + DeprecatedWrapperInfo* info) {
+ DeprecatedFunctionTemplateMap::iterator it =
+ deprecated_function_templates_.find(info);
+ if (it == deprecated_function_templates_.end()) {
+ return v8::Local<v8::FunctionTemplate>();
+ }
+ return it->second.Get(isolate_);
+}
+
+v8::Local<v8::FunctionTemplate> PerIsolateData::GetFunctionTemplate(
+ const WrapperInfo* info) {
+ FunctionTemplateMap::iterator it = function_templates_.find(info); + FunctionTemplateMap::iterator it = function_templates_.find(info);
+ if (it == function_templates_.end()) { + if (it == function_templates_.end()) {
+ return v8::Local<v8::FunctionTemplate>(); + return v8::Local<v8::FunctionTemplate>();
@ -74,11 +113,35 @@ index 884990426f13a6abca22a60dd8cc0685f8435b23..64ac0a64a05105532f3cda898aeac68c
void PerIsolateData::AddDisposeObserver(DisposeObserver* observer) { void PerIsolateData::AddDisposeObserver(DisposeObserver* observer) {
dispose_observers_.AddObserver(observer); dispose_observers_.AddObserver(observer);
} }
@@ -97,6 +141,12 @@ void PerIsolateData::NotifyBeforeDispose() {
}
}
+void PerIsolateData::NotifyBeforeMicrotasksRunnerDispose() {
+ for (auto& observer : dispose_observers_) {
+ observer.OnBeforeMicrotasksRunnerDispose(isolate_.get());
+ }
+}
+
void PerIsolateData::NotifyDisposed() {
for (auto& observer : dispose_observers_) {
observer.OnDisposed();
diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h
index bce889749415da341e6e6e4082ac06bbeb4bb80a..d748c1cf8cef7da90686686f1b8072bcd530541d 100644 index bce889749415da341e6e6e4082ac06bbeb4bb80a..2f8abc344c77713fb10d83e51ba486c84ab93474 100644
--- a/gin/per_isolate_data.h --- a/gin/per_isolate_data.h
+++ b/gin/per_isolate_data.h +++ b/gin/per_isolate_data.h
@@ -51,11 +51,24 @@ class GIN_EXPORT PerIsolateData { @@ -34,6 +34,10 @@ class GIN_EXPORT PerIsolateData {
// be entered before the observer is notified, but there will not be a
// handle scope by default.
virtual void OnBeforeDispose(v8::Isolate* isolate) = 0;
+
+ // Called just before the microtasks runner is about to be disposed.
+ virtual void OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) = 0;
+
// Called just after the isolate has been disposed.
virtual void OnDisposed() = 0;
};
@@ -51,14 +55,36 @@ class GIN_EXPORT PerIsolateData {
static PerIsolateData* From(v8::Isolate* isolate); static PerIsolateData* From(v8::Isolate* isolate);
@ -89,7 +152,12 @@ index bce889749415da341e6e6e4082ac06bbeb4bb80a..d748c1cf8cef7da90686686f1b8072bc
void SetObjectTemplate(const WrapperInfo* info, void SetObjectTemplate(const WrapperInfo* info,
v8::Local<v8::ObjectTemplate> object_template); v8::Local<v8::ObjectTemplate> object_template);
+ void SetFunctionTemplate(DeprecatedWrapperInfo* info, + void DeprecatedSetFunctionTemplate(
+ DeprecatedWrapperInfo* info,
+ v8::Local<v8::FunctionTemplate> function_template);
+
+ void SetFunctionTemplate(
+ const WrapperInfo* info,
+ v8::Local<v8::FunctionTemplate> function_template); + v8::Local<v8::FunctionTemplate> function_template);
+ +
+ v8::Local<v8::ObjectTemplate> DeprecatedGetObjectTemplate( + v8::Local<v8::ObjectTemplate> DeprecatedGetObjectTemplate(
@ -97,13 +165,20 @@ index bce889749415da341e6e6e4082ac06bbeb4bb80a..d748c1cf8cef7da90686686f1b8072bc
+ +
v8::Local<v8::ObjectTemplate> GetObjectTemplate(const WrapperInfo* info); v8::Local<v8::ObjectTemplate> GetObjectTemplate(const WrapperInfo* info);
+ v8::Local<v8::FunctionTemplate> GetFunctionTemplate( + v8::Local<v8::FunctionTemplate> DeprecatedGetFunctionTemplate(
+ DeprecatedWrapperInfo* info); + DeprecatedWrapperInfo* info);
+
+ v8::Local<v8::FunctionTemplate> GetFunctionTemplate(
+ const WrapperInfo* info);
+ +
void AddDisposeObserver(DisposeObserver* observer); void AddDisposeObserver(DisposeObserver* observer);
void RemoveDisposeObserver(DisposeObserver* observer); void RemoveDisposeObserver(DisposeObserver* observer);
void NotifyBeforeDispose(); void NotifyBeforeDispose();
@@ -74,14 +87,20 @@ class GIN_EXPORT PerIsolateData { + void NotifyBeforeMicrotasksRunnerDispose();
void NotifyDisposed();
void EnableIdleTasks(std::unique_ptr<V8IdleTaskRunner> idle_task_runner);
@@ -74,14 +100,23 @@ class GIN_EXPORT PerIsolateData {
} }
private: private:
@ -112,6 +187,8 @@ index bce889749415da341e6e6e4082ac06bbeb4bb80a..d748c1cf8cef7da90686686f1b8072bc
typedef std::map<const WrapperInfo*, v8::Eternal<v8::ObjectTemplate>> typedef std::map<const WrapperInfo*, v8::Eternal<v8::ObjectTemplate>>
ObjectTemplateMap; ObjectTemplateMap;
+ typedef std::map<DeprecatedWrapperInfo*, v8::Eternal<v8::FunctionTemplate>> + typedef std::map<DeprecatedWrapperInfo*, v8::Eternal<v8::FunctionTemplate>>
+ DeprecatedFunctionTemplateMap;
+ typedef std::map<const WrapperInfo*, v8::Eternal<v8::FunctionTemplate>>
+ FunctionTemplateMap; + FunctionTemplateMap;
// PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is // PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is
@ -120,6 +197,7 @@ index bce889749415da341e6e6e4082ac06bbeb4bb80a..d748c1cf8cef7da90686686f1b8072bc
raw_ptr<v8::ArrayBuffer::Allocator, DanglingUntriaged> allocator_; raw_ptr<v8::ArrayBuffer::Allocator, DanglingUntriaged> allocator_;
+ DeprecatedObjectTemplateMap deprecated_object_templates_; + DeprecatedObjectTemplateMap deprecated_object_templates_;
ObjectTemplateMap object_templates_; ObjectTemplateMap object_templates_;
+ DeprecatedFunctionTemplateMap deprecated_function_templates_;
+ FunctionTemplateMap function_templates_; + FunctionTemplateMap function_templates_;
base::ObserverList<DisposeObserver> dispose_observers_; base::ObserverList<DisposeObserver> dispose_observers_;
std::shared_ptr<V8ForegroundTaskRunnerBase> task_runner_; std::shared_ptr<V8ForegroundTaskRunnerBase> task_runner_;

View file

@ -99,6 +99,7 @@
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "url/origin.h" #include "url/origin.h"
#include "v8/include/v8-traced-handle.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "shell/browser/api/electron_api_extensions.h" #include "shell/browser/api/electron_api_extensions.h"
@ -544,23 +545,27 @@ class DictionaryObserver final : public SpellcheckCustomDictionary::Observer {
#endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
struct UserDataLink : base::SupportsUserData::Data { struct UserDataLink : base::SupportsUserData::Data {
explicit UserDataLink(base::WeakPtr<Session> session_in) explicit UserDataLink(
: session{std::move(session_in)} {} cppgc::WeakPersistent<gin::WeakCell<Session>> session_in)
: session{session_in} {}
base::WeakPtr<Session> session; cppgc::WeakPersistent<gin::WeakCell<Session>> session;
}; };
const void* kElectronApiSessionKey = &kElectronApiSessionKey; const void* kElectronApiSessionKey = &kElectronApiSessionKey;
} // namespace } // namespace
gin::DeprecatedWrapperInfo Session::kWrapperInfo = {gin::kEmbedderNativeGin}; gin::WrapperInfo Session::kWrapperInfo = {{gin::kEmbedderNativeGin},
gin::kElectronSession};
Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context) Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
: isolate_(isolate), : isolate_(isolate),
network_emulation_token_(base::UnguessableToken::Create()), network_emulation_token_(base::UnguessableToken::Create()),
browser_context_{ browser_context_{
raw_ref<ElectronBrowserContext>::from_ptr(browser_context)} { raw_ref<ElectronBrowserContext>::from_ptr(browser_context)} {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
data->AddDisposeObserver(this);
// Observe DownloadManager to get download notifications. // Observe DownloadManager to get download notifications.
browser_context->GetDownloadManager()->AddObserver(this); browser_context->GetDownloadManager()->AddObserver(this);
@ -572,7 +577,10 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
browser_context->SetUserData( browser_context->SetUserData(
kElectronApiSessionKey, kElectronApiSessionKey,
std::make_unique<UserDataLink>(weak_factory_.GetWeakPtr())); std::make_unique<UserDataLink>(
cppgc::WeakPersistent<gin::WeakCell<Session>>(
weak_factory_.GetWeakCell(
isolate->GetCppHeap()->GetAllocationHandle()))));
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
if (auto* service = if (auto* service =
@ -583,6 +591,11 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
} }
Session::~Session() { Session::~Session() {
Dispose();
}
void Session::Dispose() {
if (keep_alive_) {
browser_context()->GetDownloadManager()->RemoveObserver(this); browser_context()->GetDownloadManager()->RemoveObserver(this);
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
@ -592,6 +605,7 @@ Session::~Session() {
} }
#endif #endif
} }
}
void Session::OnDownloadCreated(content::DownloadManager* manager, void Session::OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) { download::DownloadItem* item) {
@ -1307,7 +1321,7 @@ v8::Local<v8::Promise> Session::GetSharedDictionaryUsageInfo() {
} }
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) { v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
if (cookies_.IsEmpty()) { if (cookies_.IsEmptyThreadSafe()) {
auto handle = Cookies::Create(isolate, browser_context()); auto handle = Cookies::Create(isolate, browser_context());
cookies_.Reset(isolate, handle.ToV8()); cookies_.Reset(isolate, handle.ToV8());
} }
@ -1316,7 +1330,7 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
v8::Local<v8::Value> Session::Extensions(v8::Isolate* isolate) { v8::Local<v8::Value> Session::Extensions(v8::Isolate* isolate) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
if (extensions_.IsEmpty()) { if (extensions_.IsEmptyThreadSafe()) {
v8::Local<v8::Value> handle; v8::Local<v8::Value> handle;
handle = Extensions::Create(isolate, browser_context()).ToV8(); handle = Extensions::Create(isolate, browser_context()).ToV8();
extensions_.Reset(isolate, handle); extensions_.Reset(isolate, handle);
@ -1330,7 +1344,7 @@ v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
} }
v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) { v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
if (service_worker_context_.IsEmpty()) { if (service_worker_context_.IsEmptyThreadSafe()) {
v8::Local<v8::Value> handle; v8::Local<v8::Value> handle;
handle = ServiceWorkerContext::Create(isolate, browser_context()).ToV8(); handle = ServiceWorkerContext::Create(isolate, browser_context()).ToV8();
service_worker_context_.Reset(isolate, handle); service_worker_context_.Reset(isolate, handle);
@ -1339,7 +1353,7 @@ v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
} }
v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) { v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
if (web_request_.IsEmpty()) { if (web_request_.IsEmptyThreadSafe()) {
auto handle = WebRequest::Create(isolate, browser_context()); auto handle = WebRequest::Create(isolate, browser_context());
web_request_.Reset(isolate, handle.ToV8()); web_request_.Reset(isolate, handle.ToV8());
} }
@ -1347,7 +1361,7 @@ v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
} }
v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) { v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) {
if (net_log_.IsEmpty()) { if (net_log_.IsEmptyThreadSafe()) {
auto handle = NetLog::Create(isolate, browser_context()); auto handle = NetLog::Create(isolate, browser_context());
net_log_.Reset(isolate, handle.ToV8()); net_log_.Reset(isolate, handle.ToV8());
} }
@ -1656,40 +1670,47 @@ bool Session::IsSpellCheckerEnabled() const {
#endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
// static // static
Session* Session::FromBrowserContext(content::BrowserContext* context) { gin::WeakCell<Session>* Session::FromBrowserContext(
content::BrowserContext* context) {
auto* data = auto* data =
static_cast<UserDataLink*>(context->GetUserData(kElectronApiSessionKey)); static_cast<UserDataLink*>(context->GetUserData(kElectronApiSessionKey));
return data ? data->session.get() : nullptr; if (data && data->session)
return data->session.Get();
return nullptr;
} }
// static // static
gin_helper::Handle<Session> Session::CreateFrom( Session* Session::CreateFrom(v8::Isolate* isolate,
v8::Isolate* isolate,
ElectronBrowserContext* browser_context) { ElectronBrowserContext* browser_context) {
Session* existing = FromBrowserContext(browser_context); gin::WeakCell<Session>* existing = FromBrowserContext(browser_context);
if (existing) if (existing && existing->Get()) {
return gin_helper::CreateHandle(isolate, existing); return existing->Get();
}
auto handle = auto* session = cppgc::MakeGarbageCollected<Session>(
gin_helper::CreateHandle(isolate, new Session(isolate, browser_context)); isolate->GetCppHeap()->GetAllocationHandle(), isolate, browser_context);
// The Sessions should never be garbage collected, since the common pattern is
// to use partition strings, instead of using the Session object directly.
handle->Pin(isolate);
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
gin_helper::CallMethod(isolate, handle.get(), "_init"); gin_helper::CallMethod(isolate, session, "_init");
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
node::errors::TriggerUncaughtException(isolate, try_catch); node::errors::TriggerUncaughtException(isolate, try_catch);
return nullptr;
} }
App::Get()->EmitWithoutEvent("session-created", handle); {
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> wrapper;
if (!session->GetWrapper(isolate).ToLocal(&wrapper)) {
return nullptr;
}
App::Get()->EmitWithoutEvent("session-created", wrapper);
}
return handle; return session;
} }
// static // static
gin_helper::Handle<Session> Session::FromPartition(v8::Isolate* isolate, Session* Session::FromPartition(v8::Isolate* isolate,
const std::string& partition, const std::string& partition,
base::Value::Dict options) { base::Value::Dict options) {
ElectronBrowserContext* browser_context; ElectronBrowserContext* browser_context;
@ -1708,34 +1729,30 @@ gin_helper::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
} }
// static // static
std::optional<gin_helper::Handle<Session>> Session::FromPath( Session* Session::FromPath(gin::Arguments* args,
v8::Isolate* isolate,
const base::FilePath& path, const base::FilePath& path,
base::Value::Dict options) { base::Value::Dict options) {
ElectronBrowserContext* browser_context; ElectronBrowserContext* browser_context;
if (path.empty()) { if (path.empty()) {
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate); args->ThrowTypeError("An empty path was specified");
promise.RejectWithErrorMessage("An empty path was specified"); return nullptr;
return std::nullopt;
} }
if (!path.IsAbsolute()) { if (!path.IsAbsolute()) {
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate); args->ThrowTypeError("An absolute path was not provided");
promise.RejectWithErrorMessage("An absolute path was not provided"); return nullptr;
return std::nullopt;
} }
browser_context = browser_context =
ElectronBrowserContext::FromPath(std::move(path), std::move(options)); ElectronBrowserContext::FromPath(std::move(path), std::move(options));
return CreateFrom(isolate, browser_context); return CreateFrom(args->isolate(), browser_context);
} }
// static // static
gin_helper::Handle<Session> Session::New() { void Session::New() {
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate()) gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
.ThrowError("Session objects cannot be created with 'new'"); .ThrowError("Session objects cannot be created with 'new'");
return {};
} }
void Session::FillObjectTemplate(v8::Isolate* isolate, void Session::FillObjectTemplate(v8::Isolate* isolate,
@ -1821,12 +1838,31 @@ void Session::FillObjectTemplate(v8::Isolate* isolate,
.Build(); .Build();
} }
const char* Session::GetTypeName() { void Session::Trace(cppgc::Visitor* visitor) const {
return GetClassName(); gin::Wrappable<Session>::Trace(visitor);
visitor->Trace(cookies_);
visitor->Trace(extensions_);
visitor->Trace(protocol_);
visitor->Trace(net_log_);
visitor->Trace(service_worker_context_);
visitor->Trace(web_request_);
visitor->Trace(weak_factory_);
} }
void Session::WillBeDestroyed() { const gin::WrapperInfo* Session::wrapper_info() const {
ClearWeak(); return &kWrapperInfo;
}
const char* Session::GetHumanReadableName() const {
return "Electron / Session";
}
void Session::OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
data->RemoveDisposeObserver(this);
Dispose();
weak_factory_.Invalidate();
keep_alive_.Clear();
} }
} // namespace electron::api } // namespace electron::api
@ -1843,8 +1879,19 @@ v8::Local<v8::Value> FromPartition(const std::string& partition,
} }
base::Value::Dict options; base::Value::Dict options;
args->GetNext(&options); args->GetNext(&options);
return Session::FromPartition(args->isolate(), partition, std::move(options)) Session* session =
.ToV8(); Session::FromPartition(args->isolate(), partition, std::move(options));
if (session) {
v8::HandleScope handle_scope(args->isolate());
v8::Local<v8::Object> wrapper;
if (!session->GetWrapper(args->isolate()).ToLocal(&wrapper)) {
return v8::Null(args->isolate());
}
return wrapper;
} else {
return v8::Null(args->isolate());
}
} }
v8::Local<v8::Value> FromPath(const base::FilePath& path, v8::Local<v8::Value> FromPath(const base::FilePath& path,
@ -1855,14 +1902,19 @@ v8::Local<v8::Value> FromPath(const base::FilePath& path,
} }
base::Value::Dict options; base::Value::Dict options;
args->GetNext(&options); args->GetNext(&options);
std::optional<gin_helper::Handle<Session>> session_handle = Session* session = Session::FromPath(args, path, std::move(options));
Session::FromPath(args->isolate(), path, std::move(options));
if (session_handle) if (session) {
return session_handle.value().ToV8(); v8::HandleScope handle_scope(args->isolate());
else v8::Local<v8::Object> wrapper;
if (!session->GetWrapper(args->isolate()).ToLocal(&wrapper)) {
return v8::Null(args->isolate()); return v8::Null(args->isolate());
} }
return wrapper;
} else {
return v8::Null(args->isolate());
}
}
void Initialize(v8::Local<v8::Object> exports, void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused, v8::Local<v8::Value> unused,
@ -1870,7 +1922,8 @@ void Initialize(v8::Local<v8::Object> exports,
void* priv) { void* priv) {
v8::Isolate* const isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* const isolate = electron::JavascriptEnvironment::GetIsolate();
gin_helper::Dictionary dict(isolate, exports); gin_helper::Dictionary dict(isolate, exports);
dict.Set("Session", Session::GetConstructor(isolate, context)); dict.Set("Session",
Session::GetConstructor(isolate, context, &Session::kWrapperInfo));
dict.SetMethod("fromPartition", &FromPartition); dict.SetMethod("fromPartition", &FromPartition);
dict.SetMethod("fromPath", &FromPath); dict.SetMethod("fromPath", &FromPath);
} }

View file

@ -15,15 +15,15 @@
#include "base/values.h" #include "base/values.h"
#include "content/public/browser/download_manager.h" #include "content/public/browser/download_manager.h"
#include "electron/buildflags/buildflags.h" #include "electron/buildflags/buildflags.h"
#include "gin/weak_cell.h"
#include "gin/wrappable.h"
#include "services/network/public/mojom/host_resolver.mojom-forward.h" #include "services/network/public/mojom/host_resolver.mojom-forward.h"
#include "services/network/public/mojom/ssl_config.mojom-forward.h" #include "services/network/public/mojom/ssl_config.mojom-forward.h"
#include "shell/browser/api/ipc_dispatcher.h" #include "shell/browser/api/ipc_dispatcher.h"
#include "shell/browser/event_emitter_mixin.h" #include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/net/resolve_proxy_helper.h" #include "shell/browser/net/resolve_proxy_helper.h"
#include "shell/common/gin_helper/cleaned_up_at_exit.h"
#include "shell/common/gin_helper/constructible.h" #include "shell/common/gin_helper/constructible.h"
#include "shell/common/gin_helper/pinnable.h" #include "shell/common/gin_helper/self_keep_alive.h"
#include "shell/common/gin_helper/wrappable.h"
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" // nogncheck #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" // nogncheck
@ -39,11 +39,6 @@ namespace gin {
class Arguments; class Arguments;
} // namespace gin } // namespace gin
namespace gin_helper {
template <typename T>
class Handle;
} // namespace gin_helper
namespace gin_helper { namespace gin_helper {
class Dictionary; class Dictionary;
class ErrorThrower; class ErrorThrower;
@ -53,6 +48,11 @@ namespace net {
class ProxyConfig; class ProxyConfig;
} }
namespace v8 {
template <typename T>
class TracedReference;
}
namespace electron { namespace electron {
class ElectronBrowserContext; class ElectronBrowserContext;
@ -60,11 +60,10 @@ struct PreloadScript;
namespace api { namespace api {
class Session final : public gin_helper::DeprecatedWrappable<Session>, class Session final : public gin::Wrappable<Session>,
public gin_helper::Pinnable<Session>,
public gin_helper::Constructible<Session>, public gin_helper::Constructible<Session>,
public gin_helper::EventEmitterMixin<Session>, public gin_helper::EventEmitterMixin<Session>,
public gin_helper::CleanedUpAtExit, public gin::PerIsolateData::DisposeObserver,
public IpcDispatcher<Session>, public IpcDispatcher<Session>,
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
private SpellcheckHunspellDictionary::Observer, private SpellcheckHunspellDictionary::Observer,
@ -72,39 +71,46 @@ class Session final : public gin_helper::DeprecatedWrappable<Session>,
private content::DownloadManager::Observer { private content::DownloadManager::Observer {
public: public:
// Gets or creates Session from the |browser_context|. // Gets or creates Session from the |browser_context|.
static gin_helper::Handle<Session> CreateFrom( static Session* CreateFrom(v8::Isolate* isolate,
v8::Isolate* isolate,
ElectronBrowserContext* browser_context); ElectronBrowserContext* browser_context);
static gin_helper::Handle<Session> New(); // Dummy, do not use! static void New(); // Dummy, do not use!
static Session* FromBrowserContext(content::BrowserContext* context); static gin::WeakCell<Session>* FromBrowserContext(
content::BrowserContext* context);
// Gets the Session of |partition|. // Gets the Session of |partition|.
static gin_helper::Handle<Session> FromPartition( static Session* FromPartition(v8::Isolate* isolate,
v8::Isolate* isolate,
const std::string& partition, const std::string& partition,
base::Value::Dict options = {}); base::Value::Dict options = {});
// Gets the Session based on |path|. // Gets the Session based on |path|.
static std::optional<gin_helper::Handle<Session>> FromPath( static Session* FromPath(gin::Arguments* args,
v8::Isolate* isolate,
const base::FilePath& path, const base::FilePath& path,
base::Value::Dict options = {}); base::Value::Dict options = {});
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>);
static const char* GetClassName() { return "Session"; }
Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
~Session() override;
ElectronBrowserContext* browser_context() const { ElectronBrowserContext* browser_context() const {
return &browser_context_.get(); return &browser_context_.get();
} }
// gin_helper::Wrappable // gin::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo; static gin::WrapperInfo kWrapperInfo;
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>); void Trace(cppgc::Visitor*) const override;
static const char* GetClassName() { return "Session"; } const gin::WrapperInfo* wrapper_info() const override;
const char* GetTypeName() override; const char* GetHumanReadableName() const override;
// gin_helper::CleanedUpAtExit // gin::PerIsolateData::DisposeObserver
void WillBeDestroyed() override; void OnBeforeDispose(v8::Isolate* isolate) override {}
void OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) override;
void OnDisposed() override {}
// Methods. // Methods.
void Dispose();
v8::Local<v8::Promise> ResolveHost( v8::Local<v8::Promise> ResolveHost(
std::string host, std::string host,
std::optional<network::mojom::ResolveHostParametersPtr> params); std::optional<network::mojom::ResolveHostParametersPtr> params);
@ -180,9 +186,6 @@ class Session final : public gin_helper::DeprecatedWrappable<Session>,
Session& operator=(const Session&) = delete; Session& operator=(const Session&) = delete;
protected: protected:
Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
~Session() override;
// content::DownloadManager::Observer: // content::DownloadManager::Observer:
void OnDownloadCreated(content::DownloadManager* manager, void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override; download::DownloadItem* item) override;
@ -202,12 +205,12 @@ class Session final : public gin_helper::DeprecatedWrappable<Session>,
v8::Local<v8::Value> val); v8::Local<v8::Value> val);
// Cached gin_helper::Wrappable objects. // Cached gin_helper::Wrappable objects.
v8::Global<v8::Value> cookies_; v8::TracedReference<v8::Value> cookies_;
v8::Global<v8::Value> extensions_; v8::TracedReference<v8::Value> extensions_;
v8::Global<v8::Value> protocol_; v8::TracedReference<v8::Value> protocol_;
v8::Global<v8::Value> net_log_; v8::TracedReference<v8::Value> net_log_;
v8::Global<v8::Value> service_worker_context_; v8::TracedReference<v8::Value> service_worker_context_;
v8::Global<v8::Value> web_request_; v8::TracedReference<v8::Value> web_request_;
raw_ptr<v8::Isolate> isolate_; raw_ptr<v8::Isolate> isolate_;
@ -216,7 +219,9 @@ class Session final : public gin_helper::DeprecatedWrappable<Session>,
const raw_ref<ElectronBrowserContext> browser_context_; const raw_ref<ElectronBrowserContext> browser_context_;
base::WeakPtrFactory<Session> weak_factory_{this}; gin::WeakCellFactory<Session> weak_factory_{this};
gin_helper::SelfKeepAlive<Session> keep_alive_{this};
}; };
} // namespace api } // namespace api

View file

@ -87,7 +87,6 @@
#include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_provider.h"
#include "shell/browser/api/electron_api_browser_window.h" #include "shell/browser/api/electron_api_browser_window.h"
#include "shell/browser/api/electron_api_debugger.h" #include "shell/browser/api/electron_api_debugger.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/electron_api_web_frame_main.h" #include "shell/browser/api/electron_api_web_frame_main.h"
#include "shell/browser/api/frame_subscriber.h" #include "shell/browser/api/frame_subscriber.h"
#include "shell/browser/api/message_port.h" #include "shell/browser/api/message_port.h"
@ -755,8 +754,7 @@ WebContents::WebContents(v8::Isolate* isolate,
script_executor_ = std::make_unique<extensions::ScriptExecutor>(web_contents); script_executor_ = std::make_unique<extensions::ScriptExecutor>(web_contents);
#endif #endif
auto session = Session::CreateFrom(isolate, GetBrowserContext()); session_ = Session::CreateFrom(isolate, GetBrowserContext());
session_.Reset(isolate, session.ToV8());
SetUserAgent(GetBrowserContext()->GetUserAgent()); SetUserAgent(GetBrowserContext()->GetUserAgent());
@ -778,9 +776,9 @@ WebContents::WebContents(v8::Isolate* isolate,
{ {
DCHECK(type != Type::kRemote) DCHECK(type != Type::kRemote)
<< "Can't take ownership of a remote WebContents"; << "Can't take ownership of a remote WebContents";
auto session = Session::CreateFrom(isolate, GetBrowserContext()); session_ = Session::CreateFrom(isolate, GetBrowserContext());
session_.Reset(isolate, session.ToV8()); InitWithSessionAndOptions(isolate, std::move(web_contents),
InitWithSessionAndOptions(isolate, std::move(web_contents), session, session_->browser_context(),
gin::Dictionary::CreateEmpty(isolate)); gin::Dictionary::CreateEmpty(isolate));
} }
@ -829,15 +827,15 @@ WebContents::WebContents(v8::Isolate* isolate,
// Obtain the session. // Obtain the session.
std::string partition; std::string partition;
gin_helper::Handle<api::Session> session; api::Session* session = nullptr;
if (options.Get("session", &session) && !session.IsEmpty()) { if (options.Get("session", &session) && session) {
} else if (options.Get("partition", &partition)) { } else if (options.Get("partition", &partition)) {
session = Session::FromPartition(isolate, partition); session = Session::FromPartition(isolate, partition);
} else { } else {
// Use the default session if not specified. // Use the default session if not specified.
session = Session::FromPartition(isolate, ""); session = Session::FromPartition(isolate, "");
} }
session_.Reset(isolate, session.ToV8()); session_ = session;
std::unique_ptr<content::WebContents> web_contents; std::unique_ptr<content::WebContents> web_contents;
if (is_guest()) { if (is_guest()) {
@ -886,7 +884,8 @@ WebContents::WebContents(v8::Isolate* isolate,
web_contents = content::WebContents::Create(params); web_contents = content::WebContents::Create(params);
} }
InitWithSessionAndOptions(isolate, std::move(web_contents), session, options); InitWithSessionAndOptions(isolate, std::move(web_contents),
session->browser_context(), options);
} }
void WebContents::InitZoomController(content::WebContents* web_contents, void WebContents::InitZoomController(content::WebContents* web_contents,
@ -907,10 +906,10 @@ void WebContents::InitZoomController(content::WebContents* web_contents,
void WebContents::InitWithSessionAndOptions( void WebContents::InitWithSessionAndOptions(
v8::Isolate* isolate, v8::Isolate* isolate,
std::unique_ptr<content::WebContents> owned_web_contents, std::unique_ptr<content::WebContents> owned_web_contents,
gin_helper::Handle<api::Session> session, ElectronBrowserContext* browser_context,
const gin_helper::Dictionary& options) { const gin_helper::Dictionary& options) {
Observe(owned_web_contents.get()); Observe(owned_web_contents.get());
InitWithWebContents(std::move(owned_web_contents), session->browser_context(), InitWithWebContents(std::move(owned_web_contents), browser_context,
is_guest()); is_guest());
inspectable_web_contents_->GetView()->SetDelegate(this); inspectable_web_contents_->GetView()->SetDelegate(this);
@ -3754,7 +3753,12 @@ v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow(
} }
v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) { v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, session_); v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> wrapper;
if (!session_->GetWrapper(isolate).ToLocal(&wrapper)) {
return v8::Null(isolate);
}
return v8::Local<v8::Value>::New(isolate, wrapper);
} }
content::WebContents* WebContents::HostWebContents() const { content::WebContents* WebContents::HostWebContents() const {

View file

@ -31,6 +31,7 @@
#include "content/public/common/stop_find_action.h" #include "content/public/common/stop_find_action.h"
#include "electron/buildflags/buildflags.h" #include "electron/buildflags/buildflags.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/save_page_handler.h" #include "shell/browser/api/save_page_handler.h"
#include "shell/browser/background_throttling_source.h" #include "shell/browser/background_throttling_source.h"
#include "shell/browser/event_emitter_mixin.h" #include "shell/browser/event_emitter_mixin.h"
@ -47,6 +48,7 @@
#include "shell/common/gin_helper/wrappable.h" #include "shell/common/gin_helper/wrappable.h"
#include "shell/common/web_contents_utility.mojom.h" #include "shell/common/web_contents_utility.mojom.h"
#include "ui/base/models/image_model.h" #include "ui/base/models/image_model.h"
#include "v8/include/cppgc/persistent.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/common/mojom/view_type.mojom-forward.h" #include "extensions/common/mojom/view_type.mojom-forward.h"
@ -479,7 +481,7 @@ class WebContents final : public ExclusiveAccessContext,
void InitWithSessionAndOptions( void InitWithSessionAndOptions(
v8::Isolate* isolate, v8::Isolate* isolate,
std::unique_ptr<content::WebContents> web_contents, std::unique_ptr<content::WebContents> web_contents,
gin_helper::Handle<class Session> session, ElectronBrowserContext* browser_context,
const gin_helper::Dictionary& options); const gin_helper::Dictionary& options);
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
@ -768,7 +770,7 @@ class WebContents final : public ExclusiveAccessContext,
// Update the html fullscreen flag in both browser and renderer. // Update the html fullscreen flag in both browser and renderer.
void UpdateHtmlApiFullscreen(bool fullscreen); void UpdateHtmlApiFullscreen(bool fullscreen);
v8::Global<v8::Value> session_; cppgc::Persistent<api::Session> session_;
v8::Global<v8::Value> devtools_web_contents_; v8::Global<v8::Value> devtools_web_contents_;
v8::Global<v8::Value> debugger_; v8::Global<v8::Value> debugger_;

View file

@ -44,68 +44,80 @@ void ElectronApiIPCHandlerImpl::OnConnectionError() {
void ElectronApiIPCHandlerImpl::Message(bool internal, void ElectronApiIPCHandlerImpl::Message(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments) { blink::CloneableMessage arguments) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal); auto event = MakeIPCEvent(isolate, session->Get(), internal);
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->Message(event, channel, std::move(arguments)); session->Get()->Message(event, channel, std::move(arguments));
}
} }
void ElectronApiIPCHandlerImpl::Invoke(bool internal, void ElectronApiIPCHandlerImpl::Invoke(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments, blink::CloneableMessage arguments,
InvokeCallback callback) { InvokeCallback callback) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal, std::move(callback)); auto event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->Invoke(event, channel, std::move(arguments)); session->Get()->Invoke(event, channel, std::move(arguments));
}
} }
void ElectronApiIPCHandlerImpl::ReceivePostMessage( void ElectronApiIPCHandlerImpl::ReceivePostMessage(
const std::string& channel, const std::string& channel,
blink::TransferableMessage message) { blink::TransferableMessage message) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, false); auto event = MakeIPCEvent(isolate, session->Get(), false);
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->ReceivePostMessage(event, channel, std::move(message)); session->Get()->ReceivePostMessage(event, channel, std::move(message));
}
} }
void ElectronApiIPCHandlerImpl::MessageSync(bool internal, void ElectronApiIPCHandlerImpl::MessageSync(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments, blink::CloneableMessage arguments,
MessageSyncCallback callback) { MessageSyncCallback callback) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal, std::move(callback)); auto event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->MessageSync(event, channel, std::move(arguments)); session->Get()->MessageSync(event, channel, std::move(arguments));
}
} }
void ElectronApiIPCHandlerImpl::MessageHost(const std::string& channel, void ElectronApiIPCHandlerImpl::MessageHost(const std::string& channel,
blink::CloneableMessage arguments) { blink::CloneableMessage arguments) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, false); auto event = MakeIPCEvent(isolate, session->Get(), false);
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->MessageHost(event, channel, std::move(arguments)); session->Get()->MessageHost(event, channel, std::move(arguments));
}
} }
content::RenderFrameHost* ElectronApiIPCHandlerImpl::GetRenderFrameHost() { content::RenderFrameHost* ElectronApiIPCHandlerImpl::GetRenderFrameHost() {
return content::RenderFrameHost::FromID(render_frame_host_id_); return content::RenderFrameHost::FromID(render_frame_host_id_);
} }
api::Session* ElectronApiIPCHandlerImpl::GetSession() { gin::WeakCell<api::Session>* ElectronApiIPCHandlerImpl::GetSession() {
auto* rfh = GetRenderFrameHost(); auto* rfh = GetRenderFrameHost();
return rfh ? api::Session::FromBrowserContext(rfh->GetBrowserContext()) return rfh ? api::Session::FromBrowserContext(rfh->GetBrowserContext())
: nullptr; : nullptr;

View file

@ -18,6 +18,11 @@ namespace content {
class RenderFrameHost; class RenderFrameHost;
} }
namespace gin {
template <typename T>
class WeakCell;
} // namespace gin
namespace electron { namespace electron {
class ElectronApiIPCHandlerImpl : public mojom::ElectronApiIPC, class ElectronApiIPCHandlerImpl : public mojom::ElectronApiIPC,
private content::WebContentsObserver { private content::WebContentsObserver {
@ -65,7 +70,7 @@ class ElectronApiIPCHandlerImpl : public mojom::ElectronApiIPC,
void OnConnectionError(); void OnConnectionError();
content::RenderFrameHost* GetRenderFrameHost(); content::RenderFrameHost* GetRenderFrameHost();
api::Session* GetSession(); gin::WeakCell<api::Session>* GetSession();
gin_helper::Handle<gin_helper::internal::Event> MakeIPCEvent( gin_helper::Handle<gin_helper::internal::Event> MakeIPCEvent(
v8::Isolate* isolate, v8::Isolate* isolate,

View file

@ -71,51 +71,61 @@ void ElectronApiSWIPCHandlerImpl::RemoteDisconnected() {
void ElectronApiSWIPCHandlerImpl::Message(bool internal, void ElectronApiSWIPCHandlerImpl::Message(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments) { blink::CloneableMessage arguments) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal); auto event = MakeIPCEvent(isolate, session->Get(), internal);
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->Message(event, channel, std::move(arguments)); session->Get()->Message(event, channel, std::move(arguments));
}
} }
void ElectronApiSWIPCHandlerImpl::Invoke(bool internal, void ElectronApiSWIPCHandlerImpl::Invoke(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments, blink::CloneableMessage arguments,
InvokeCallback callback) { InvokeCallback callback) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal, std::move(callback)); auto event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->Invoke(event, channel, std::move(arguments)); session->Get()->Invoke(event, channel, std::move(arguments));
}
} }
void ElectronApiSWIPCHandlerImpl::ReceivePostMessage( void ElectronApiSWIPCHandlerImpl::ReceivePostMessage(
const std::string& channel, const std::string& channel,
blink::TransferableMessage message) { blink::TransferableMessage message) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, false); auto event = MakeIPCEvent(isolate, session->Get(), false);
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->ReceivePostMessage(event, channel, std::move(message)); session->Get()->ReceivePostMessage(event, channel, std::move(message));
}
} }
void ElectronApiSWIPCHandlerImpl::MessageSync(bool internal, void ElectronApiSWIPCHandlerImpl::MessageSync(bool internal,
const std::string& channel, const std::string& channel,
blink::CloneableMessage arguments, blink::CloneableMessage arguments,
MessageSyncCallback callback) { MessageSyncCallback callback) {
auto* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session && session->Get()) {
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto event = MakeIPCEvent(isolate, session, internal, std::move(callback)); auto event =
MakeIPCEvent(isolate, session->Get(), internal, std::move(callback));
if (event.IsEmpty()) if (event.IsEmpty())
return; return;
session->MessageSync(event, channel, std::move(arguments)); session->Get()->MessageSync(event, channel, std::move(arguments));
}
} }
void ElectronApiSWIPCHandlerImpl::MessageHost( void ElectronApiSWIPCHandlerImpl::MessageHost(
@ -130,7 +140,7 @@ ElectronBrowserContext* ElectronApiSWIPCHandlerImpl::GetBrowserContext() {
return browser_context; return browser_context;
} }
api::Session* ElectronApiSWIPCHandlerImpl::GetSession() { gin::WeakCell<api::Session>* ElectronApiSWIPCHandlerImpl::GetSession() {
return api::Session::FromBrowserContext(GetBrowserContext()); return api::Session::FromBrowserContext(GetBrowserContext());
} }

View file

@ -19,6 +19,11 @@ namespace content {
class RenderProcessHost; class RenderProcessHost;
} }
namespace gin {
template <typename T>
class WeakCell;
} // namespace gin
namespace electron { namespace electron {
class ElectronBrowserContext; class ElectronBrowserContext;
@ -68,7 +73,7 @@ class ElectronApiSWIPCHandlerImpl : public mojom::ElectronApiIPC,
private: private:
ElectronBrowserContext* GetBrowserContext(); ElectronBrowserContext* GetBrowserContext();
api::Session* GetSession(); gin::WeakCell<api::Session>* GetSession();
gin_helper::Handle<gin_helper::internal::Event> MakeIPCEvent( gin_helper::Handle<gin_helper::internal::Event> MakeIPCEvent(
v8::Isolate* isolate, v8::Isolate* isolate,

View file

@ -56,13 +56,13 @@ class EventEmitterMixin {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
auto* wrapper_info = &(static_cast<T*>(this)->kWrapperInfo); auto* wrapper_info = &(static_cast<T*>(this)->kWrapperInfo);
v8::Local<v8::FunctionTemplate> constructor = v8::Local<v8::FunctionTemplate> constructor =
data->GetFunctionTemplate(wrapper_info); data->DeprecatedGetFunctionTemplate(wrapper_info);
if (constructor.IsEmpty()) { if (constructor.IsEmpty()) {
constructor = v8::FunctionTemplate::New(isolate); constructor = v8::FunctionTemplate::New(isolate);
constructor->SetClassName( constructor->SetClassName(
gin::StringToV8(isolate, static_cast<T*>(this)->GetTypeName())); gin::StringToV8(isolate, static_cast<T*>(this)->GetTypeName()));
constructor->Inherit(internal::GetEventEmitterTemplate(isolate)); constructor->Inherit(internal::GetEventEmitterTemplate(isolate));
data->SetFunctionTemplate(wrapper_info, constructor); data->DeprecatedSetFunctionTemplate(wrapper_info, constructor);
} }
return gin::ObjectTemplateBuilder(isolate, return gin::ObjectTemplateBuilder(isolate,
static_cast<T*>(this)->GetTypeName(), static_cast<T*>(this)->GetTypeName(),

View file

@ -732,8 +732,9 @@ void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
} }
if (should_block) { if (should_block) {
auto* session = gin::WeakCell<api::Session>* session =
electron::api::Session::FromBrowserContext(browser_context()); electron::api::Session::FromBrowserContext(browser_context());
if (session && session->Get()) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
v8::Local<v8::Object> details = v8::Local<v8::Object> details =
@ -742,11 +743,12 @@ void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
.Set("isDirectory", handle_type == HandleType::kDirectory) .Set("isDirectory", handle_type == HandleType::kDirectory)
.Set("path", path_info.path) .Set("path", path_info.path)
.Build(); .Build();
session->Emit( session->Get()->Emit(
"file-system-access-restricted", details, "file-system-access-restricted", details,
base::BindRepeating( base::BindRepeating(
&FileSystemAccessPermissionContext::OnRestrictedPathResult, &FileSystemAccessPermissionContext::OnRestrictedPathResult,
weak_factory_.GetWeakPtr(), path_info.path)); weak_factory_.GetWeakPtr(), path_info.path));
}
return; return;
} }

View file

@ -293,14 +293,15 @@ void HidChooserContext::RevokeDevicePermission(
} else { } else {
RevokeEphemeralDevicePermission(origin, device); RevokeEphemeralDevicePermission(origin, device);
} }
api::Session* session = api::Session::FromBrowserContext(browser_context_); gin::WeakCell<api::Session>* session =
if (session) { api::Session::FromBrowserContext(browser_context_);
if (session && session->Get()) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
auto details = gin_helper::Dictionary::CreateEmpty(isolate); auto details = gin_helper::Dictionary::CreateEmpty(isolate);
details.Set("device", device.Clone()); details.Set("device", device.Clone());
details.Set("origin", origin.Serialize()); details.Set("origin", origin.Serialize());
session->Emit("hid-device-revoked", details); session->Get()->Emit("hid-device-revoked", details);
} }
} }

View file

@ -124,7 +124,7 @@ const std::string& HidChooserController::PhysicalDeviceIdFromDeviceInfo(
: device.physical_device_id; : device.physical_device_id;
} }
api::Session* HidChooserController::GetSession() { gin::WeakCell<api::Session>* HidChooserController::GetSession() {
if (!web_contents()) { if (!web_contents()) {
return nullptr; return nullptr;
} }
@ -137,8 +137,8 @@ void HidChooserController::OnDeviceAdded(
return; return;
if (AddDeviceInfo(device)) { if (AddDeviceInfo(device)) {
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_); auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
@ -146,7 +146,7 @@ void HidChooserController::OnDeviceAdded(
.Set("device", device.Clone()) .Set("device", device.Clone())
.Set("frame", rfh) .Set("frame", rfh)
.Build(); .Build();
session->Emit("hid-device-added", details); session->Get()->Emit("hid-device-added", details);
} }
} }
} }
@ -156,8 +156,8 @@ void HidChooserController::OnDeviceRemoved(
if (!base::Contains(items_, PhysicalDeviceIdFromDeviceInfo(device))) if (!base::Contains(items_, PhysicalDeviceIdFromDeviceInfo(device)))
return; return;
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_); auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
@ -165,7 +165,7 @@ void HidChooserController::OnDeviceRemoved(
.Set("device", device.Clone()) .Set("device", device.Clone())
.Set("frame", rfh) .Set("frame", rfh)
.Build(); .Build();
session->Emit("hid-device-removed", details); session->Get()->Emit("hid-device-removed", details);
} }
RemoveDeviceInfo(device); RemoveDeviceInfo(device);
} }
@ -239,8 +239,8 @@ void HidChooserController::OnGotDevices(
observation_.Observe(chooser_context_.get()); observation_.Observe(chooser_context_.get());
bool prevent_default = false; bool prevent_default = false;
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_); auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
@ -248,8 +248,8 @@ void HidChooserController::OnGotDevices(
.Set("deviceList", devicesToDisplay) .Set("deviceList", devicesToDisplay)
.Set("frame", rfh) .Set("frame", rfh)
.Build(); .Build();
prevent_default = prevent_default = session->Get()->Emit(
session->Emit("select-hid-device", details, "select-hid-device", details,
base::BindRepeating(&HidChooserController::OnDeviceChosen, base::BindRepeating(&HidChooserController::OnDeviceChosen,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }

View file

@ -28,7 +28,9 @@ class WebContents;
namespace gin { namespace gin {
class Arguments; class Arguments;
} template <typename T>
class WeakCell;
} // namespace gin
namespace electron { namespace electron {
namespace api { namespace api {
@ -78,7 +80,7 @@ class HidChooserController
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
private: private:
api::Session* GetSession(); gin::WeakCell<api::Session>* GetSession();
void OnGotDevices(std::vector<device::mojom::HidDeviceInfoPtr> devices); void OnGotDevices(std::vector<device::mojom::HidDeviceInfoPtr> devices);
bool DisplayDevice(const device::mojom::HidDeviceInfo& device) const; bool DisplayDevice(const device::mojom::HidDeviceInfo& device) const;
bool FilterMatchesAny(const device::mojom::HidDeviceInfo& device) const; bool FilterMatchesAny(const device::mojom::HidDeviceInfo& device) const;

View file

@ -31,9 +31,10 @@ void NetworkHintsHandlerImpl::Preconnect(const url::SchemeHostPort& url,
if (!browser_context_) { if (!browser_context_) {
return; return;
} }
auto* session = electron::api::Session::FromBrowserContext(browser_context_); gin::WeakCell<electron::api::Session>* session =
if (session) { electron::api::Session::FromBrowserContext(browser_context_);
session->Emit("preconnect", url.GetURL(), allow_credentials); if (session && session->Get()) {
session->Get()->Emit("preconnect", url.GetURL(), allow_credentials);
} }
} }

View file

@ -149,17 +149,16 @@ void SerialChooserContext::RevokePortPermissionWebInitiated(
auto* web_contents = auto* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host); content::WebContents::FromRenderFrameHost(render_frame_host);
api::Session* session = gin::WeakCell<api::Session>* session =
api::Session::FromBrowserContext(web_contents->GetBrowserContext()); api::Session::FromBrowserContext(web_contents->GetBrowserContext());
if (session && session->Get()) {
if (session) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
auto details = gin_helper::Dictionary::CreateEmpty(isolate); auto details = gin_helper::Dictionary::CreateEmpty(isolate);
details.Set("port", it->second); details.Set("port", it->second);
details.SetGetter("frame", render_frame_host); details.SetGetter("frame", render_frame_host);
details.Set("origin", origin.Serialize()); details.Set("origin", origin.Serialize());
session->Emit("serial-port-revoked", details); session->Get()->Emit("serial-port-revoked", details);
} }
} }

View file

@ -143,7 +143,7 @@ SerialChooserController::~SerialChooserController() {
RunCallback(/*port=*/nullptr); RunCallback(/*port=*/nullptr);
} }
api::Session* SerialChooserController::GetSession() { gin::WeakCell<api::Session>* SerialChooserController::GetSession() {
if (!web_contents_) { if (!web_contents_) {
return nullptr; return nullptr;
} }
@ -180,9 +180,10 @@ void SerialChooserController::OnPortAdded(
ports_.push_back(port.Clone()); ports_.push_back(port.Clone());
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
session->Emit("serial-port-added", port.Clone(), web_contents_.get()); session->Get()->Emit("serial-port-added", port.Clone(),
web_contents_.get());
} }
} }
@ -191,8 +192,11 @@ void SerialChooserController::OnPortRemoved(
const auto it = std::ranges::find(ports_, port.token, const auto it = std::ranges::find(ports_, port.token,
&device::mojom::SerialPortInfo::token); &device::mojom::SerialPortInfo::token);
if (it != ports_.end()) { if (it != ports_.end()) {
if (api::Session* session = GetSession()) gin::WeakCell<api::Session>* session = GetSession();
session->Emit("serial-port-removed", port.Clone(), web_contents_.get()); if (session && session->Get()) {
session->Get()->Emit("serial-port-removed", port.Clone(),
web_contents_.get());
}
ports_.erase(it); ports_.erase(it);
} }
} }
@ -236,8 +240,9 @@ void SerialChooserController::OnGetDevices(
} }
bool prevent_default = false; bool prevent_default = false;
if (api::Session* session = GetSession()) { gin::WeakCell<api::Session>* session = GetSession();
prevent_default = session->Emit( if (session && session->Get()) {
prevent_default = session->Get()->Emit(
"select-serial-port", ports_, web_contents_.get(), "select-serial-port", ports_, web_contents_.get(),
base::BindRepeating(&SerialChooserController::OnDeviceChosen, base::BindRepeating(&SerialChooserController::OnDeviceChosen,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));

View file

@ -24,6 +24,11 @@ class RenderFrameHost;
class WebContents; class WebContents;
} // namespace content } // namespace content
namespace gin {
template <typename T>
class WeakCell;
} // namespace gin
namespace electron { namespace electron {
namespace api { namespace api {
@ -64,7 +69,7 @@ class SerialChooserController final
bool powered) override; bool powered) override;
private: private:
api::Session* GetSession(); gin::WeakCell<api::Session>* GetSession();
void GetDevices(); void GetDevices();
void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports); void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports);
bool DisplayDevice(const device::mojom::SerialPortInfo& port) const; bool DisplayDevice(const device::mojom::SerialPortInfo& port) const;

View file

@ -276,14 +276,15 @@ void UsbChooserContext::RevokeObjectPermissionInternal(
} }
} }
api::Session* session = api::Session::FromBrowserContext(browser_context_); gin::WeakCell<api::Session>* session =
if (session) { api::Session::FromBrowserContext(browser_context_);
if (session && session->Get()) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
auto details = gin_helper::Dictionary::CreateEmpty(isolate); auto details = gin_helper::Dictionary::CreateEmpty(isolate);
details.Set("device", object); details.Set("device", object);
details.Set("origin", origin.Serialize()); details.Set("origin", origin.Serialize());
session->Emit("usb-device-revoked", details); session->Get()->Emit("usb-device-revoked", details);
} }
} }

View file

@ -58,7 +58,7 @@ UsbChooserController::~UsbChooserController() {
RunCallback(/*device_info=*/nullptr); RunCallback(/*device_info=*/nullptr);
} }
api::Session* UsbChooserController::GetSession() { gin::WeakCell<api::Session>* UsbChooserController::GetSession() {
if (!web_contents()) { if (!web_contents()) {
return nullptr; return nullptr;
} }
@ -68,18 +68,20 @@ api::Session* UsbChooserController::GetSession() {
void UsbChooserController::OnDeviceAdded( void UsbChooserController::OnDeviceAdded(
const device::mojom::UsbDeviceInfo& device_info) { const device::mojom::UsbDeviceInfo& device_info) {
if (DisplayDevice(device_info)) { if (DisplayDevice(device_info)) {
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
session->Emit("usb-device-added", device_info.Clone(), web_contents()); session->Get()->Emit("usb-device-added", device_info.Clone(),
web_contents());
} }
} }
} }
void UsbChooserController::OnDeviceRemoved( void UsbChooserController::OnDeviceRemoved(
const device::mojom::UsbDeviceInfo& device_info) { const device::mojom::UsbDeviceInfo& device_info) {
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
session->Emit("usb-device-removed", device_info.Clone(), web_contents()); session->Get()->Emit("usb-device-removed", device_info.Clone(),
web_contents());
} }
} }
@ -114,8 +116,8 @@ void UsbChooserController::GotUsbDeviceList(
observation_.Observe(chooser_context_.get()); observation_.Observe(chooser_context_.get());
bool prevent_default = false; bool prevent_default = false;
api::Session* session = GetSession(); gin::WeakCell<api::Session>* session = GetSession();
if (session) { if (session && session->Get()) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_); auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope{isolate}; v8::HandleScope handle_scope{isolate};
@ -130,8 +132,8 @@ void UsbChooserController::GotUsbDeviceList(
.Set("frame", rfh) .Set("frame", rfh)
.Build(); .Build();
prevent_default = prevent_default = session->Get()->Emit(
session->Emit("select-usb-device", details, "select-usb-device", details,
base::BindRepeating(&UsbChooserController::OnDeviceChosen, base::BindRepeating(&UsbChooserController::OnDeviceChosen,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }

View file

@ -22,7 +22,9 @@ class WebContents;
namespace gin { namespace gin {
class Arguments; class Arguments;
} template <typename T>
class WeakCell;
} // namespace gin
namespace electron { namespace electron {
class ElectronUsbDelegate; class ElectronUsbDelegate;
@ -57,7 +59,7 @@ class UsbChooserController final : private UsbChooserContext::DeviceObserver,
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
private: private:
api::Session* GetSession(); gin::WeakCell<api::Session>* GetSession();
void GotUsbDeviceList(std::vector<device::mojom::UsbDeviceInfoPtr> devices); void GotUsbDeviceList(std::vector<device::mojom::UsbDeviceInfoPtr> devices);
bool DisplayDevice(const device::mojom::UsbDeviceInfo& device) const; bool DisplayDevice(const device::mojom::UsbDeviceInfo& device) const;
void RunCallback(device::mojom::UsbDeviceInfoPtr device_info); void RunCallback(device::mojom::UsbDeviceInfoPtr device_info);

View file

@ -577,11 +577,11 @@ gin::ObjectTemplateBuilder NativeImage::GetObjectTemplateBuilder(
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
auto* wrapper_info = &kWrapperInfo; auto* wrapper_info = &kWrapperInfo;
v8::Local<v8::FunctionTemplate> constructor = v8::Local<v8::FunctionTemplate> constructor =
data->GetFunctionTemplate(wrapper_info); data->DeprecatedGetFunctionTemplate(wrapper_info);
if (constructor.IsEmpty()) { if (constructor.IsEmpty()) {
constructor = v8::FunctionTemplate::New(isolate); constructor = v8::FunctionTemplate::New(isolate);
constructor->SetClassName(gin::StringToV8(isolate, GetTypeName())); constructor->SetClassName(gin::StringToV8(isolate, GetTypeName()));
data->SetFunctionTemplate(wrapper_info, constructor); data->DeprecatedSetFunctionTemplate(wrapper_info, constructor);
} }
return gin::ObjectTemplateBuilder(isolate, GetTypeName(), return gin::ObjectTemplateBuilder(isolate, GetTypeName(),
constructor->InstanceTemplate()) constructor->InstanceTemplate())

View file

@ -698,13 +698,14 @@ gin_helper::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
ElectronBrowserContext* browser_context = nullptr; ElectronBrowserContext* browser_context = nullptr;
if (electron::IsBrowserProcess()) { if (electron::IsBrowserProcess()) {
std::string partition; std::string partition;
gin_helper::Handle<Session> session; Session* session = nullptr;
if (!opts.Get("session", &session)) { if (!opts.Get("session", &session)) {
if (opts.Get("partition", &partition)) if (opts.Get("partition", &partition))
session = Session::FromPartition(args->isolate(), partition); session = Session::FromPartition(args->isolate(), partition);
else // default session else // default session
session = Session::FromPartition(args->isolate(), ""); session = Session::FromPartition(args->isolate(), "");
} }
if (session)
browser_context = session->browser_context(); browser_context = session->browser_context();
} }

View file

@ -45,7 +45,7 @@ class Constructible {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
auto* wrapper_info = &T::kWrapperInfo; auto* wrapper_info = &T::kWrapperInfo;
v8::Local<v8::FunctionTemplate> constructor = v8::Local<v8::FunctionTemplate> constructor =
data->GetFunctionTemplate(wrapper_info); data->DeprecatedGetFunctionTemplate(wrapper_info);
if (constructor.IsEmpty()) { if (constructor.IsEmpty()) {
constructor = gin::CreateConstructorFunctionTemplate( constructor = gin::CreateConstructorFunctionTemplate(
isolate, base::BindRepeating(&T::New)); isolate, base::BindRepeating(&T::New));
@ -59,6 +59,30 @@ class Constructible {
T::FillObjectTemplate(isolate, constructor->PrototypeTemplate()); T::FillObjectTemplate(isolate, constructor->PrototypeTemplate());
data->DeprecatedSetObjectTemplate(wrapper_info, data->DeprecatedSetObjectTemplate(wrapper_info,
constructor->InstanceTemplate()); constructor->InstanceTemplate());
data->DeprecatedSetFunctionTemplate(wrapper_info, constructor);
}
return constructor->GetFunction(context).ToLocalChecked();
}
static v8::Local<v8::Function> GetConstructor(
v8::Isolate* const isolate,
v8::Local<v8::Context> context,
gin::WrapperInfo* wrapper_info) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
v8::Local<v8::FunctionTemplate> constructor =
data->GetFunctionTemplate(wrapper_info);
if (constructor.IsEmpty()) {
constructor = gin::CreateConstructorFunctionTemplate(
isolate, base::BindRepeating(&T::New));
if (std::is_base_of<EventEmitterMixin<T>, T>::value) {
constructor->Inherit(
gin_helper::internal::GetEventEmitterTemplate(isolate));
}
constructor->InstanceTemplate()->SetInternalFieldCount(
gin::kNumberOfInternalFields);
constructor->SetClassName(gin::StringToV8(isolate, T::GetClassName()));
T::FillObjectTemplate(isolate, constructor->PrototypeTemplate());
data->SetObjectTemplate(wrapper_info, constructor->InstanceTemplate());
data->SetFunctionTemplate(wrapper_info, constructor); data->SetFunctionTemplate(wrapper_info, constructor);
} }
return constructor->GetFunction(context).ToLocalChecked(); return constructor->GetFunction(context).ToLocalChecked();

View file

@ -10,6 +10,7 @@
#include "base/containers/span.h" #include "base/containers/span.h"
#include "gin/converter.h" #include "gin/converter.h"
#include "gin/wrappable.h"
#include "shell/common/gin_converters/std_converter.h" // for ConvertToV8(iso, &&) #include "shell/common/gin_converters/std_converter.h" // for ConvertToV8(iso, &&)
#include "shell/common/gin_helper/wrappable.h" #include "shell/common/gin_helper/wrappable.h"
@ -76,6 +77,28 @@ v8::Local<v8::Value> CallMethod(gin_helper::DeprecatedWrappable<T>* object,
return CallMethod(isolate, object, method_name, std::forward<Args>(args)...); return CallMethod(isolate, object, method_name, std::forward<Args>(args)...);
} }
template <typename T, typename... Args>
v8::Local<v8::Value> CallMethod(v8::Isolate* isolate,
gin::Wrappable<T>* object,
const char* method_name,
Args&&... args) {
v8::EscapableHandleScope scope(isolate);
v8::Local<v8::Object> v8_object;
if (object->GetWrapper(isolate).ToLocal(&v8_object))
return scope.Escape(CustomEmit(isolate, v8_object, method_name,
std::forward<Args>(args)...));
else
return {};
}
template <typename T, typename... Args>
v8::Local<v8::Value> CallMethod(gin::Wrappable<T>* object,
const char* method_name,
Args&&... args) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
return CallMethod(isolate, object, method_name, std::forward<Args>(args)...);
}
} // namespace gin_helper } // namespace gin_helper
#endif // ELECTRON_SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_CALLER_H_ #endif // ELECTRON_SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_CALLER_H_

View file

@ -17,7 +17,7 @@ gin::DeprecatedWrapperInfo kWrapperInfo = {gin::kEmbedderNativeGin};
v8::Local<v8::FunctionTemplate> GetEventEmitterTemplate(v8::Isolate* isolate) { v8::Local<v8::FunctionTemplate> GetEventEmitterTemplate(v8::Isolate* isolate) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
v8::Local<v8::FunctionTemplate> tmpl = v8::Local<v8::FunctionTemplate> tmpl =
data->GetFunctionTemplate(&kWrapperInfo); data->DeprecatedGetFunctionTemplate(&kWrapperInfo);
if (tmpl.IsEmpty()) { if (tmpl.IsEmpty()) {
tmpl = v8::FunctionTemplate::New(isolate); tmpl = v8::FunctionTemplate::New(isolate);
@ -35,7 +35,7 @@ v8::Local<v8::FunctionTemplate> GetEventEmitterTemplate(v8::Isolate* isolate) {
->SetPrototypeV2(context, eventemitter_prototype) ->SetPrototypeV2(context, eventemitter_prototype)
.ToChecked()); .ToChecked());
data->SetFunctionTemplate(&kWrapperInfo, tmpl); data->DeprecatedSetFunctionTemplate(&kWrapperInfo, tmpl);
} }
return tmpl; return tmpl;

View file

@ -81,6 +81,7 @@ class CallbackHolderBase {
// gin::PerIsolateData::DisposeObserver // gin::PerIsolateData::DisposeObserver
void OnBeforeDispose(v8::Isolate* isolate) override; void OnBeforeDispose(v8::Isolate* isolate) override;
void OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) override {}
void OnDisposed() override; void OnDisposed() override;
private: private:

View file

@ -0,0 +1,36 @@
// 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_COMMON_GIN_HELPER_SELF_KEEP_ALIVE_H_
#define ELECTRON_SHELL_COMMON_GIN_HELPER_SELF_KEEP_ALIVE_H_
#include "gin/weak_cell.h"
namespace gin_helper {
// Based on third_party/blink/renderer/platform/heap/self_keep_alive.h
template <typename Self>
class SelfKeepAlive final {
GIN_DISALLOW_NEW();
public:
explicit SelfKeepAlive(Self* self) : keep_alive_(self) {}
SelfKeepAlive& operator=(Self* self) {
DCHECK(!keep_alive_ || keep_alive_.Get() == self);
keep_alive_ = self;
return *this;
}
void Clear() { keep_alive_.Clear(); }
explicit operator bool() const { return keep_alive_; }
private:
cppgc::Persistent<Self> keep_alive_;
};
} // namespace gin_helper
#endif // ELECTRON_SHELL_COMMON_GIN_HELPER_SELF_KEEP_ALIVE_H_

View file

@ -37,19 +37,19 @@ class Wrappable : public WrappableBase {
isolate, base::BindRepeating(&internal::InvokeNew<Sig>, constructor)); isolate, base::BindRepeating(&internal::InvokeNew<Sig>, constructor));
templ->InstanceTemplate()->SetInternalFieldCount(1); templ->InstanceTemplate()->SetInternalFieldCount(1);
T::BuildPrototype(isolate, templ); T::BuildPrototype(isolate, templ);
gin::PerIsolateData::From(isolate)->SetFunctionTemplate(&kWrapperInfo, gin::PerIsolateData::From(isolate)->DeprecatedSetFunctionTemplate(
templ); &kWrapperInfo, templ);
} }
static v8::Local<v8::FunctionTemplate> GetConstructor(v8::Isolate* isolate) { static v8::Local<v8::FunctionTemplate> GetConstructor(v8::Isolate* isolate) {
// Fill the object template. // Fill the object template.
auto* data = gin::PerIsolateData::From(isolate); auto* data = gin::PerIsolateData::From(isolate);
auto templ = data->GetFunctionTemplate(&kWrapperInfo); auto templ = data->DeprecatedGetFunctionTemplate(&kWrapperInfo);
if (templ.IsEmpty()) { if (templ.IsEmpty()) {
templ = v8::FunctionTemplate::New(isolate); templ = v8::FunctionTemplate::New(isolate);
templ->InstanceTemplate()->SetInternalFieldCount(1); templ->InstanceTemplate()->SetInternalFieldCount(1);
T::BuildPrototype(isolate, templ); T::BuildPrototype(isolate, templ);
data->SetFunctionTemplate(&kWrapperInfo, templ); data->DeprecatedSetFunctionTemplate(&kWrapperInfo, templ);
} }
return templ; return templ;
} }

View file

@ -27,19 +27,53 @@ describe('cpp heap', () => {
it('should record as node in heap snapshot', async () => { it('should record as node in heap snapshot', async () => {
const { remotely } = await startRemoteControlApp(['--expose-internals']); const { remotely } = await startRemoteControlApp(['--expose-internals']);
const [nodeCount, hasPersistentParent] = await remotely(async (heap: string) => { const result = await remotely(async (heap: string, snapshotHelper: string) => {
const { recordState } = require(heap); const { recordState } = require(heap);
const { containsRetainingPath } = require(snapshotHelper);
const state = recordState(); const state = recordState();
const rootNodes = state.snapshot.filter( return containsRetainingPath(state.snapshot, ['C++ Persistent roots', 'Electron / App']);
(node: any) => node.name === 'Electron / App' && node.type !== 'string'); }, path.join(__dirname, '../../third_party/electron_node/test/common/heap'),
const hasParent = rootNodes.some((node: any) => node.incomingEdges.some( path.join(__dirname, 'lib', 'heapsnapshot-helpers.js'));
(edge: any) => { expect(result).to.equal(true);
return edge.type === 'element' && edge.from.name === 'C++ Persistent roots'; });
})); });
return [rootNodes.length, hasParent];
}, path.join(__dirname, '../../third_party/electron_node/test/common/heap')); describe('session module', () => {
expect(nodeCount).to.equal(1); it('should record as node in heap snapshot', async () => {
expect(hasPersistentParent).to.be.true(); const { remotely } = await startRemoteControlApp(['--expose-internals']);
const result = await remotely(async (heap: string, snapshotHelper: string) => {
const { session, BrowserWindow } = require('electron');
const { once } = require('node:events');
const assert = require('node:assert');
const { recordState } = require(heap);
const { containsRetainingPath } = require(snapshotHelper);
const session1 = session.defaultSession;
console.log(session1.getStoragePath());
const session2 = session.fromPartition('cppheap1');
const session3 = session.fromPartition('cppheap1');
const session4 = session.fromPartition('cppheap2');
console.log(session2.cookies);
assert.strictEqual(session2, session3);
assert.notStrictEqual(session2, session4);
const w = new BrowserWindow({
show: false,
webPreferences: {
session: session.fromPartition('cppheap1')
}
});
await w.loadURL('about:blank');
const state = recordState();
const isClosed = once(w, 'closed');
w.destroy();
await isClosed;
const numSessions = containsRetainingPath(state.snapshot, ['C++ Persistent roots', 'Electron / Session'], {
occurrences: 4
});
const canTraceJSReferences = containsRetainingPath(state.snapshot, ['C++ Persistent roots', 'Electron / Session', 'Cookies']);
return numSessions && canTraceJSReferences;
}, path.join(__dirname, '../../third_party/electron_node/test/common/heap'),
path.join(__dirname, 'lib', 'heapsnapshot-helpers.js'));
expect(result).to.equal(true);
}); });
}); });
}); });

View file

@ -0,0 +1,25 @@
export function containsRetainingPath (snapshot, retainingPath, options) {
let root = snapshot.filter(
(node) => node.name === retainingPath[0] && node.type !== 'string');
for (let i = 1; i < retainingPath.length; i++) {
const needle = retainingPath[i];
const newRoot = [];
for (const node of root) {
for (let j = 0; j < node.outgoingEdges.length; j++) {
const child = node.outgoingEdges[j].to;
if (child.type === 'string') continue;
if (child.name === needle) {
newRoot.push(child);
}
}
}
if (!newRoot.length) {
console.log(`No retaining path found for ${needle}`);
return false;
}
root = newRoot;
}
return options?.occurrances
? root.length === options.occurrances
: true;
}