refactor: use gin::Wrappable for electron::api::DataPipeHolder (#49495)

* refactor: make `DataPipeHolder` inherit from `gin::Wrappable`

* test: add a test to ensure GC clears the data pipe holder

* chore: e patches all

* chore: e patches all (trivial only)

* refactor: make AllDataPipeHolders a base::flat_map of WeakPersistent
This commit is contained in:
Charles Kerr
2026-01-23 05:29:01 -06:00
committed by GitHub
parent 24526ccd39
commit 8c5c6a6088
6 changed files with 156 additions and 55 deletions

View File

@@ -7,6 +7,8 @@
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/map_util.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
@@ -14,10 +16,10 @@
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/net_errors.h"
#include "shell/common/gin_helper/handle.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/key_weak_map.h"
#include "shell/common/node_util.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
#include "shell/common/node_includes.h"
@@ -29,9 +31,11 @@ namespace {
int g_next_id = 0;
// Map that manages all the DataPipeHolder objects.
KeyWeakMap<std::string>& AllDataPipeHolders() {
static base::NoDestructor<KeyWeakMap<std::string>> weak_map;
return *weak_map.get();
[[nodiscard]] auto& AllDataPipeHolders() {
static base::NoDestructor<
base::flat_map<std::string, cppgc::WeakPersistent<DataPipeHolder>>>
weak_map;
return *weak_map;
}
// Utility class to read from data pipe.
@@ -143,8 +147,9 @@ class DataPipeReader {
} // namespace
gin::DeprecatedWrapperInfo DataPipeHolder::kWrapperInfo = {
gin::kEmbedderNativeGin};
const gin::WrapperInfo DataPipeHolder::kWrapperInfo = {
{gin::kEmbedderNativeGin},
gin::kElectronDataPipeHolder};
DataPipeHolder::DataPipeHolder(const network::DataElement& element)
: id_(base::NumberToString(++g_next_id)) {
@@ -166,30 +171,28 @@ v8::Local<v8::Promise> DataPipeHolder::ReadAll(v8::Isolate* isolate) {
return handle;
}
const char* DataPipeHolder::GetTypeName() {
return "DataPipeHolder";
const gin::WrapperInfo* DataPipeHolder::wrapper_info() const {
return &kWrapperInfo;
}
const char* DataPipeHolder::GetHumanReadableName() const {
return "Electron / DataPipeHolder";
}
// static
gin_helper::Handle<DataPipeHolder> DataPipeHolder::Create(
v8::Isolate* isolate,
const network::DataElement& element) {
auto handle = gin_helper::CreateHandle(isolate, new DataPipeHolder(element));
AllDataPipeHolders().Set(isolate, handle->id(),
handle->GetWrapper(isolate).ToLocalChecked());
return handle;
DataPipeHolder* DataPipeHolder::Create(v8::Isolate* isolate,
const network::DataElement& element) {
auto* holder = cppgc::MakeGarbageCollected<DataPipeHolder>(
isolate->GetCppHeap()->GetAllocationHandle(), element);
AllDataPipeHolders().insert_or_assign(holder->id(), holder);
return holder;
}
// static
gin_helper::Handle<DataPipeHolder> DataPipeHolder::From(v8::Isolate* isolate,
const std::string& id) {
v8::MaybeLocal<v8::Object> object = AllDataPipeHolders().Get(isolate, id);
if (!object.IsEmpty()) {
gin_helper::Handle<DataPipeHolder> handle;
if (gin::ConvertFromV8(isolate, object.ToLocalChecked(), &handle))
return handle;
}
return {};
DataPipeHolder* DataPipeHolder::From(v8::Isolate* isolate,
const std::string_view id) {
auto* found = base::FindOrNull(AllDataPipeHolders(), id);
return found ? found->Get() : nullptr;
}
} // namespace electron::api

View File

@@ -7,31 +7,28 @@
#include <string>
#include "gin/wrappable.h"
#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/gin_helper/wrappable.h"
namespace gin_helper {
template <typename T>
class Handle;
} // namespace gin_helper
namespace electron::api {
// Retains reference to the data pipe.
class DataPipeHolder final
: public gin_helper::DeprecatedWrappable<DataPipeHolder> {
class DataPipeHolder final : public gin::Wrappable<DataPipeHolder> {
public:
// gin_helper::Wrappable
static gin::DeprecatedWrapperInfo kWrapperInfo;
const char* GetTypeName() override;
// gin::Wrappable
static const gin::WrapperInfo kWrapperInfo;
const gin::WrapperInfo* wrapper_info() const override;
const char* GetHumanReadableName() const override;
static gin_helper::Handle<DataPipeHolder> Create(
v8::Isolate* isolate,
const network::DataElement& element);
static gin_helper::Handle<DataPipeHolder> From(v8::Isolate* isolate,
const std::string& id);
static DataPipeHolder* Create(v8::Isolate* isolate,
const network::DataElement& element);
static DataPipeHolder* From(v8::Isolate* isolate, std::string_view id);
// Make public for cppgc::MakeGarbageCollected.
explicit DataPipeHolder(const network::DataElement& element);
~DataPipeHolder() override;
// Read all data at once.
//
@@ -47,9 +44,6 @@ class DataPipeHolder final
DataPipeHolder& operator=(const DataPipeHolder&) = delete;
private:
explicit DataPipeHolder(const network::DataElement& element);
~DataPipeHolder() override;
std::string id_;
mojo::Remote<network::mojom::DataPipeGetter> data_pipe_;
};

View File

@@ -1044,15 +1044,13 @@ bool Session::IsPersistent() {
v8::Local<v8::Promise> Session::GetBlobData(v8::Isolate* isolate,
const std::string& uuid) {
gin_helper::Handle<DataPipeHolder> holder =
DataPipeHolder::From(isolate, uuid);
if (holder.IsEmpty()) {
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
promise.RejectWithErrorMessage("Could not get blob data handle");
return promise.GetHandle();
if (DataPipeHolder* holder = DataPipeHolder::From(isolate, uuid)) {
return holder->ReadAll(isolate);
}
return holder->ReadAll(isolate);
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
promise.RejectWithErrorMessage("Could not get blob data handle");
return promise.GetHandle();
}
void Session::DownloadURL(const GURL& url, gin::Arguments* args) {