diff --git a/docs/api/session.md b/docs/api/session.md index 9554cb238e0b..6b5717733212 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -42,6 +42,22 @@ To create a `Session` with `options`, you have to ensure the `Session` with the `partition` has never been used before. There is no way to change the `options` of an existing `Session` object. +### `session.fromPath(path[, options])` + +* `path` string +* `options` Object (optional) + * `cache` boolean - Whether to enable cache. + +Returns `Session` - A session instance from the absolute path as specified by the `path` +string. When there is an existing `Session` with the same absolute path, it +will be returned; otherwise a new `Session` instance will be created with `options`. The +call will throw an error if the path is not an absolute path. Additionally, an error will +be thrown if an empty string is provided. + +To create a `Session` with `options`, you have to ensure the `Session` with the +`path` has never been used before. There is no way to change the `options` +of an existing `Session` object. + ## Properties The `session` module has the following properties: diff --git a/lib/browser/api/session.ts b/lib/browser/api/session.ts index 8184cd576474..ea74cc88730a 100644 --- a/lib/browser/api/session.ts +++ b/lib/browser/api/session.ts @@ -1,5 +1,5 @@ import { fetchWithSession } from '@electron/internal/browser/api/net-fetch'; -const { fromPartition, Session } = process._linkedBinding('electron_browser_session'); +const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session'); Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) { return fetchWithSession(input, init, this); @@ -7,6 +7,7 @@ Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) { export default { fromPartition, + fromPath, get defaultSession () { return fromPartition(''); } diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index 1dc28c474139..b80978ee899e 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -12,7 +12,9 @@ #include #include "base/command_line.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/guid.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -1206,6 +1208,30 @@ gin::Handle Session::FromPartition(v8::Isolate* isolate, return CreateFrom(isolate, browser_context); } +// static +absl::optional> Session::FromPath( + v8::Isolate* isolate, + const base::FilePath& path, + base::Value::Dict options) { + ElectronBrowserContext* browser_context; + + if (path.empty()) { + gin_helper::Promise> promise(isolate); + promise.RejectWithErrorMessage("An empty path was specified"); + return absl::nullopt; + } + if (!path.IsAbsolute()) { + gin_helper::Promise> promise(isolate); + promise.RejectWithErrorMessage("An absolute path was not provided"); + return absl::nullopt; + } + + browser_context = + ElectronBrowserContext::FromPath(std::move(path), std::move(options)); + + return CreateFrom(isolate, browser_context); +} + // static gin::Handle Session::New() { gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate()) @@ -1311,6 +1337,23 @@ v8::Local FromPartition(const std::string& partition, .ToV8(); } +v8::Local FromPath(const base::FilePath& path, + gin::Arguments* args) { + if (!electron::Browser::Get()->is_ready()) { + args->ThrowTypeError("Session can only be received when app is ready"); + return v8::Null(args->isolate()); + } + base::Value::Dict options; + args->GetNext(&options); + absl::optional> session_handle = + Session::FromPath(args->isolate(), path, std::move(options)); + + if (session_handle) + return session_handle.value().ToV8(); + else + return v8::Null(args->isolate()); +} + void Initialize(v8::Local exports, v8::Local unused, v8::Local context, @@ -1319,6 +1362,7 @@ void Initialize(v8::Local exports, gin_helper::Dictionary dict(isolate, exports); dict.Set("Session", Session::GetConstructor(context)); dict.SetMethod("fromPartition", &FromPartition); + dict.SetMethod("fromPath", &FromPath); } } // namespace diff --git a/shell/browser/api/electron_api_session.h b/shell/browser/api/electron_api_session.h index df6071def420..d80baaf6fb4e 100644 --- a/shell/browser/api/electron_api_session.h +++ b/shell/browser/api/electron_api_session.h @@ -82,6 +82,12 @@ class Session : public gin::Wrappable, const std::string& partition, base::Value::Dict options = {}); + // Gets the Session based on |path|. + static absl::optional> FromPath( + v8::Isolate* isolate, + const base::FilePath& path, + base::Value::Dict options = {}); + ElectronBrowserContext* browser_context() const { return browser_context_; } // gin::Wrappable diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 44df84670561..3331898adc13 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -106,9 +106,10 @@ ElectronBrowserContext::browser_context_map() { return *browser_context_map; } -ElectronBrowserContext::ElectronBrowserContext(const std::string& partition, - bool in_memory, - base::Value::Dict options) +ElectronBrowserContext::ElectronBrowserContext( + const PartitionOrPath partition_location, + bool in_memory, + base::Value::Dict options) : in_memory_pref_store_(new ValueMapPrefStore), storage_policy_(base::MakeRefCounted()), protocol_registry_(base::WrapUnique(new ProtocolRegistry)), @@ -124,11 +125,21 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition, base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize), &max_cache_size_); - base::PathService::Get(DIR_SESSION_DATA, &path_); - if (!in_memory && !partition.empty()) - path_ = path_.Append(FILE_PATH_LITERAL("Partitions")) - .Append(base::FilePath::FromUTF8Unsafe( - MakePartitionName(partition))); + if (auto* path_value = std::get_if>( + &partition_location)) { + base::PathService::Get(DIR_SESSION_DATA, &path_); + const std::string& partition_loc = path_value->get(); + if (!in_memory && !partition_loc.empty()) { + path_ = path_.Append(FILE_PATH_LITERAL("Partitions")) + .Append(base::FilePath::FromUTF8Unsafe( + MakePartitionName(partition_loc))); + } + } else if (auto* filepath_partition = + std::get_if>( + &partition_location)) { + const base::FilePath& partition_path = filepath_partition->get(); + path_ = std::move(partition_path); + } BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this); @@ -674,8 +685,25 @@ ElectronBrowserContext* ElectronBrowserContext::From( return browser_context; } + auto* new_context = new ElectronBrowserContext(std::cref(partition), + in_memory, std::move(options)); + browser_context_map()[key] = + std::unique_ptr(new_context); + return new_context; +} + +ElectronBrowserContext* ElectronBrowserContext::FromPath( + const base::FilePath& path, + base::Value::Dict options) { + PartitionKey key(path); + + ElectronBrowserContext* browser_context = browser_context_map()[key].get(); + if (browser_context) { + return browser_context; + } + auto* new_context = - new ElectronBrowserContext(partition, in_memory, std::move(options)); + new ElectronBrowserContext(std::cref(path), false, std::move(options)); browser_context_map()[key] = std::unique_ptr(new_context); return new_context; diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 06782ffa16b6..2774f0a9df7d 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -65,6 +65,9 @@ using DisplayMediaResponseCallbackJs = using DisplayMediaRequestHandler = base::RepeatingCallback; +using PartitionOrPath = + std::variant, + std::reference_wrapper>; class ElectronBrowserContext : public content::BrowserContext { public: @@ -74,22 +77,43 @@ class ElectronBrowserContext : public content::BrowserContext { // partition_id => browser_context struct PartitionKey { - std::string partition; + enum class KeyType { Partition, FilePath }; + std::string location; bool in_memory; + KeyType partition_type; PartitionKey(const std::string& partition, bool in_memory) - : partition(partition), in_memory(in_memory) {} + : location(partition), + in_memory(in_memory), + partition_type(KeyType::Partition) {} + explicit PartitionKey(const base::FilePath& file_path) + : location(file_path.AsUTF8Unsafe()), + in_memory(false), + partition_type(KeyType::FilePath) {} bool operator<(const PartitionKey& other) const { - if (partition == other.partition) - return in_memory < other.in_memory; - return partition < other.partition; + if (partition_type == KeyType::Partition) { + if (location == other.location) + return in_memory < other.in_memory; + return location < other.location; + } else { + if (location == other.location) + return false; + return location < other.location; + } } bool operator==(const PartitionKey& other) const { - return (partition == other.partition) && (in_memory == other.in_memory); + if (partition_type == KeyType::Partition) { + return (location == other.location) && (in_memory < other.in_memory); + } else { + if (location == other.location) + return true; + return false; + } } }; + using BrowserContextMap = std::map>; @@ -100,6 +124,12 @@ class ElectronBrowserContext : public content::BrowserContext { bool in_memory, base::Value::Dict options = {}); + // Get or create the BrowserContext using the |path|. + // The |options| will be passed to constructor when there is no + // existing BrowserContext. + static ElectronBrowserContext* FromPath(const base::FilePath& path, + base::Value::Dict options = {}); + static BrowserContextMap& browser_context_map(); void SetUserAgent(const std::string& user_agent); @@ -190,10 +220,12 @@ class ElectronBrowserContext : public content::BrowserContext { blink::PermissionType permissionType); private: - ElectronBrowserContext(const std::string& partition, + ElectronBrowserContext(const PartitionOrPath partition_location, bool in_memory, base::Value::Dict options); + ElectronBrowserContext(base::FilePath partition, base::Value::Dict options); + static void DisplayMediaDeviceChosen( const content::MediaStreamRequest& request, content::MediaResponseCallback callback, diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts index f1c587d4d154..18cf97a1f8e4 100644 --- a/spec/api-session-spec.ts +++ b/spec/api-session-spec.ts @@ -31,6 +31,14 @@ describe('session module', () => { }); }); + describe('session.fromPath(path)', () => { + it('returns storage path of a session which was created with an absolute path', () => { + const tmppath = require('electron').app.getPath('temp'); + const ses = session.fromPath(tmppath); + expect(ses.storagePath).to.equal(tmppath); + }); + }); + describe('ses.cookies', () => { const name = '0'; const value = '0'; diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index caa73ec1eb8e..edad6f9c9d5a 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -226,7 +226,7 @@ declare namespace NodeJS { _linkedBinding(name: 'electron_browser_power_save_blocker'): { powerSaveBlocker: Electron.PowerSaveBlocker }; _linkedBinding(name: 'electron_browser_push_notifications'): { pushNotifications: Electron.PushNotifications }; _linkedBinding(name: 'electron_browser_safe_storage'): { safeStorage: Electron.SafeStorage }; - _linkedBinding(name: 'electron_browser_session'): {fromPartition: typeof Electron.Session.fromPartition, Session: typeof Electron.Session}; + _linkedBinding(name: 'electron_browser_session'): {fromPartition: typeof Electron.Session.fromPartition, fromPath: typeof Electron.Session.fromPath, Session: typeof Electron.Session}; _linkedBinding(name: 'electron_browser_screen'): { createScreen(): Electron.Screen }; _linkedBinding(name: 'electron_browser_system_preferences'): { systemPreferences: Electron.SystemPreferences }; _linkedBinding(name: 'electron_browser_tray'): { Tray: Electron.Tray };