feat: Allow usage of an absolute path for partitions used in a session (#37604)
* Allow an absolute path to be used for creating sessions Allows an absolute path to be used for creating sessions by adding the session.fromPath() API. * Fixup! Clarify that an emptry string is not permitted as a parameter to fromPath()
This commit is contained in:
parent
eb613ef3d4
commit
e0c348a2f8
8 changed files with 153 additions and 18 deletions
|
@ -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`
|
`partition` has never been used before. There is no way to change the `options`
|
||||||
of an existing `Session` object.
|
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
|
## Properties
|
||||||
|
|
||||||
The `session` module has the following properties:
|
The `session` module has the following properties:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
|
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) {
|
Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
|
||||||
return fetchWithSession(input, init, this);
|
return fetchWithSession(input, init, this);
|
||||||
|
@ -7,6 +7,7 @@ Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fromPartition,
|
fromPartition,
|
||||||
|
fromPath,
|
||||||
get defaultSession () {
|
get defaultSession () {
|
||||||
return fromPartition('');
|
return fromPartition('');
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
|
#include "base/files/file_enumerator.h"
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/files/file_util.h"
|
||||||
#include "base/guid.h"
|
#include "base/guid.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "base/strings/string_util.h"
|
#include "base/strings/string_util.h"
|
||||||
|
@ -1206,6 +1208,30 @@ gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
|
||||||
return CreateFrom(isolate, browser_context);
|
return CreateFrom(isolate, browser_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
absl::optional<gin::Handle<Session>> Session::FromPath(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
const base::FilePath& path,
|
||||||
|
base::Value::Dict options) {
|
||||||
|
ElectronBrowserContext* browser_context;
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
|
||||||
|
promise.RejectWithErrorMessage("An empty path was specified");
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
if (!path.IsAbsolute()) {
|
||||||
|
gin_helper::Promise<v8::Local<v8::Value>> 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
|
// static
|
||||||
gin::Handle<Session> Session::New() {
|
gin::Handle<Session> Session::New() {
|
||||||
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
|
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
|
||||||
|
@ -1311,6 +1337,23 @@ v8::Local<v8::Value> FromPartition(const std::string& partition,
|
||||||
.ToV8();
|
.ToV8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> 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<gin::Handle<Session>> 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<v8::Object> exports,
|
void Initialize(v8::Local<v8::Object> exports,
|
||||||
v8::Local<v8::Value> unused,
|
v8::Local<v8::Value> unused,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
|
@ -1319,6 +1362,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
gin_helper::Dictionary dict(isolate, exports);
|
gin_helper::Dictionary dict(isolate, exports);
|
||||||
dict.Set("Session", Session::GetConstructor(context));
|
dict.Set("Session", Session::GetConstructor(context));
|
||||||
dict.SetMethod("fromPartition", &FromPartition);
|
dict.SetMethod("fromPartition", &FromPartition);
|
||||||
|
dict.SetMethod("fromPath", &FromPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -82,6 +82,12 @@ class Session : public gin::Wrappable<Session>,
|
||||||
const std::string& partition,
|
const std::string& partition,
|
||||||
base::Value::Dict options = {});
|
base::Value::Dict options = {});
|
||||||
|
|
||||||
|
// Gets the Session based on |path|.
|
||||||
|
static absl::optional<gin::Handle<Session>> FromPath(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
const base::FilePath& path,
|
||||||
|
base::Value::Dict options = {});
|
||||||
|
|
||||||
ElectronBrowserContext* browser_context() const { return browser_context_; }
|
ElectronBrowserContext* browser_context() const { return browser_context_; }
|
||||||
|
|
||||||
// gin::Wrappable
|
// gin::Wrappable
|
||||||
|
|
|
@ -106,9 +106,10 @@ ElectronBrowserContext::browser_context_map() {
|
||||||
return *browser_context_map;
|
return *browser_context_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
|
ElectronBrowserContext::ElectronBrowserContext(
|
||||||
bool in_memory,
|
const PartitionOrPath partition_location,
|
||||||
base::Value::Dict options)
|
bool in_memory,
|
||||||
|
base::Value::Dict options)
|
||||||
: in_memory_pref_store_(new ValueMapPrefStore),
|
: in_memory_pref_store_(new ValueMapPrefStore),
|
||||||
storage_policy_(base::MakeRefCounted<SpecialStoragePolicy>()),
|
storage_policy_(base::MakeRefCounted<SpecialStoragePolicy>()),
|
||||||
protocol_registry_(base::WrapUnique(new ProtocolRegistry)),
|
protocol_registry_(base::WrapUnique(new ProtocolRegistry)),
|
||||||
|
@ -124,11 +125,21 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
|
||||||
base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize),
|
base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize),
|
||||||
&max_cache_size_);
|
&max_cache_size_);
|
||||||
|
|
||||||
base::PathService::Get(DIR_SESSION_DATA, &path_);
|
if (auto* path_value = std::get_if<std::reference_wrapper<const std::string>>(
|
||||||
if (!in_memory && !partition.empty())
|
&partition_location)) {
|
||||||
path_ = path_.Append(FILE_PATH_LITERAL("Partitions"))
|
base::PathService::Get(DIR_SESSION_DATA, &path_);
|
||||||
.Append(base::FilePath::FromUTF8Unsafe(
|
const std::string& partition_loc = path_value->get();
|
||||||
MakePartitionName(partition)));
|
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<std::reference_wrapper<const base::FilePath>>(
|
||||||
|
&partition_location)) {
|
||||||
|
const base::FilePath& partition_path = filepath_partition->get();
|
||||||
|
path_ = std::move(partition_path);
|
||||||
|
}
|
||||||
|
|
||||||
BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this);
|
BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this);
|
||||||
|
|
||||||
|
@ -674,8 +685,25 @@ ElectronBrowserContext* ElectronBrowserContext::From(
|
||||||
return browser_context;
|
return browser_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* new_context = new ElectronBrowserContext(std::cref(partition),
|
||||||
|
in_memory, std::move(options));
|
||||||
|
browser_context_map()[key] =
|
||||||
|
std::unique_ptr<ElectronBrowserContext>(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 =
|
auto* new_context =
|
||||||
new ElectronBrowserContext(partition, in_memory, std::move(options));
|
new ElectronBrowserContext(std::cref(path), false, std::move(options));
|
||||||
browser_context_map()[key] =
|
browser_context_map()[key] =
|
||||||
std::unique_ptr<ElectronBrowserContext>(new_context);
|
std::unique_ptr<ElectronBrowserContext>(new_context);
|
||||||
return new_context;
|
return new_context;
|
||||||
|
|
|
@ -65,6 +65,9 @@ using DisplayMediaResponseCallbackJs =
|
||||||
using DisplayMediaRequestHandler =
|
using DisplayMediaRequestHandler =
|
||||||
base::RepeatingCallback<void(const content::MediaStreamRequest&,
|
base::RepeatingCallback<void(const content::MediaStreamRequest&,
|
||||||
DisplayMediaResponseCallbackJs)>;
|
DisplayMediaResponseCallbackJs)>;
|
||||||
|
using PartitionOrPath =
|
||||||
|
std::variant<std::reference_wrapper<const std::string>,
|
||||||
|
std::reference_wrapper<const base::FilePath>>;
|
||||||
|
|
||||||
class ElectronBrowserContext : public content::BrowserContext {
|
class ElectronBrowserContext : public content::BrowserContext {
|
||||||
public:
|
public:
|
||||||
|
@ -74,22 +77,43 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||||
|
|
||||||
// partition_id => browser_context
|
// partition_id => browser_context
|
||||||
struct PartitionKey {
|
struct PartitionKey {
|
||||||
std::string partition;
|
enum class KeyType { Partition, FilePath };
|
||||||
|
std::string location;
|
||||||
bool in_memory;
|
bool in_memory;
|
||||||
|
KeyType partition_type;
|
||||||
|
|
||||||
PartitionKey(const std::string& partition, bool in_memory)
|
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 {
|
bool operator<(const PartitionKey& other) const {
|
||||||
if (partition == other.partition)
|
if (partition_type == KeyType::Partition) {
|
||||||
return in_memory < other.in_memory;
|
if (location == other.location)
|
||||||
return partition < other.partition;
|
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 {
|
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 =
|
using BrowserContextMap =
|
||||||
std::map<PartitionKey, std::unique_ptr<ElectronBrowserContext>>;
|
std::map<PartitionKey, std::unique_ptr<ElectronBrowserContext>>;
|
||||||
|
|
||||||
|
@ -100,6 +124,12 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||||
bool in_memory,
|
bool in_memory,
|
||||||
base::Value::Dict options = {});
|
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();
|
static BrowserContextMap& browser_context_map();
|
||||||
|
|
||||||
void SetUserAgent(const std::string& user_agent);
|
void SetUserAgent(const std::string& user_agent);
|
||||||
|
@ -190,10 +220,12 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||||
blink::PermissionType permissionType);
|
blink::PermissionType permissionType);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ElectronBrowserContext(const std::string& partition,
|
ElectronBrowserContext(const PartitionOrPath partition_location,
|
||||||
bool in_memory,
|
bool in_memory,
|
||||||
base::Value::Dict options);
|
base::Value::Dict options);
|
||||||
|
|
||||||
|
ElectronBrowserContext(base::FilePath partition, base::Value::Dict options);
|
||||||
|
|
||||||
static void DisplayMediaDeviceChosen(
|
static void DisplayMediaDeviceChosen(
|
||||||
const content::MediaStreamRequest& request,
|
const content::MediaStreamRequest& request,
|
||||||
content::MediaResponseCallback callback,
|
content::MediaResponseCallback callback,
|
||||||
|
|
|
@ -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', () => {
|
describe('ses.cookies', () => {
|
||||||
const name = '0';
|
const name = '0';
|
||||||
const value = '0';
|
const value = '0';
|
||||||
|
|
2
typings/internal-ambient.d.ts
vendored
2
typings/internal-ambient.d.ts
vendored
|
@ -226,7 +226,7 @@ declare namespace NodeJS {
|
||||||
_linkedBinding(name: 'electron_browser_power_save_blocker'): { powerSaveBlocker: Electron.PowerSaveBlocker };
|
_linkedBinding(name: 'electron_browser_power_save_blocker'): { powerSaveBlocker: Electron.PowerSaveBlocker };
|
||||||
_linkedBinding(name: 'electron_browser_push_notifications'): { pushNotifications: Electron.PushNotifications };
|
_linkedBinding(name: 'electron_browser_push_notifications'): { pushNotifications: Electron.PushNotifications };
|
||||||
_linkedBinding(name: 'electron_browser_safe_storage'): { safeStorage: Electron.SafeStorage };
|
_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_screen'): { createScreen(): Electron.Screen };
|
||||||
_linkedBinding(name: 'electron_browser_system_preferences'): { systemPreferences: Electron.SystemPreferences };
|
_linkedBinding(name: 'electron_browser_system_preferences'): { systemPreferences: Electron.SystemPreferences };
|
||||||
_linkedBinding(name: 'electron_browser_tray'): { Tray: Electron.Tray };
|
_linkedBinding(name: 'electron_browser_tray'): { Tray: Electron.Tray };
|
||||||
|
|
Loading…
Reference in a new issue