refactor: Add ElectronBrowserContext::BrowserContexts() (35-x-y backport) (#46161)

refactor: Add `ElectronBrowserContext::BrowserContexts()`

* refactor: add ElectronBrowserContext::BrowserContexts()

* refactor: use ElectronBrowserContext::BrowserContexts() in ElectronBrowserMainParts::PostMainMessageLoopRun()

* refactor: use ElectronBrowserContext::BrowserContexts() in ElectronExtensionsBrowserClient::IsValidContext()

* refactor: use ElectronBrowserContext::BrowserContexts() in ElectronExtensionsBrowserClient::BroadcastEventToRenderers()

* refactor: move PartitionKey, BrowserContextMap private

* refactor: add ElectronBrowserContext::IsValidContext()

decouple ElectronExtensionsBrowserClient from the internals of ElectronBrowserContext
This commit is contained in:
Charles Kerr 2025-03-21 10:50:16 -05:00 committed by GitHub
parent e3d95bb8a4
commit 998de7aa6c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 58 additions and 51 deletions

View file

@ -12,6 +12,7 @@
#include "base/barrier_closure.h" #include "base/barrier_closure.h"
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/path_service.h" #include "base/path_service.h"
@ -308,14 +309,53 @@ bool DoesDeviceMatch(const base::Value& device,
return false; return false;
} }
// partition_id => browser_context
struct PartitionKey {
PartitionKey(const std::string_view partition, bool in_memory)
: type_{Type::Partition}, location_{partition}, in_memory_{in_memory} {}
explicit PartitionKey(const base::FilePath& file_path)
: type_{Type::Path},
location_{file_path.AsUTF8Unsafe()},
in_memory_{false} {}
friend auto operator<=>(const PartitionKey&, const PartitionKey&) = default;
private:
enum class Type { Partition, Path };
Type type_;
std::string location_;
bool in_memory_;
};
[[nodiscard]] auto& ContextMap() {
static base::NoDestructor<
std::map<PartitionKey, std::unique_ptr<ElectronBrowserContext>>>
map;
return *map;
}
} // namespace } // namespace
// static // static
ElectronBrowserContext::BrowserContextMap& std::vector<ElectronBrowserContext*> ElectronBrowserContext::BrowserContexts() {
ElectronBrowserContext::browser_context_map() { return base::ToVector(ContextMap(),
static base::NoDestructor<ElectronBrowserContext::BrowserContextMap> [](auto& iter) { return iter.second.get(); });
browser_context_map; }
return *browser_context_map;
bool ElectronBrowserContext::IsValidContext(const void* context) {
return std::ranges::any_of(ContextMap(), [context](const auto& iter) {
return iter.second.get() == context;
});
}
// static
void ElectronBrowserContext::DestroyAllContexts() {
auto& map = ContextMap();
// Avoid UAF by destroying the default context last. See ba629e3 for info.
const auto extracted = map.extract(PartitionKey{"", false});
map.clear();
} }
ElectronBrowserContext::ElectronBrowserContext( ElectronBrowserContext::ElectronBrowserContext(
@ -833,7 +873,7 @@ ElectronBrowserContext* ElectronBrowserContext::From(
const std::string& partition, const std::string& partition,
bool in_memory, bool in_memory,
base::Value::Dict options) { base::Value::Dict options) {
auto& context = browser_context_map()[PartitionKey(partition, in_memory)]; auto& context = ContextMap()[PartitionKey(partition, in_memory)];
if (!context) { if (!context) {
context.reset(new ElectronBrowserContext{std::cref(partition), in_memory, context.reset(new ElectronBrowserContext{std::cref(partition), in_memory,
std::move(options)}); std::move(options)});
@ -844,7 +884,7 @@ ElectronBrowserContext* ElectronBrowserContext::From(
ElectronBrowserContext* ElectronBrowserContext::FromPath( ElectronBrowserContext* ElectronBrowserContext::FromPath(
const base::FilePath& path, const base::FilePath& path,
base::Value::Dict options) { base::Value::Dict options) {
auto& context = browser_context_map()[PartitionKey(path)]; auto& context = ContextMap()[PartitionKey(path)];
if (!context) { if (!context) {
context.reset( context.reset(
new ElectronBrowserContext{std::cref(path), false, std::move(options)}); new ElectronBrowserContext{std::cref(path), false, std::move(options)});

View file

@ -61,28 +61,9 @@ class ElectronBrowserContext : public content::BrowserContext {
ElectronBrowserContext(const ElectronBrowserContext&) = delete; ElectronBrowserContext(const ElectronBrowserContext&) = delete;
ElectronBrowserContext& operator=(const ElectronBrowserContext&) = delete; ElectronBrowserContext& operator=(const ElectronBrowserContext&) = delete;
// partition_id => browser_context [[nodiscard]] static std::vector<ElectronBrowserContext*> BrowserContexts();
struct PartitionKey {
PartitionKey(const std::string_view partition, bool in_memory)
: type_{Type::Partition}, location_{partition}, in_memory_{in_memory} {}
explicit PartitionKey(const base::FilePath& file_path) [[nodiscard]] static bool IsValidContext(const void* context);
: type_{Type::Path},
location_{file_path.AsUTF8Unsafe()},
in_memory_{false} {}
friend auto operator<=>(const PartitionKey&, const PartitionKey&) = default;
private:
enum class Type { Partition, Path };
Type type_;
std::string location_;
bool in_memory_;
};
using BrowserContextMap =
std::map<PartitionKey, std::unique_ptr<ElectronBrowserContext>>;
// Get or create the BrowserContext according to its |partition| and // Get or create the BrowserContext according to its |partition| and
// |in_memory|. The |options| will be passed to constructor when there is no // |in_memory|. The |options| will be passed to constructor when there is no
@ -97,7 +78,7 @@ class ElectronBrowserContext : public content::BrowserContext {
static ElectronBrowserContext* FromPath(const base::FilePath& path, static ElectronBrowserContext* FromPath(const base::FilePath& path,
base::Value::Dict options = {}); base::Value::Dict options = {});
static BrowserContextMap& browser_context_map(); static void DestroyAllContexts();
void SetUserAgent(const std::string& user_agent); void SetUserAgent(const std::string& user_agent);
std::string GetUserAgent() const; std::string GetUserAgent() const;

View file

@ -554,12 +554,10 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
// Shutdown the DownloadManager before destroying Node to prevent // Shutdown the DownloadManager before destroying Node to prevent
// DownloadItem callbacks from crashing. // DownloadItem callbacks from crashing.
for (auto& iter : ElectronBrowserContext::browser_context_map()) { for (auto* browser_context : ElectronBrowserContext::BrowserContexts()) {
auto* download_manager = iter.second.get()->GetDownloadManager(); if (auto* download_manager = browser_context->GetDownloadManager())
if (download_manager) {
download_manager->Shutdown(); download_manager->Shutdown();
} }
}
// Shutdown utility process created with Electron API before // Shutdown utility process created with Electron API before
// stopping Node.js so that exit events can be emitted. We don't let // stopping Node.js so that exit events can be emitted. We don't let
@ -598,11 +596,7 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
node_bindings_->set_uv_env(nullptr); node_bindings_->set_uv_env(nullptr);
node_env_.reset(); node_env_.reset();
auto default_context_key = ElectronBrowserContext::PartitionKey("", false); ElectronBrowserContext::DestroyAllContexts();
std::unique_ptr<ElectronBrowserContext> default_context = std::move(
ElectronBrowserContext::browser_context_map()[default_context_key]);
ElectronBrowserContext::browser_context_map().clear();
default_context.reset();
fake_browser_process_->PostMainMessageLoopRun(); fake_browser_process_->PostMainMessageLoopRun();
content::DevToolsAgentHost::StopRemoteDebuggingPipeHandler(); content::DevToolsAgentHost::StopRemoteDebuggingPipeHandler();

View file

@ -85,12 +85,7 @@ bool ElectronExtensionsBrowserClient::AreExtensionsDisabled(
} }
bool ElectronExtensionsBrowserClient::IsValidContext(void* context) { bool ElectronExtensionsBrowserClient::IsValidContext(void* context) {
auto& context_map = ElectronBrowserContext::browser_context_map(); return ElectronBrowserContext::IsValidContext(context);
for (auto const& entry : context_map) {
if (entry.second && entry.second.get() == context)
return true;
}
return false;
} }
bool ElectronExtensionsBrowserClient::IsSameContext(BrowserContext* first, bool ElectronExtensionsBrowserClient::IsSameContext(BrowserContext* first,
@ -341,14 +336,11 @@ void ElectronExtensionsBrowserClient::BroadcastEventToRenderers(
return; return;
} }
for (auto const& [key, browser_context] : for (auto* browser_context : ElectronBrowserContext::BrowserContexts()) {
ElectronBrowserContext::browser_context_map()) { extensions::EventRouter::Get(browser_context)
if (browser_context) {
extensions::EventRouter::Get(browser_context.get())
->BroadcastEvent(std::make_unique<extensions::Event>( ->BroadcastEvent(std::make_unique<extensions::Event>(
histogram_value, event_name, args.Clone())); histogram_value, event_name, args.Clone()));
} }
}
} }
extensions::ExtensionCache* extensions::ExtensionCache*