1
0
Эх сурвалжийг харах

Improve IndexedDB versioning and recover from version mismatch

Thibaut Courouble 8 жил өмнө
parent
commit
684355cc52

+ 0 - 1
assets/javascripts/app/app.coffee

@@ -201,7 +201,6 @@
     return if @quotaExceeded
     return if @quotaExceeded
     @quotaExceeded = true
     @quotaExceeded = true
     new app.views.Notif 'QuotaExceeded', autoHide: null
     new app.views.Notif 'QuotaExceeded', autoHide: null
-    Raven.captureMessage 'QuotaExceededError', level: 'warning'
     return
     return
 
 
   onCookieBlocked: (key, value, actual) ->
   onCookieBlocked: (key, value, actual) ->

+ 55 - 27
assets/javascripts/app/db.coffee

@@ -1,9 +1,9 @@
 class app.DB
 class app.DB
   NAME = 'docs'
   NAME = 'docs'
+  VERSION = 15
 
 
   constructor: ->
   constructor: ->
     @useIndexedDB = @useIndexedDB()
     @useIndexedDB = @useIndexedDB()
-    @appVersion = @appVersion()
     @callbacks = []
     @callbacks = []
 
 
   db: (fn) ->
   db: (fn) ->
@@ -13,7 +13,7 @@ class app.DB
 
 
     try
     try
       @open = true
       @open = true
-      req = indexedDB.open(NAME, @schemaVersion())
+      req = indexedDB.open(NAME, VERSION * 1e9 + @userVersion())
       req.onsuccess = @onOpenSuccess
       req.onsuccess = @onOpenSuccess
       req.onerror = @onOpenError
       req.onerror = @onOpenError
       req.onupgradeneeded = @onUpgradeNeeded
       req.onupgradeneeded = @onUpgradeNeeded
@@ -26,21 +26,16 @@ class app.DB
 
 
     if db.objectStoreNames.length is 0
     if db.objectStoreNames.length is 0
       try db.close()
       try db.close()
+      @open = false
       @fail 'empty'
       @fail 'empty'
-      return
-
-    unless @checkedBuggyIDB
-      @checkedBuggyIDB = true
-      try
-        @idbTransaction(db, stores: $.makeArray(db.objectStoreNames)[0..1], mode: 'readwrite').abort() # https://bugs.webkit.org/show_bug.cgi?id=136937
-      catch error
-        try db.close()
-        @fail 'buggy', error
-        return
-
-    @runCallbacks(db)
-    @open = false
-    db.close()
+    else if error = @buggyIDB(db)
+      try db.close()
+      @open = false
+      @fail 'buggy', error
+    else
+      @runCallbacks(db)
+      @open = false
+      db.close()
     return
     return
 
 
   onOpenError: (event) =>
   onOpenError: (event) =>
@@ -48,15 +43,17 @@ class app.DB
     @open = false
     @open = false
     error = event.target.error
     error = event.target.error
 
 
-    if error.name is 'QuotaExceededError'
-      @reset()
-      @db()
-      app.onQuotaExceeded()
-    else
-      @fail 'cant_open', error
+    switch error.name
+      when 'QuotaExceededError'
+        @onQuotaExceededError()
+      when 'VersionError'
+        @onVersionError()
+      else
+        @fail 'cant_open', error
     return
     return
 
 
   fail: (reason, error) ->
   fail: (reason, error) ->
+    @cachedDocs = null
     @useIndexedDB = false
     @useIndexedDB = false
     @reason or= reason
     @reason or= reason
     @error or= error
     @error or= error
@@ -65,6 +62,39 @@ class app.DB
     Raven.captureException error, level: 'warning' if error
     Raven.captureException error, level: 'warning' if error
     return
     return
 
 
+  onQuotaExceededError: ->
+    @reset()
+    @db()
+    app.onQuotaExceeded()
+    Raven.captureMessage 'QuotaExceededError', level: 'warning'
+    return
+
+  onVersionError: ->
+    req = indexedDB.open(NAME)
+    req.onsuccess = (event) =>
+      @handleVersionMismatch event.target.result.version
+    req.onerror = (event) ->
+      event.preventDefault()
+      @fail 'cant_open', error
+    return
+
+  handleVersionMismatch: (actualVersion) ->
+    if Math.floor(actualVersion / 1e9) isnt VERSION
+      @fail 'version'
+    else
+      @setUserVersion actualVersion - VERSION * 1e9
+      @db()
+    return
+
+  buggyIDB: (db) ->
+    return if @checkedBuggyIDB
+    @checkedBuggyIDB = true
+    try
+      @idbTransaction(db, stores: $.makeArray(db.objectStoreNames)[0..1], mode: 'readwrite').abort() # https://bugs.webkit.org/show_bug.cgi?id=136937
+      return
+    catch error
+      return error
+
   runCallbacks: (db) ->
   runCallbacks: (db) ->
     fn(db) while fn = @callbacks.shift()
     fn(db) while fn = @callbacks.shift()
     return
     return
@@ -340,11 +370,9 @@ class app.DB
     app.settings.set('schema', @userVersion() + 1)
     app.settings.set('schema', @userVersion() + 1)
     return
     return
 
 
-  schemaVersion: ->
-    @appVersion * 10 + @userVersion()
+  setUserVersion: (version) ->
+    app.settings.set('schema', version)
+    return
 
 
   userVersion: ->
   userVersion: ->
     app.settings.get('schema')
     app.settings.get('schema')
-
-  appVersion: ->
-    if app.config.env is 'production' then app.config.version else Math.floor(Date.now() / 1000)

+ 3 - 0
assets/javascripts/templates/error_tmpl.coffee

@@ -40,6 +40,9 @@ app.templates.offlineError = (reason, exception) ->
       """ An error occured when trying to open the IndexedDB database:<br>
       """ An error occured when trying to open the IndexedDB database:<br>
           <code class="_label">#{exception.name}: #{exception.message}</code><br>
           <code class="_label">#{exception.name}: #{exception.message}</code><br>
           This could be because you're browsing in private mode or have disallowed offline storage on the domain. """
           This could be because you're browsing in private mode or have disallowed offline storage on the domain. """
+    when 'version'
+      """ The IndexedDB database was modified with a newer version of the app.<br>
+          <a href="#" data-behavior="reload">Reload the page</a> to use offline mode. """
     when 'empty'
     when 'empty'
       """ The IndexedDB database appears to be corrupted. Try <a href="#" data-behavior="reset">resetting the app</a>. """
       """ The IndexedDB database appears to be corrupted. Try <a href="#" data-behavior="reset">resetting the app</a>. """