aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts/sync.coffee
diff options
context:
space:
mode:
authorStephen Blott2012-11-02 13:10:33 +0000
committerStephen Blott2014-04-18 21:39:58 +0100
commit3aa47a198f2424ba91bb7a8ac07d6b2c53da9698 (patch)
tree2912ad714439c1416b39b934b2073b51bb3af8f9 /background_scripts/sync.coffee
parent366c0a56a4eed38fa8425fd227cd389181fbed92 (diff)
downloadvimium-3aa47a198f2424ba91bb7a8ac07d6b2c53da9698.tar.bz2
Initial synchronization commit.
Synchronization is via `chrome.storage.sync.*`; data is cached in `localStorage`.
Diffstat (limited to 'background_scripts/sync.coffee')
-rw-r--r--background_scripts/sync.coffee136
1 files changed, 136 insertions, 0 deletions
diff --git a/background_scripts/sync.coffee b/background_scripts/sync.coffee
new file mode 100644
index 00000000..d3a18b85
--- /dev/null
+++ b/background_scripts/sync.coffee
@@ -0,0 +1,136 @@
+
+#
+# * Sync.set() and Sync.clear() propagate local changes to chrome.storage.
+# * Sync.listener() listens for changes to chrome.storage and propagates those
+# changes to localStorage and into vimium's internal state.
+# * Sync.pull() polls chrome.storage at startup, similarly propagating changes
+# to localStorage and into vimium's internal state.
+#
+# Changes are propagated into vimium's state using the same mechanism that is
+# used when options are changed on the options page.
+#
+# The effect is best-effort synchronization of vimium options/settings between
+# chrome/vimium instances, whenever:
+# - chrome is logged in to the user's Google account, and
+# - chrome synchronization is enabled.
+#
+# NOTE:
+# Values handled within this module are ALWAYS already JSON.stringifed, so
+# they're always non-empty strings.
+#
+
+console.log ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
+root = exports ? window
+root.Sync = Sync =
+
+ # ##################
+ # constants
+
+ debug: true
+ storage: chrome.storage.sync
+ doNotSync: [ "settingsVersion", "previousVersion" ]
+
+ init: ->
+ chrome.storage.onChanged.addListener (changes, area) -> Sync.listener changes, area
+ @pull()
+ @log "Sync.init()"
+
+ # asynchronous fetch from synced storage, called at startup
+ pull: ->
+ @storage.get null, (items) ->
+ Sync.log "pull callback: #{Sync.callbackStatus()}"
+ if not chrome.runtime.lastError
+ for own key, value of items
+ Sync.storeAndPropagate key, value
+
+ # asynchronous message from synced storage
+ listener: (changes, area) ->
+ @log "listener: #{area}"
+ for own key, change of changes
+ @storeAndPropagate key, change.newValue
+
+ # only ever called from asynchronous synced-storage callbacks (pull and listener)
+ storeAndPropagate: (key, value) ->
+ # must be JSON.stringifed or undefined
+ @checkHaveStringOrUndefined value
+ # ignore, if not accepting this key
+ if not @syncKey key
+ @log "callback ignoring: #{key}"
+ return
+ # ignore, if unchanged
+ if localStorage[key] == value
+ @log "callback unchanged: #{key}"
+ return
+
+ # ok: accept, store and propagate update
+ defaultValue = root.Settings.defaults[key]
+ defaultValueJSON = JSON.stringify(defaultValue) # could cache this to avoid repeated recalculation
+
+ if value && value != defaultValueJSON
+ # key/value has been changed to non-default value at remote instance
+ @log "callback set: #{key}=#{value}"
+ localStorage[key] = value
+ root.Settings.doPostUpdateHook key, JSON.parse(value)
+ else
+ # key has been reset to default value at remote instance
+ @log "callback clear: #{key}=#{value}"
+ delete localStorage[key]
+ root.Settings.doPostUpdateHook key, defaultValue
+
+ # only called synchronously from within vimium, never on a callback
+ # no need to propagate updates into the rest of vimium (because that will already have been handled externally)
+ set: (key, value) ->
+ # value must be JSON.stringifed
+ @checkHaveString value
+ if value
+ if @syncKey key
+ @storage.set @mkKeyValue(key,value), -> Sync.logCallback "DONE set", key
+ @log "set scheduled: #{key}=#{value}"
+ else
+ # unreachable? (because value is a JSON string)
+ @log "UNREACHABLE in Sync.set(): #{key}"
+ @clear key
+
+ # only called synchronously from within vimium, never on a callback
+ # no need to propagate updates into the rest of vimium (because that will already have been handled by externally)
+ clear: (key) ->
+ if @syncKey key
+ @storage.remove key, -> Sync.logCallback "DONE clear", key
+ @log "clear scheduled: #{key}"
+
+ # ##################
+ # utilities
+
+ syncKey: (key) ->
+ key not in @doNotSync
+
+ # there has to be a more elegant way to do this!
+ mkKeyValue: (key, value) ->
+ obj = {}
+ obj[key] = value
+ obj
+
+ # debugging messages
+ # disable these by setting root.Sync.debug to anything falsy
+ log: (msg) ->
+ console.log "sync debug: #{msg}" if @debug
+
+ logCallback: (where, key) ->
+ @log "#{where} callback: #{key} #{@callbackStatus()}"
+
+ callbackStatus: ->
+ if chrome.runtime.lastError then "ERROR: #{chrome.runtime.lastError.message}" else "(OK)"
+
+ checkHaveString: (thing) ->
+ if typeof(thing) != "string" or not thing
+ @log "sync.coffee: Yikes! this should be a non-empty string: #{typeof(thing)} #{thing}"
+
+ checkHaveStringOrUndefined: (thing) ->
+ if ( typeof(thing) != "string" and typeof(thing) != "undefined" ) or ( typeof(thing) == "string" and not thing )
+ @log "sync.coffee: Yikes! this should be a non-empty string or undefined: #{typeof(thing)} #{thing}"
+
+ # end of Sync object
+ # ##################
+
+Sync.init()
+