Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/browserView/electron-main/browserSession.ts
13397 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { session } from 'electron';
7
import { joinPath } from '../../../base/common/resources.js';
8
import { URI } from '../../../base/common/uri.js';
9
import { IApplicationStorageMainService } from '../../storage/electron-main/storageMainService.js';
10
import { BrowserViewStorageScope } from '../common/browserView.js';
11
import { BrowserSessionTrust, IBrowserSessionTrust } from './browserSessionTrust.js';
12
import { FileAccess } from '../../../base/common/network.js';
13
14
// Same as webviews, minus clipboard-read
15
const allowedPermissions = new Set([
16
'pointerLock',
17
'notifications',
18
'clipboard-sanitized-write'
19
]);
20
21
/**
22
* Holds an Electron session along with its storage scope and unique browser
23
* context identifier. Each instance maps one-to-one to an Electron
24
* {@link Electron.Session} -- the {@link id} is derived from what makes the
25
* Electron session unique (scope + workspace), **not** from any view id.
26
* Multiple browser views may reference the same `BrowserSession`.
27
*
28
* The class centralises the permission configuration. The {@link id}
29
* doubles as the CDP `browserContextId`.
30
*
31
* This class uses a private constructor with static factory methods
32
* ({@link getOrCreate}, {@link getOrCreateGlobal}, etc.) and maintains
33
* an internal registry of live sessions. Use the static methods to
34
* obtain instances.
35
*/
36
export class BrowserSession {
37
38
// #region Static registry
39
40
/**
41
* Primary store — keyed by Electron session so entries are
42
* automatically removed when the Electron session is GC'd.
43
*
44
* The goal is to ensure that BrowserSessions have the exact same lifespan as their Electron sessions.
45
*/
46
private static readonly _bySession = new WeakMap<Electron.Session, BrowserSession>();
47
48
/**
49
* String-keyed lookup for {@link get} and {@link getBrowserContextIds}.
50
* Values are weak references so they don't prevent GC of the
51
* {@link BrowserSession} (and transitively the Electron session).
52
*
53
* ID derivation rules (one-to-one with Electron sessions):
54
* - Global scope -> `"global"`
55
* - Workspace scope -> `"workspace:${workspaceId}"`
56
* - Ephemeral scope -> `"ephemeral:${viewId}"` or `"${type}:${viewId}"` for custom types
57
*/
58
private static readonly _byId = new Map<string, WeakRef<BrowserSession>>();
59
60
/**
61
* Cleans up stale {@link _byId} entries when the Electron session
62
* they point to is garbage-collected.
63
*/
64
private static readonly _finalizer = new FinalizationRegistry<string>((id) => {
65
BrowserSession._byId.delete(id);
66
});
67
68
/**
69
* Weak set mirroring the Electron sessions owned by any BrowserSession.
70
* Useful for quickly checking whether a given {@link Electron.WebContents}
71
* belongs to the integrated browser.
72
*/
73
static readonly knownSessions = new WeakSet<Electron.Session>();
74
75
/**
76
* Check if a {@link Electron.WebContents} belongs to an integrated browser
77
* view backed by a BrowserSession.
78
*/
79
static isBrowserViewWebContents(contents: Electron.WebContents): boolean {
80
return BrowserSession.knownSessions.has(contents.session);
81
}
82
83
/**
84
* Return an existing session for the given id, or `undefined`.
85
*/
86
static get(id: string): BrowserSession | undefined {
87
const ref = BrowserSession._byId.get(id);
88
if (!ref) {
89
return undefined;
90
}
91
const bs = ref.deref();
92
if (!bs) {
93
BrowserSession._byId.delete(id);
94
}
95
return bs;
96
}
97
98
/**
99
* Return all live browser context IDs (i.e. all session {@link id}s).
100
*/
101
static getBrowserContextIds(): string[] {
102
const ids: string[] = [];
103
for (const [id, ref] of BrowserSession._byId) {
104
if (ref.deref()) {
105
ids.push(id);
106
} else {
107
BrowserSession._byId.delete(id);
108
}
109
}
110
return ids;
111
}
112
113
/**
114
* Get or create the singleton global-scope session.
115
*/
116
static getOrCreateGlobal(): BrowserSession {
117
const electronSession = session.fromPartition('persist:vscode-browser');
118
return BrowserSession._bySession.get(electronSession)
119
?? new BrowserSession('global', electronSession, BrowserViewStorageScope.Global);
120
}
121
122
/**
123
* Get or create a workspace-scope session for the given workspace.
124
*/
125
static getOrCreateWorkspace(workspaceId: string, workspaceStorageHome: URI): BrowserSession {
126
const storage = joinPath(workspaceStorageHome, workspaceId, 'browserStorage');
127
const electronSession = session.fromPath(storage.fsPath);
128
return BrowserSession._bySession.get(electronSession)
129
?? new BrowserSession(`workspace:${workspaceId}`, electronSession, BrowserViewStorageScope.Workspace);
130
}
131
132
/**
133
* Get or create an ephemeral session for the given view / target id.
134
*/
135
static getOrCreateEphemeral(viewId: string, type?: string): BrowserSession {
136
if (type === 'workspace' || type === 'ephemeral') {
137
throw new Error(`Cannot create session with reserved type '${type}'`);
138
}
139
140
const sessionId = `${type ?? 'ephemeral'}:${viewId}`;
141
const electronSession = session.fromPartition(`vscode-browser-${type}${viewId}`);
142
return BrowserSession._bySession.get(electronSession)
143
?? new BrowserSession(sessionId, electronSession, BrowserViewStorageScope.Ephemeral);
144
}
145
146
/**
147
* Get or create a session for a workbench-originated browser view.
148
* The session id is derived from the *scope* -- not the view id -- so
149
* multiple views that share a scope (e.g. two Global views) get the
150
* same `BrowserSession`.
151
*
152
* @param viewId Used only for ephemeral sessions where every view
153
* needs its own Electron session.
154
* @param scope Desired storage scope.
155
* @param workspaceStorageHome Root folder under which per-workspace
156
* browser storage is created
157
* (`IEnvironmentMainService.workspaceStorageHome`).
158
* @param workspaceId Only required when `scope` is `workspace`.
159
*/
160
static getOrCreate(
161
viewId: string,
162
scope: BrowserViewStorageScope,
163
workspaceStorageHome: URI,
164
workspaceId?: string,
165
): BrowserSession {
166
switch (scope) {
167
case BrowserViewStorageScope.Global:
168
return BrowserSession.getOrCreateGlobal();
169
case BrowserViewStorageScope.Workspace:
170
if (workspaceId) {
171
return BrowserSession.getOrCreateWorkspace(workspaceId, workspaceStorageHome);
172
}
173
// fallthrough -- no workspace context -> ephemeral
174
case BrowserViewStorageScope.Ephemeral:
175
default:
176
return BrowserSession.getOrCreateEphemeral(viewId);
177
}
178
}
179
180
// #endregion
181
182
// #region Instance
183
184
private readonly _trust: BrowserSessionTrust;
185
186
private constructor(
187
/**
188
* Unique identifier for this session. Derived from what makes the
189
* underlying Electron session unique (scope key, workspace id, view
190
* id, or context uuid) -- NOT from any particular view id.
191
*/
192
readonly id: string,
193
/** The underlying Electron session. */
194
readonly electronSession: Electron.Session,
195
/** Resolved storage scope. */
196
readonly storageScope: BrowserViewStorageScope,
197
) {
198
this._trust = new BrowserSessionTrust(this);
199
this.configure();
200
BrowserSession.knownSessions.add(electronSession);
201
BrowserSession._bySession.set(electronSession, this);
202
BrowserSession._byId.set(id, new WeakRef(this));
203
BrowserSession._finalizer.register(electronSession, id);
204
}
205
206
/** Public trust interface for consumers that need cert operations. */
207
get trust(): IBrowserSessionTrust {
208
return this._trust;
209
}
210
211
/**
212
* Connect application storage to this session so that preferences
213
* (trusted certificates, permissions, etc.) are persisted across
214
* restarts. Restores any previously-saved data on first call;
215
* subsequent calls are no-ops.
216
*/
217
connectStorage(storage: IApplicationStorageMainService): void {
218
this._trust.connectStorage(storage);
219
}
220
221
/**
222
* Apply the permission policy and preload scripts to the session.
223
*/
224
private configure(): void {
225
this.electronSession.setPermissionRequestHandler((_webContents, permission, callback) => {
226
return callback(allowedPermissions.has(permission));
227
});
228
this.electronSession.setPermissionCheckHandler((_webContents, permission, _origin) => {
229
return allowedPermissions.has(permission);
230
});
231
this.electronSession.registerPreloadScript({
232
type: 'frame',
233
filePath: FileAccess.asFileUri('vs/platform/browserView/electron-browser/preload-browserView.js').fsPath
234
});
235
}
236
237
/**
238
* Clear all session data including trust state and all browsing data.
239
*/
240
async clearData(): Promise<void> {
241
await this._trust.clear();
242
await this.electronSession.clearData();
243
}
244
245
// #endregion
246
}
247
248