Skip to content

Commit

Permalink
Migrate to navigator.storage.persist
Browse files Browse the repository at this point in the history
  • Loading branch information
jingyu9575 committed May 11, 2018
1 parent aa8ca51 commit cfd0e03
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 29 deletions.
92 changes: 73 additions & 19 deletions src/background.ts
Expand Up @@ -101,17 +101,22 @@ function waitForBrowserDownload(id: number) {
return deferred.promise
}

class NotFoundError extends ExtendableError {
constructor(message = 'not found') { super(message) }
}

class WritableFile {
private mutableFile?: IDBMutableFile
private handle?: IDBFileHandle
private readonly initialization: Promise<void>
readonly initialization: Promise<void>

constructor(private readonly storage: SimpleStorage,
private readonly filename: string, mode: 'create' | 'open') {
private readonly filename: string, mode: 'create' | 'open' | 'opencreate') {
this.initialization = (async () => {
if (mode === 'open')
if (mode === 'open' || mode === 'opencreate')
this.mutableFile = await storage.get(filename)
if (!this.mutableFile) {
if (mode === 'open') throw new NotFoundError()
this.mutableFile = await storage.transaction(function* (_, db) {
return yield db.createMutableFile(filename,
'application/octet-stream')
Expand Down Expand Up @@ -165,7 +170,8 @@ class WritableFile {
}

let taskStorage: SimpleStorage
let fileStorage: SimpleStorage
let fileStorageV0: SimpleStorage
let fileStorageV1: SimpleStorage

class TaskPersistentData extends TaskOptions {
state: DownloadState = 'paused'
Expand Down Expand Up @@ -228,10 +234,25 @@ class Task extends TaskPersistentData {
if (this[key] === undefined)
this[key] = await Settings.get(key)
if (options.state !== 'completed') {
this.file = new WritableFile(fileStorage,
`${this.id}`, loadId === undefined ? 'create' : 'open')
if (loadId === undefined)
await fileStorage.delete(this.snapshotName)
if (loadId === undefined) {
this.file = new WritableFile(fileStorageV1, `${this.id}`, 'create')
} else {
const params: [SimpleStorage, 'open' | 'create'][] = [
[fileStorageV1, 'open'], [fileStorageV0, 'open'],
[fileStorageV1, 'create']
]
for (const param of params) {
try {
this.file = new WritableFile(param[0], `${this.id}`, param[1])
await this.file.initialization // check error
break // finish if there is no error
} catch { }
}
}
if (loadId === undefined) {
await fileStorageV0.delete(this.snapshotName)
await fileStorageV1.delete(this.snapshotName)
}
}
if (options.totalSize !== undefined) {
if (options.state === 'saving' || options.state === 'completed') {
Expand Down Expand Up @@ -506,7 +527,7 @@ class Task extends TaskPersistentData {

try {
for (const lastTrial of [false, true]) {
const snapshot = await fileStorage.get(this.snapshotName)
const snapshot = await fileStorageV1.get(this.snapshotName)
if (snapshot) {
blobUrl.open(snapshot)
} else {
Expand Down Expand Up @@ -536,9 +557,9 @@ class Task extends TaskPersistentData {
blobUrl.close()
if (Number.isFinite(saveId))
await removeBrowserDownload(saveId)
await fileStorage.initialization /* prevent async */
await fileStorageV1.initialization /* prevent async */
await this.file!.getBlob(blob =>
fileStorage.set(this.snapshotName, blob))
fileStorageV1.set(this.snapshotName, blob))
continue
}
this.fileAccessId = saveId
Expand All @@ -565,7 +586,8 @@ class Task extends TaskPersistentData {
async cleanupFileStorage() {
if (this.file) void this.file.destroy()
delete this.file
void fileStorage.delete(this.snapshotName)
void fileStorageV0.delete(this.snapshotName)
void fileStorageV1.delete(this.snapshotName)
}

remove() {
Expand Down Expand Up @@ -975,14 +997,46 @@ function getSuggestedFilename(url: string, contentDisposition: string,
if (!browser.contextMenus)
browser.contextMenus = { create() { }, remove() { } } as any

async function migrateLegacyPersistentSimpleStorage(databaseName: string) {
try {
const oldStorage = new SimpleStorage({ databaseName, legacyPersistent: true })
const newStorage = new SimpleStorage({ databaseName, legacyPersistent: false })
await Promise.all((await oldStorage.keys())
.map(async key => newStorage.set(key, await oldStorage.get(key))))
void indexedDB.deleteDatabase(databaseName, { storage: "persistent" });
} catch { }
}

const initialization = async function () {
await Settings.set({ version: 0 })
const persistent = await hasPersistentDB()
taskStorage = new SimpleStorage({ databaseName: 'tasks', persistent })
fileStorage = new SimpleStorage({
persistent, databaseName: 'IDBFilesStorage-DB-taskFiles',
storeName: 'IDBFilesObjectStorage',
})
if (navigator.storage && navigator.storage.persist)
void navigator.storage.persist()

const legacyPersistent = (await browser.runtime.getPlatformInfo()).os !== 'android'

if (await Settings.get("version") < 1 && legacyPersistent) {
await migrateLegacyPersistentSimpleStorage('tasks')
await migrateLegacyPersistentSimpleStorage('etc')
}
await Settings.set({ version: 1 })

taskStorage = new SimpleStorage({ databaseName: 'tasks' })
fileStorageV1 = new SimpleStorage({ databaseName: 'files' })
try {
fileStorageV0 = new SimpleStorage({
legacyPersistent,
databaseName: 'IDBFilesStorage-DB-taskFiles',
storeName: 'IDBFilesObjectStorage',
})
if (!(await fileStorageV0.keys()).length) {
fileStorageV0 = fileStorageV1
if (legacyPersistent)
void indexedDB.deleteDatabase(fileStorageV0.databaseName,
{ storage: "persistent" })
else
void indexedDB.deleteDatabase(fileStorageV0.databaseName)
}
} catch { fileStorageV0 = fileStorageV1 }

const taskOrder = new Map((await Settings.get('taskOrder')).map(
(v, i) => [v, i] as [number, number]))
const getTaskOrder = (v: IDBValidKey) =>
Expand Down
11 changes: 3 additions & 8 deletions src/common.ts
Expand Up @@ -274,7 +274,7 @@ async function bindPortToPopupWindow(port: browser.runtime.Port) {
class SimpleStorageOptions {
readonly databaseName: string = 'simpleStorage'
readonly storeName: string = 'simpleStorage'
readonly persistent: boolean = true
readonly legacyPersistent: boolean = false

constructor(source: Partial<SimpleStorageOptions>) { Object.assign(this, source) }
}
Expand All @@ -293,7 +293,7 @@ class SimpleStorage extends SimpleStorageOptions {
constructor(options: Partial<SimpleStorageOptions> = {}) {
super(options)
const request = indexedDB.open(this.databaseName,
this.persistent ? { version: 1, storage: "persistent" } : 1 as any)
this.legacyPersistent ? { version: 1, storage: "persistent" } : 1 as any)
request.onupgradeneeded = event => {
const db = request.result as IDBDatabase
db.createObjectStore(this.storeName)
Expand Down Expand Up @@ -352,13 +352,8 @@ class SimpleStorage extends SimpleStorageOptions {
}
}

async function hasPersistentDB() {
return (await browser.runtime.getPlatformInfo()).os !== 'android'
}

async function loadCustomCSS() {
const storage = new SimpleStorage(
{ databaseName: 'etc', persistent: await hasPersistentDB() })
const storage = new SimpleStorage({ databaseName: 'etc' })
const css = await storage.get('customCSS')
if (!css) return
const node = document.createElement('style')
Expand Down
3 changes: 1 addition & 2 deletions src/custom-css.ts
@@ -1,8 +1,7 @@
applyI18n()

void async function () {
const storage = new SimpleStorage(
{ databaseName: 'etc', persistent: await hasPersistentDB() })
const storage = new SimpleStorage({ databaseName: 'etc' })
const textarea = document.getElementById('customCSS') as HTMLTextAreaElement
textarea.value = String(await storage.get('customCSS') || '')

Expand Down
12 changes: 12 additions & 0 deletions typings/x-firefox.d.ts
Expand Up @@ -30,3 +30,15 @@ declare namespace browser.webRequest {
addEventListener(type: string, listener: EventListener): void
}
}

interface Navigator {
storage: StorageManager
}

interface StorageManager {
persist(): Promise<boolean>
}

interface IDBFactory {
deleteDatabase(name: string, options: { storage: string }): IDBOpenDBRequest;
}

0 comments on commit cfd0e03

Please sign in to comment.