This repository has been archived by the owner on Dec 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 971
/
Copy pathsessionStore.js
739 lines (679 loc) · 25.8 KB
/
sessionStore.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'
// Session store in Brave works as follows:
// - Electron sends a ‘before-quit’ event
// - Brave sends REQUEST_WINDOW_STATE to each renderer process
// - Each renderer responds with its window state with a RESPONSE_WINDOW_STATE IPC message
// - When all state is collected save it to a JSON file and close the app
// - NODE_ENV of ‘test’ bypassing session state or else they all fail.
const Immutable = require('immutable')
const path = require('path')
const electron = require('electron')
const os = require('os')
const assert = require('assert')
const app = electron.app
const locale = require('./locale')
const UpdateStatus = require('../js/constants/updateStatus')
const settings = require('../js/constants/settings')
const downloadStates = require('../js/constants/downloadStates')
const siteUtil = require('../js/state/siteUtil')
const { topSites, pinnedTopSites } = require('../js/data/newTabData')
const { defaultSiteSettingsList } = require('../js/data/siteSettingsList')
const sessionStorageVersion = 1
const filtering = require('./filtering')
const autofill = require('./autofill')
const {navigatableTypes} = require('../js/lib/appUrlUtil')
const Channel = require('./channel')
const {isList, isMap, isImmutable, makeImmutable, deleteImmutablePaths} = require('./common/state/immutableUtil')
const tabState = require('./common/state/tabState')
const windowState = require('./common/state/windowState')
const platformUtil = require('./common/lib/platformUtil')
const getSetting = require('../js/settings').getSetting
const sessionStorageName = `session-store-${sessionStorageVersion}`
const getTopSiteMap = () => {
if (Array.isArray(topSites) && topSites.length) {
let siteMap = {}
let order = 0
topSites.forEach((site) => {
let key = siteUtil.getSiteKey(Immutable.fromJS(site))
site.order = order++
siteMap[key] = site
})
return siteMap
}
return {}
}
const getTempStoragePath = (filename) => {
const epochTimestamp = (new Date()).getTime().toString()
filename = filename || 'tmp'
return process.env.NODE_ENV !== 'test'
? path.join(app.getPath('userData'), 'session-store-' + filename + '-' + epochTimestamp)
: path.join(process.env.HOME, '.brave-test-session-store-' + filename + '-' + epochTimestamp)
}
const getStoragePath = () => {
return path.join(app.getPath('userData'), sessionStorageName)
}
/**
* Saves the specified immutable browser state to storage.
*
* @param {object} immutablePayload - Application immutable state as per
* https://github.com/brave/browser/wiki/Application-State
* (not immutable data)
* @return a promise which resolves when the state is saved
*/
module.exports.saveAppState = (immutablePayload, isShutdown) => {
assert(isImmutable(immutablePayload))
return new Promise((resolve, reject) => {
// Don't persist private frames
let startupModeSettingValue = getSetting(settings.STARTUP_MODE)
const savePerWindowState = startupModeSettingValue == null ||
startupModeSettingValue === 'lastTime'
if (immutablePayload.get('perWindowState') && savePerWindowState) {
immutablePayload.get('perWindowState').forEach((immutableWndPayload, i) => {
const frames = immutableWndPayload.get('frames').filter((frame) => !frame.get('isPrivate'))
immutableWndPayload = immutableWndPayload.set('frames', frames)
immutablePayload = immutablePayload.setIn(['perWindowState', i], immutableWndPayload)
})
} else {
immutablePayload = immutablePayload.delete('perWindowState')
}
try {
immutablePayload = module.exports.cleanAppData(immutablePayload, isShutdown)
immutablePayload = immutablePayload.set('cleanedOnShutdown', isShutdown)
} catch (e) {
immutablePayload = immutablePayload.set('cleanedOnShutdown', false)
}
immutablePayload = immutablePayload.set('lastAppVersion', app.getVersion())
if (isShutdown) {
module.exports.cleanSessionDataOnShutdown()
}
const storagePath = getStoragePath()
const json = JSON.stringify(immutablePayload)
muon.file.writeImportant(storagePath, json, (success) => {
if (success) {
resolve()
} else {
reject(new Error('Could not save app state to ' + getStoragePath()))
}
})
})
}
/**
* Cleans session data from unwanted values.
* @param immutablePerWindowData - Per window data in ImmutableJS format
* @return ImmutableJS cleaned window data
*/
module.exports.cleanPerWindowData = (immutablePerWindowData, isShutdown) => {
if (!immutablePerWindowData) {
immutablePerWindowData = Immutable.Map()
}
assert(isImmutable(immutablePerWindowData))
// delete the frame index because tabId is per-session
immutablePerWindowData = immutablePerWindowData.delete('framesInternal')
immutablePerWindowData = deleteImmutablePaths(immutablePerWindowData, [
// Hide the context menu when we restore.
'contextMenuDetail',
// Don't save preview frame since they are only related to hovering on a tab
'previewFrameKey',
// Don't save widevine panel detail
'widevinePanelDetail',
// Don't save preview tab pages
['ui', 'tabs', 'previewTabPageIndex'],
// Don't restore add/edit dialog
'bookmarkDetail',
// Don't restore bravery panel
'braveryPanelDetail',
// Don't restore drag data and clearBrowsingDataPanel's visibility
// This is no longer stored, we can remove this line eventually
['ui', 'isFocused'],
['ui', 'mouseInTitlebar'],
['ui', 'mouseInFrame'],
['ui', 'dragging'],
['ui', 'isClearBrowsingDataPanelVisible']
])
if (!immutablePerWindowData.get('frames')) {
immutablePerWindowData = immutablePerWindowData.set('frames', Immutable.List())
}
let newKey = 0
const cleanFrame = (immutableFrame) => {
newKey++
// Reset the ids back to sequential numbers
if (immutableFrame.get('key') === immutablePerWindowData.get('activeFrameKey')) {
immutablePerWindowData = immutablePerWindowData.set('activeFrameKey', newKey)
} else {
// For now just set everything to unloaded unless it's the active frame
immutableFrame = immutableFrame.set('unloaded', true)
}
immutableFrame = immutableFrame.set('key', newKey)
// Set the frame src to the last visited location
// or else users will see the first visited URL.
// Pinned location always get reset to what they are
immutableFrame = immutableFrame.set('src', immutableFrame.get('pinnedLocation') || immutableFrame.get('location'))
// If a blob is present for the thumbnail, create the object URL
if (immutableFrame.get('thumbnailBlob')) {
try {
immutableFrame = immutableFrame.set('thumbnailUrl', window.URL.createObjectURL(immutableFrame.get('thumbnailBlob')))
} catch (e) {
immutableFrame = immutableFrame.delete('thumbnailUrl')
}
}
immutableFrame = deleteImmutablePaths(immutableFrame, [
// Delete lists of blocked sites
'trackingProtection',
'httpsEverywhere',
'adblock',
'noScript',
// clean up any legacy frame opening props
'openInForeground',
'disposition',
// Guest instance ID's are not valid after restarting.
// Electron won't know about them.
'guestInstanceId',
// Tab ids are per-session and should not be persisted
'tabId',
// Do not show the audio indicator until audio starts playing
'audioMuted',
'audioPlaybackActive',
// Let's not assume wknow anything about loading
'loading',
// Always re-determine the security data
'security',
// Value is only used for local storage
'isActive',
// Hide modal prompts.
'modalPromptDetail',
// Remove HTTP basic authentication requests.
'basicAuthDetail',
// Remove open search details
'searchDetail',
// Remove find in page details
['findDetail', 'numberOfMatches'],
['findDetail', 'activeMatchOrdinal'],
['findDetail', 'internalFindStatePresent'],
'findbarShown',
// Don't restore full screen state
'isFullScreen',
'showFullScreenWarning',
// Don't store child tab open ordering since keys
// currently get re-generated when session store is
// restored. We will be able to keep this once we
// don't regenerate new frame keys when opening storage.
'parentFrameKey',
// Delete the active shortcut details
'activeShortcut',
'activeShortcutDetails'
])
if (immutableFrame.get('navbar') && immutableFrame.getIn(['navbar', 'urlbar'])) {
if (immutableFrame.getIn(['navbar', 'urlbar', 'suggestions'])) {
immutableFrame = immutableFrame.setIn(['navbar', 'urlbar', 'suggestions', 'selectedIndex'], null)
immutableFrame = immutableFrame.setIn(['navbar', 'urlbar', 'suggestions', 'suggestionList'], null)
}
immutableFrame = immutableFrame.deleteIn(['navbar', 'urlbar', 'searchDetail'])
}
return immutableFrame
}
const clearHistory = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_HISTORY) === true
if (clearHistory) {
immutablePerWindowData = immutablePerWindowData.set('closedFrames', Immutable.List())
}
// Clean closed frame data before frames because the keys are re-ordered
// and the new next key is calculated in windowStore.js based on
// the max frame key ID.
if (immutablePerWindowData.get('closedFrames')) {
immutablePerWindowData =
immutablePerWindowData.get('closedFrames').reduce((immutablePerWindowData, immutableFrame, index) => {
const cleanImmutableFrame = cleanFrame(immutableFrame)
return immutablePerWindowData.setIn(['closedFrames', index], cleanImmutableFrame)
}, immutablePerWindowData)
}
if (immutablePerWindowData.get('frames')) {
// Don't restore pinned locations because they will be auto created by the app state change event
immutablePerWindowData = immutablePerWindowData.set('frames',
immutablePerWindowData.get('frames')
.filter((frame) => !frame.get('pinnedLocation')))
immutablePerWindowData =
immutablePerWindowData.get('frames').reduce((immutablePerWindowData, immutableFrame, index) => {
const cleanImmutableFrame = cleanFrame(immutableFrame)
return immutablePerWindowData.setIn(['frames', index], cleanImmutableFrame)
}, immutablePerWindowData)
}
return immutablePerWindowData
}
/**
* Cleans app data before it's written to disk.
* @param {Object} data - top-level app data in ImmutableJS format
* @param {Object} isShutdown - true if the data is being cleared for a shutdown
* WARNING: getPrefs is only available in this function when isShutdown is true
* @return Immutable JS cleaned up data
*/
module.exports.cleanAppData = (immutableData, isShutdown) => {
assert(isImmutable(immutableData))
// Don't show notifications from the last session
immutableData = immutableData.set('notifications', Immutable.List())
// Delete temp site settings
immutableData = immutableData.set('temporarySiteSettings', Immutable.Map())
if (immutableData.getIn(['settings', settings.CHECK_DEFAULT_ON_STARTUP]) === true) {
// Delete defaultBrowserCheckComplete state since this is checked on startup
immutableData = immutableData.delete('defaultBrowserCheckComplete')
}
// Delete Recovery status on shut down
try {
immutableData = immutableData.deleteIn(['ui', 'about', 'preferences', 'recoverySucceeded'])
} catch (e) {}
const perWindowStateList = immutableData.get('perWindowState')
if (perWindowStateList) {
perWindowStateList.forEach((immutablePerWindowState, i) => {
const cleanedImmutablePerWindowState = module.exports.cleanPerWindowData(immutablePerWindowState, isShutdown)
immutableData = immutableData.setIn(['perWindowState', i], cleanedImmutablePerWindowState)
})
}
const clearAutocompleteData = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_AUTOCOMPLETE_DATA) === true
if (clearAutocompleteData) {
try {
autofill.clearAutocompleteData()
} catch (e) {
console.log('cleanAppData: error calling autofill.clearAutocompleteData: ', e)
}
}
const clearAutofillData = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_AUTOFILL_DATA) === true
if (clearAutofillData) {
autofill.clearAutofillData()
const date = new Date().getTime()
immutableData = immutableData.set('autofill', Immutable.fromJS({
addresses: {
guid: [],
timestamp: date
},
creditCards: {
guid: [],
timestamp: date
}
}))
}
immutableData = immutableData.delete('dragData')
if (immutableData.get('sync')) {
// clear sync site cache
immutableData = immutableData.deleteIn(['sync', 'objectsById'], Immutable.Map())
}
const clearSiteSettings = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_SITE_SETTINGS) === true
if (clearSiteSettings) {
immutableData = immutableData.set('siteSettings', Immutable.Map())
}
// Delete expired Flash and NoScript allow-once approvals
let now = Date.now()
immutableData.get('siteSettings', Immutable.Map()).forEach((value, host) => {
let expireTime = value.get('flash')
if (typeof expireTime === 'number' && expireTime < now) {
immutableData = immutableData.deleteIn(['siteSettings', host, 'flash'])
}
let noScript = immutableData.getIn(['siteSettings', host, 'noScript'])
if (typeof noScript === 'number') {
immutableData = immutableData.deleteIn(['siteSettings', host, 'noScript'])
}
// Don't persist any noScript exceptions
immutableData = immutableData.deleteIn(['siteSettings', host, 'noScriptExceptions'])
// Don't write runInsecureContent to session
immutableData = immutableData.deleteIn(['siteSettings', host, 'runInsecureContent'])
// If the site setting is empty, delete it for privacy
if (Array.from(immutableData.getIn(['siteSettings', host]).keys()).length === 0) {
immutableData = immutableData.deleteIn(['siteSettings', host])
}
})
if (immutableData.get('sites')) {
const clearHistory = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_HISTORY) === true
if (clearHistory) {
const sitesAfterClearHistory = siteUtil.clearHistory(immutableData.get('sites'))
immutableData = immutableData.set('sites', sitesAfterClearHistory)
immutableData = immutableData.set('historySites', Immutable.Map())
immutableData = deleteImmutablePaths(immutableData, [
['about', 'history'],
['about', 'newtab']
])
}
}
if (immutableData.get('downloads')) {
const clearDownloads = isShutdown && getSetting(settings.SHUTDOWN_CLEAR_DOWNLOADS) === true
if (clearDownloads) {
immutableData = immutableData.delete('downloads')
} else {
// Always at least delete downloaded items older than a week
const dateOffset = 7 * 24 * 60 * 60 * 1000
const lastWeek = new Date().getTime() - dateOffset
Array.from(immutableData.get('downloads').keys()).forEach((downloadId) => {
if (immutableData.getIn(['downloads', downloadId, 'startTime']) < lastWeek) {
immutableData = immutableData.deleteIn(['downloads', downloadId])
} else {
const state = immutableData.getIn(['downloads', downloadId, 'state'])
if (state === downloadStates.IN_PROGRESS || state === downloadStates.PAUSED) {
immutableData = immutableData.setIn(['downloads', downloadId, 'state'], downloadStates.INTERRUPTED)
}
}
})
}
}
immutableData = immutableData.delete('menu')
try {
immutableData = tabState.getPersistentState(immutableData)
} catch (e) {
console.log('cleanAppData: error calling tabState.getPersistentState: ', e)
immutableData = immutableData.set('tabs', Immutable.List())
}
try {
immutableData = windowState.getPersistentState(immutableData)
} catch (e) {
console.log('cleanAppData: error calling windowState.getPersistentState: ', e)
immutableData = immutableData.set('windows', Immutable.List())
}
if (immutableData.get('extensions')) {
Array.from(immutableData.get('extensions').keys()).forEach((extensionId) => {
immutableData = immutableData.deleteIn(['extensions', extensionId, 'tabs'])
})
}
return immutableData
}
/**
* Cleans session data on shutdown if the prefs are on.
* @return a promise which resolve when the work is done.
*/
module.exports.cleanSessionDataOnShutdown = () => {
if (getSetting(settings.SHUTDOWN_CLEAR_ALL_SITE_COOKIES) === true) {
filtering.clearStorageData()
}
if (getSetting(settings.SHUTDOWN_CLEAR_CACHE) === true) {
filtering.clearCache()
}
if (getSetting(settings.SHUTDOWN_CLEAR_HISTORY) === true) {
filtering.clearHistory()
}
}
const safeGetVersion = (fieldName, getFieldVersion) => {
const versionField = {
name: fieldName,
version: undefined
}
try {
if (typeof getFieldVersion === 'function') {
versionField.version = getFieldVersion()
return versionField
}
console.log('ERROR getting value for field ' + fieldName + ' in sessionStore::setVersionInformation(): ', getFieldVersion, ' is not a function')
} catch (e) {
console.log('ERROR getting value for field ' + fieldName + ' in sessionStore::setVersionInformation(): ', e)
}
return versionField
}
/**
* version information (shown on about:brave)
*/
const setVersionInformation = (immutableData) => {
const versionFields = [
['Brave', app.getVersion],
['rev', Channel.browserLaptopRev],
['Muon', () => { return process.versions['atom-shell'] }],
['libchromiumcontent', () => { return process.versions['chrome'] }],
['V8', () => { return process.versions.v8 }],
['Node.js', () => { return process.versions.node }],
['Update Channel', Channel.channel],
['OS Platform', () => platformUtil.formatOsPlatform(os.platform())],
['OS Release', os.release],
['OS Architecture', os.arch]
]
const versionInformation = []
versionFields.forEach((field) => {
versionInformation.push(safeGetVersion(field[0], field[1]))
})
if (!immutableData.get('about')) {
immutableData = immutableData.set('about', Immutable.Map())
}
immutableData = immutableData.setIn(['about', 'brave', 'versionInformation'], Immutable.fromJS(versionInformation))
return immutableData
}
module.exports.runPreMigrations = (data) => {
// autofill data migration
if (data.autofill) {
if (Array.isArray(data.autofill.addresses)) {
let addresses = exports.defaultAppState().autofill.addresses
data.autofill.addresses.forEach((guid) => {
addresses.guid.push(guid)
addresses.timestamp = new Date().getTime()
})
data.autofill.addresses = addresses
}
if (Array.isArray(data.autofill.creditCards)) {
let creditCards = exports.defaultAppState().autofill.creditCards
data.autofill.creditCards.forEach((guid) => {
creditCards.guid.push(guid)
creditCards.timestamp = new Date().getTime()
})
data.autofill.creditCards = creditCards
}
if (data.autofill.addresses.guid) {
let guids = []
data.autofill.addresses.guid.forEach((guid) => {
if (typeof guid === 'object') {
guids.push(guid['persist:default'])
} else {
guids.push(guid)
}
})
data.autofill.addresses.guid = guids
}
if (data.autofill.creditCards.guid) {
let guids = []
data.autofill.creditCards.guid.forEach((guid) => {
if (typeof guid === 'object') {
guids.push(guid['persist:default'])
} else {
guids.push(guid)
}
})
data.autofill.creditCards.guid = guids
}
}
// xml migration
if (data.settings) {
if (data.settings[settings.DEFAULT_SEARCH_ENGINE] === 'content/search/google.xml') {
data.settings[settings.DEFAULT_SEARCH_ENGINE] = 'Google'
}
if (data.settings[settings.DEFAULT_SEARCH_ENGINE] === 'content/search/duckduckgo.xml') {
data.settings[settings.DEFAULT_SEARCH_ENGINE] = 'DuckDuckGo'
}
}
return data
}
module.exports.runPostMigrations = (immutableData) => {
// sites refactoring migration
let oldSites = immutableData.get('sites')
if (isList(oldSites) && oldSites.size) {
let newSites = Immutable.List()
let order = 0
oldSites.forEach((site) => {
let key = siteUtil.getSiteKey(site)
site.order = order++
newSites = newSites.set(key, site)
})
immutableData = immutableData.set('sites', newSites)
}
// sites trailing slash migration
oldSites = immutableData.get('sites')
if (isMap(oldSites)) {
const keys = Array.from(oldSites.keys())
for (let key of keys) {
if (/^http.+\/\|\d+\|\d+/.test(key)) {
const site = oldSites.get(key)
const newKey = siteUtil.getSiteKey(site)
if (!newKey) {
continue
}
immutableData = immutableData.setIn(['sites', newKey], site)
immutableData = immutableData.deleteIn(['sites', key])
}
}
}
return immutableData
}
module.exports.runImportDefaultSettings = (data) => {
// import default site settings list
if (!data.defaultSiteSettingsListImported) {
for (var i = 0; i < defaultSiteSettingsList.length; ++i) {
let setting = defaultSiteSettingsList[i]
if (!data.siteSettings[setting.pattern]) {
data.siteSettings[setting.pattern] = {}
}
let targetSetting = data.siteSettings[setting.pattern]
if (!targetSetting.hasOwnProperty[setting.name]) {
targetSetting[setting.name] = setting.value
}
}
data.defaultSiteSettingsListImported = true
}
return data
}
/**
* Loads the browser state from storage.
*
* @return a promise which resolves with the immutable browser state or
* rejects if the state cannot be loaded.
*/
module.exports.loadAppState = () => {
return new Promise((resolve, reject) => {
const fs = require('fs')
let data
try {
data = fs.readFileSync(getStoragePath())
} catch (e) {}
let loaded = false
try {
data = JSON.parse(data)
loaded = true
} catch (e) {
// Session state might be corrupted; let's backup this
// corrupted value for people to report into support.
module.exports.backupSession()
if (data) {
console.log('could not parse data: ', data, e)
}
data = {}
}
data = Object.assign({}, module.exports.defaultAppState(), data)
data = module.exports.runImportDefaultSettings(data)
if (loaded) {
data = module.exports.runPreMigrations(data)
}
let immutableData = makeImmutable(data)
if (loaded) {
// Clean app data here if it wasn't cleared on shutdown
if (immutableData.get('cleanedOnShutdown') !== true || immutableData.get('lastAppVersion') !== app.getVersion()) {
immutableData = module.exports.cleanAppData(immutableData, false)
}
immutableData = immutableData.set('cleanedOnShutdown', false)
// Always recalculate the update status
if (immutableData.get('updates')) {
const updateStatus = immutableData.getIn(['updates', 'status'])
immutableData = immutableData.deleteIn(['updates', 'status'])
// The process always restarts after an update so if the state
// indicates that a restart isn't wanted, close right away.
if (updateStatus === UpdateStatus.UPDATE_APPLYING_NO_RESTART) {
module.exports.saveAppState(immutableData, true).then(() => {
// Exit immediately without doing the session store saving stuff
// since we want the same state saved except for the update status
app.exit(0)
})
return
}
}
immutableData = module.exports.runPostMigrations(immutableData)
}
immutableData = setVersionInformation(immutableData)
locale.init(immutableData.getIn(['settings', settings.LANGUAGE])).then((locale) => {
app.setLocale(locale)
resolve(immutableData)
})
})
}
/**
* Called when session is suspected for corruption; this will move it out of the way
*/
module.exports.backupSession = () => {
const fs = require('fs-extra')
const src = getStoragePath()
const dest = getTempStoragePath('backup')
if (fs.existsSync(src)) {
try {
fs.copySync(src, dest)
console.log('An error occurred. For support purposes, file "' + src + '" has been copied to "' + dest + '".')
} catch (e) {
console.log('backupSession: error making copy of session file: ', e)
}
}
}
/**
* Obtains the default application level state
*/
module.exports.defaultAppState = () => {
return {
firstRunTimestamp: new Date().getTime(),
sync: {
devices: {},
lastFetchTimestamp: 0,
objectsById: {},
pendingRecords: {},
lastConfirmedRecordTimestamp: 0
},
locationSiteKeysCache: undefined,
sites: getTopSiteMap(),
tabs: [],
windows: [],
extensions: {},
visits: [],
settings: {},
siteSettings: {},
passwords: [],
notifications: [],
temporarySiteSettings: {},
autofill: {
addresses: {
guid: [],
timestamp: 0
},
creditCards: {
guid: [],
timestamp: 0
}
},
menubar: {},
about: {
newtab: {
gridLayoutSize: 'small',
sites: topSites,
ignoredTopSites: [],
pinnedTopSites: pinnedTopSites
},
welcome: {
showOnLoad: !['test', 'development'].includes(process.env.NODE_ENV)
}
},
trackingProtection: {
count: 0
},
adblock: {
count: 0
},
httpsEverywhere: {
count: 0
},
defaultWindowParams: {},
searchDetail: null
}
}
/**
* Determines if a protocol is handled.
* app.on('ready') must have been fired before this is called.
*/
module.exports.isProtocolHandled = (protocol) => {
protocol = (protocol || '').split(':')[0]
return navigatableTypes.includes(`${protocol}:`) ||
electron.session.defaultSession.protocol.isNavigatorProtocolHandled(protocol)
}