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
|
#
# * Sync.set() and Sync.clear() propagate local changes to chrome.storage.sync.
# * Sync.listener() listens for changes to chrome.storage.sync and propagates those
# changes to localStorage and into vimium's internal state.
# * Sync.pull() polls chrome.storage.sync at startup, similarly propagating
# changes to localStorage and into vimium's internal state.
#
# Changes are propagated into vimium's state using the same mechanism
# (Settings.doPostUpdateHook) 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.
#
# NOTE:
# Values handled within this module are ALWAYS already JSON.stringifed, so
# they're always non-empty strings.
#
root = exports ? window
root.Sync = Sync =
# 19/4/14:
# Leave logging statements in, but disable debugging.
# We may need to come back to this, so removing logging now would be premature.
# However, if users have problems, they are unlikely to notice and make sense of console logs on
# background pages. So disable it, by default.
# For genuine errors, we call console.log directly.
debug: false
storage: chrome.storage.sync
doNotSync: [ "settingsVersion", "previousVersion" ]
# This is called in main().
init: ->
chrome.storage.onChanged.addListener (changes, area) -> Sync.listener changes, area
@pull()
# Asynchronous fetch from synced storage, called only at startup.
pull: ->
@storage.get null, (items) =>
# Chrome sets chrome.runtime.lastError if there is an error.
if chrome.runtime.lastError is undefined
for own key, value of items
@log "pull: #{key} <- #{value}"
@storeAndPropagate key, value
else
console.log "callback for Sync.pull() indicates error"
console.log chrome.runtime.lastError
# Asynchronous message from synced storage.
listener: (changes, area) ->
for own key, change of changes
@log "listener: #{key} <- #{change.newValue}"
@storeAndPropagate key, change?.newValue
# Only ever called from asynchronous synced-storage callbacks (pull and listener).
storeAndPropagate: (key, value) ->
return if not key of Settings.defaults
return if not @isSyncKey key
return if value and key of localStorage and localStorage[key] is value
# Ok: store and propagate this update.
defaultValue = Settings.defaults[key]
defaultValueJSON = JSON.stringify(defaultValue)
if value and value != defaultValueJSON
# Key/value has been changed to non-default value at remote instance.
@log "storeAndPropagate update: #{key}=#{value}"
localStorage[key] = value
Settings.doPostUpdateHook key, JSON.parse(value)
else
# Key has been reset to default value at remote instance.
@log "storeAndPropagate clear: #{key}"
if key of localStorage
delete localStorage[key]
Settings.doPostUpdateHook key, defaultValue
# Only called synchronously from within vimium, never on a callback.
# No need to propagate updates to the rest of vimium, that's already been done.
set: (key, value) ->
if @isSyncKey key
@log "set scheduled: #{key}=#{value}"
@storage.set @mkKeyValue(key,value), =>
# Chrome sets chrome.runtime.lastError if there is an error.
if chrome.runtime.lastError
console.log "callback for Sync.set() indicates error: #{key} <- #{value}"
console.log chrome.runtime.lastError
# Only called synchronously from within vimium, never on a callback.
clear: (key) ->
if @isSyncKey key
@log "clear scheduled: #{key}"
@storage.remove key, =>
# Chrome sets chrome.runtime.lastError if there is an error.
if chrome.runtime.lastError
console.log "for Sync.clear() indicates error: #{key}"
console.log chrome.runtime.lastError
# Should we synchronize this key?
isSyncKey: (key) ->
key not in @doNotSync
# There has to be a more elegant way to do this!
mkKeyValue: (key, value) ->
obj = {}
obj[key] = value
obj
log: (msg) ->
console.log "Sync: #{msg}" if @debug
|