5
5
6
6
import { isSafari } from 'vs/base/browser/browser' ;
7
7
import { IndexedDB } from 'vs/base/browser/indexedDB' ;
8
- import { Promises } from 'vs/base/common/async' ;
8
+ import { DeferredPromise , Promises } from 'vs/base/common/async' ;
9
9
import { toErrorMessage } from 'vs/base/common/errorMessage' ;
10
10
import { Emitter } from 'vs/base/common/event' ;
11
- import { Disposable , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
11
+ import { Disposable , DisposableStore , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
12
+ import { assertIsDefined } from 'vs/base/common/types' ;
12
13
import { InMemoryStorageDatabase , isStorageItemsChangeEvent , IStorage , IStorageDatabase , IStorageItemsChangeEvent , IUpdateRequest , Storage } from 'vs/base/parts/storage/common/storage' ;
13
14
import { ILogService } from 'vs/platform/log/common/log' ;
14
15
import { AbstractStorageService , IS_NEW_KEY , StorageScope , StorageTarget } from 'vs/platform/storage/common/storage' ;
15
- import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' ;
16
+ import { IUserDataProfile , IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' ;
16
17
import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace' ;
17
18
18
19
export class BrowserStorageService extends AbstractStorageService {
19
20
20
21
private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000 ; // every 5s because async operations are not permitted on shutdown
21
22
22
23
private applicationStorage : IStorage | undefined ;
23
- private globalStorage : IStorage | undefined ;
24
- private workspaceStorage : IStorage | undefined ;
25
-
26
24
private applicationStorageDatabase : IIndexedDBStorageDatabase | undefined ;
25
+ private readonly applicationStoragePromise = new DeferredPromise < { indededDb : IIndexedDBStorageDatabase ; storage : IStorage } > ( ) ;
26
+
27
+ private globalStorage : IStorage | undefined ;
27
28
private globalStorageDatabase : IIndexedDBStorageDatabase | undefined ;
29
+ private globalStorageProfile : IUserDataProfile ;
30
+ private readonly globalStorageDisposables = this . _register ( new DisposableStore ( ) ) ;
31
+
32
+ private workspaceStorage : IStorage | undefined ;
28
33
private workspaceStorageDatabase : IIndexedDBStorageDatabase | undefined ;
29
34
30
35
get hasPendingUpdate ( ) : boolean {
@@ -38,20 +43,22 @@ export class BrowserStorageService extends AbstractStorageService {
38
43
constructor (
39
44
private readonly payload : IAnyWorkspaceIdentifier ,
40
45
@ILogService private readonly logService : ILogService ,
41
- @IUserDataProfilesService private readonly userDataProfileService : IUserDataProfilesService
46
+ @IUserDataProfilesService userDataProfileService : IUserDataProfilesService
42
47
) {
43
48
super ( { flushInterval : BrowserStorageService . BROWSER_DEFAULT_FLUSH_INTERVAL } ) ;
49
+
50
+ this . globalStorageProfile = userDataProfileService . currentProfile ;
44
51
}
45
52
46
53
private getId ( scope : StorageScope ) : string {
47
54
switch ( scope ) {
48
55
case StorageScope . APPLICATION :
49
56
return 'global' ; // use the default profile global DB for application scope
50
57
case StorageScope . GLOBAL :
51
- if ( this . userDataProfileService . currentProfile . isDefault ) {
58
+ if ( this . globalStorageProfile . isDefault ) {
52
59
return 'global' ; // default profile DB has a fixed name for backwards compatibility
53
60
} else {
54
- return `global-${ this . userDataProfileService . currentProfile . id } ` ;
61
+ return `global-${ this . globalStorageProfile . id } ` ;
55
62
}
56
63
case StorageScope . WORKSPACE :
57
64
return this . payload . id ;
@@ -60,55 +67,81 @@ export class BrowserStorageService extends AbstractStorageService {
60
67
61
68
protected async doInitialize ( ) : Promise < void > {
62
69
63
- // Create Storage in Parallel
64
- const promises : Promise < IIndexedDBStorageDatabase > [ ] = [ ] ;
65
- promises . push ( IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . APPLICATION ) , broadcastChanges : true } , this . logService ) ) ;
66
- promises . push ( IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . WORKSPACE ) } , this . logService ) ) ;
67
- if ( ! this . userDataProfileService . currentProfile . isDefault ) {
70
+ // Init storages
71
+ await Promises . settled ( [
72
+ this . createApplicationStorage ( ) ,
73
+ this . createGlobalStorage ( this . globalStorageProfile ) ,
74
+ this . createWorkspaceStorage ( )
75
+ ] ) ;
76
+ }
77
+
78
+ private async createApplicationStorage ( ) : Promise < void > {
79
+ const applicationStorageIndexedDB = await IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . APPLICATION ) , broadcastChanges : true } , this . logService ) ;
80
+
81
+ this . applicationStorageDatabase = this . _register ( applicationStorageIndexedDB ) ;
82
+ this . applicationStorage = this . _register ( new Storage ( this . applicationStorageDatabase ) ) ;
83
+
84
+ this . _register ( this . applicationStorage . onDidChangeStorage ( key => this . emitDidChangeValue ( StorageScope . APPLICATION , key ) ) ) ;
85
+
86
+ await this . applicationStorage . init ( ) ;
87
+
88
+ this . updateIsNew ( this . applicationStorage ) ;
89
+
90
+ this . applicationStoragePromise . complete ( { indededDb : applicationStorageIndexedDB , storage : this . applicationStorage } ) ;
91
+ }
92
+
93
+ private async createGlobalStorage ( profile : IUserDataProfile ) : Promise < void > {
94
+
95
+ // First clear any previously associated disposables
96
+ this . globalStorageDisposables . clear ( ) ;
97
+
98
+ // Remember profile associated to global storage
99
+ this . globalStorageProfile = profile ;
100
+
101
+ if ( this . globalStorageProfile . isDefault ) {
68
102
69
103
// If we are in default profile, the global storage is
70
104
// actually the same as application storage. As such we
71
105
// avoid creating the storage library a second time on
72
106
// the same DB.
73
107
74
- promises . push ( IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . GLOBAL ) , broadcastChanges : true } , this . logService ) ) ;
108
+ const { indededDb : applicationStorageIndexedDB , storage : applicationStorage } = await this . applicationStoragePromise . p ;
109
+
110
+ this . globalStorageDatabase = applicationStorageIndexedDB ;
111
+ this . globalStorage = applicationStorage ;
112
+ } else {
113
+ const globalStorageIndexedDB = await IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . GLOBAL ) , broadcastChanges : true } , this . logService ) ;
114
+
115
+ this . globalStorageDatabase = this . globalStorageDisposables . add ( globalStorageIndexedDB ) ;
116
+ this . globalStorage = this . globalStorageDisposables . add ( new Storage ( this . globalStorageDatabase ) ) ;
75
117
}
76
- const [ applicationStorageDatabase , workspaceStorageDatabase , globalStorageDatabase ] = await Promises . settled ( promises ) ;
77
118
78
- // Workspace Storage
79
- this . workspaceStorageDatabase = this . _register ( workspaceStorageDatabase ) ;
119
+ this . globalStorageDisposables . add ( this . globalStorage . onDidChangeStorage ( key => this . emitDidChangeValue ( StorageScope . GLOBAL , key ) ) ) ;
120
+
121
+ await this . globalStorage . init ( ) ;
122
+
123
+ this . updateIsNew ( this . globalStorage ) ;
124
+ }
125
+
126
+ private async createWorkspaceStorage ( ) : Promise < void > {
127
+ const workspaceStorageIndexedDB = await IndexedDBStorageDatabase . create ( { id : this . getId ( StorageScope . WORKSPACE ) } , this . logService ) ;
128
+
129
+ this . workspaceStorageDatabase = this . _register ( workspaceStorageIndexedDB ) ;
80
130
this . workspaceStorage = this . _register ( new Storage ( this . workspaceStorageDatabase ) ) ;
131
+
81
132
this . _register ( this . workspaceStorage . onDidChangeStorage ( key => this . emitDidChangeValue ( StorageScope . WORKSPACE , key ) ) ) ;
82
133
83
- // Application Storage
84
- this . applicationStorageDatabase = this . _register ( applicationStorageDatabase ) ;
85
- this . applicationStorage = this . _register ( new Storage ( this . applicationStorageDatabase ) ) ;
86
- this . _register ( this . applicationStorage . onDidChangeStorage ( key => this . emitDidChangeValue ( StorageScope . APPLICATION , key ) ) ) ;
134
+ await this . workspaceStorage . init ( ) ;
87
135
88
- // Global Storage
89
- if ( globalStorageDatabase ) {
90
- this . globalStorageDatabase = this . _register ( globalStorageDatabase ) ;
91
- this . globalStorage = this . _register ( new Storage ( this . globalStorageDatabase ) ) ;
92
- } else {
93
- this . globalStorage = this . applicationStorage ;
94
- }
95
- this . _register ( this . globalStorage . onDidChangeStorage ( key => this . emitDidChangeValue ( StorageScope . GLOBAL , key ) ) ) ;
96
-
97
- // Init storages
98
- await Promises . settled ( [
99
- this . workspaceStorage . init ( ) ,
100
- this . globalStorage . init ( ) ,
101
- this . applicationStorage . init ( )
102
- ] ) ;
136
+ this . updateIsNew ( this . workspaceStorage ) ;
137
+ }
103
138
104
- // Apply is-new markers
105
- for ( const storage of [ this . applicationStorage , this . globalStorage , this . workspaceStorage ] ) {
106
- const firstOpen = storage . getBoolean ( IS_NEW_KEY ) ;
107
- if ( firstOpen === undefined ) {
108
- storage . set ( IS_NEW_KEY , true ) ;
109
- } else if ( firstOpen ) {
110
- storage . set ( IS_NEW_KEY , false ) ;
111
- }
139
+ private updateIsNew ( storage : IStorage ) : void {
140
+ const firstOpen = storage . getBoolean ( IS_NEW_KEY ) ;
141
+ if ( firstOpen === undefined ) {
142
+ storage . set ( IS_NEW_KEY , true ) ;
143
+ } else if ( firstOpen ) {
144
+ storage . set ( IS_NEW_KEY , false ) ;
112
145
}
113
146
}
114
147
@@ -127,7 +160,24 @@ export class BrowserStorageService extends AbstractStorageService {
127
160
return this . getId ( scope ) ;
128
161
}
129
162
130
- async migrate ( toWorkspace : IAnyWorkspaceIdentifier ) : Promise < void > {
163
+ protected async switchToProfile ( toProfile : IUserDataProfile , preserveData : boolean ) : Promise < void > {
164
+ const oldGlobalStorage = assertIsDefined ( this . globalStorage ) ;
165
+ const oldItems = oldGlobalStorage . items ;
166
+
167
+ // Close old global storage but only if this is
168
+ // different from application storage!
169
+ if ( oldGlobalStorage !== this . applicationStorage ) {
170
+ await oldGlobalStorage . close ( ) ;
171
+ }
172
+
173
+ // Create new global storage & init
174
+ await this . createGlobalStorage ( toProfile ) ;
175
+
176
+ // Handle data switch and eventing
177
+ this . switchData ( oldItems , assertIsDefined ( this . globalStorage ) , StorageScope . GLOBAL , preserveData ) ;
178
+ }
179
+
180
+ protected async switchToWorkspace ( toWorkspace : IAnyWorkspaceIdentifier , preserveData : boolean ) : Promise < void > {
131
181
throw new Error ( 'Migrating storage is currently unsupported in Web' ) ;
132
182
}
133
183
0 commit comments