1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 var chrome = chrome || {}; 6 // TODO(akalin): Add mocking code for e.g. chrome.send() so that we 7 // can test this without rebuilding chrome. 8 chrome.sync = chrome.sync || {}; 9 (function () { 10 11 // This Event class is a simplified version of the one from 12 // event_bindings.js. 13 function Event() { 14 this.listeners_ = []; 15 } 16 17 Event.prototype.addListener = function(listener) { 18 this.listeners_.push(listener); 19 }; 20 21 Event.prototype.removeListener = function(listener) { 22 var i = this.findListener_(listener); 23 if (i == -1) { 24 return; 25 } 26 this.listeners_.splice(i, 1); 27 }; 28 29 Event.prototype.hasListener = function(listener) { 30 return this.findListener_(listener) > -1; 31 }; 32 33 Event.prototype.hasListeners = function(listener) { 34 return this.listeners_.length > 0; 35 }; 36 37 // Returns the index of the given listener, or -1 if not found. 38 Event.prototype.findListener_ = function(listener) { 39 for (var i = 0; i < this.listeners_.length; i++) { 40 if (this.listeners_[i] == listener) { 41 return i; 42 } 43 } 44 return -1; 45 }; 46 47 // Fires the event. Called by the actual event callback. Any 48 // exceptions thrown by a listener are caught and logged. 49 Event.prototype.dispatch_ = function() { 50 var args = Array.prototype.slice.call(arguments); 51 for (var i = 0; i < this.listeners_.length; i++) { 52 try { 53 this.listeners_[i].apply(null, args); 54 } catch (e) { 55 if (e instanceof Error) { 56 // Non-standard, but useful. 57 console.error(e.stack); 58 } else { 59 console.error(e); 60 } 61 } 62 } 63 }; 64 65 // Service events. 66 chrome.sync.onSyncServiceStateChanged = new Event(); 67 68 // Notifier events. 69 chrome.sync.onSyncNotificationStateChange = new Event(); 70 chrome.sync.onSyncIncomingNotification = new Event(); 71 72 // Manager events. 73 chrome.sync.onChangesApplied = new Event(); 74 chrome.sync.onChangesComplete = new Event(); 75 chrome.sync.onSyncCycleCompleted = new Event(); 76 chrome.sync.onAuthError = new Event(); 77 chrome.sync.onUpdatedToken = new Event(); 78 chrome.sync.onPassphraseRequired = new Event(); 79 chrome.sync.onPassphraseAccepted = new Event(); 80 chrome.sync.onEncryptionComplete = new Event(); 81 chrome.sync.onMigrationNeededForTypes = new Event(); 82 chrome.sync.onInitializationComplete = new Event(); 83 chrome.sync.onPaused = new Event(); 84 chrome.sync.onResumed = new Event(); 85 chrome.sync.onStopSyncingPermanently = new Event(); 86 chrome.sync.onClearServerDataSucceeded = new Event(); 87 chrome.sync.onClearServerDataFailed = new Event(); 88 89 function AsyncFunction(name) { 90 this.name_ = name; 91 this.callbacks_ = []; 92 } 93 94 // Calls the function, assuming the last argument is a callback to be 95 // called with the return value. 96 AsyncFunction.prototype.call = function() { 97 var args = Array.prototype.slice.call(arguments); 98 this.callbacks_.push(args.pop()); 99 chrome.send(this.name_, args); 100 } 101 102 // Handle a reply, assuming that messages are processed in FIFO order. 103 AsyncFunction.prototype.handleReply = function() { 104 var args = Array.prototype.slice.call(arguments); 105 // Remove the callback before we call it since the callback may 106 // throw. 107 var callback = this.callbacks_.shift(); 108 callback.apply(null, args); 109 } 110 111 // Sync service functions. 112 chrome.sync.getAboutInfo_ = new AsyncFunction('getAboutInfo'); 113 chrome.sync.getAboutInfo = function(callback) { 114 chrome.sync.getAboutInfo_.call(callback); 115 } 116 117 // Notification functions. 118 chrome.sync.getNotificationState_ = 119 new AsyncFunction('getNotificationState'); 120 chrome.sync.getNotificationState = function(callback) { 121 chrome.sync.getNotificationState_.call(callback); 122 } 123 124 chrome.sync.getNotificationInfo_ = 125 new AsyncFunction('getNotificationInfo'); 126 chrome.sync.getNotificationInfo = function(callback) { 127 chrome.sync.getNotificationInfo_.call(callback); 128 } 129 130 // Node lookup functions. 131 chrome.sync.getRootNode_ = new AsyncFunction('getRootNode'); 132 chrome.sync.getRootNode = function(callback) { 133 chrome.sync.getRootNode_.call(callback); 134 } 135 136 chrome.sync.getNodeById_ = new AsyncFunction('getNodeById'); 137 chrome.sync.getNodeById = function(id, callback) { 138 chrome.sync.getNodeById_.call(id, callback); 139 } 140 141 })(); 142 143 // TODO(akalin): Rewrite the C++ side to not need the handlers below. 144 145 // Sync service event handlers. 146 147 function onSyncServiceStateChanged() { 148 chrome.sync.onSyncServiceStateChanged.dispatch_(); 149 } 150 151 // Notification event handlers. 152 153 function onSyncNotificationStateChange(notificationsEnabled) { 154 chrome.sync.onSyncNotificationStateChange.dispatch_(notificationsEnabled); 155 } 156 157 function onSyncIncomingNotification(changedTypes) { 158 chrome.sync.onSyncIncomingNotification.dispatch_(changedTypes); 159 } 160 161 // Sync manager event handlers. 162 163 function onChangesApplied(modelType, changes) { 164 chrome.sync.onChangesApplied.dispatch_(modelType, changes); 165 } 166 167 function onChangesComplete(modelType) { 168 chrome.sync.onChangesComplete.dispatch_(modelType); 169 } 170 171 function onSyncCycleCompleted(snapshot) { 172 chrome.sync.onSyncCycleCompleted.dispatch_(snapshot); 173 } 174 175 function onAuthError(authError) { 176 chrome.sync.onAuthError.dispatch_(authError); 177 } 178 179 function onUpdatedToken(token) { 180 chrome.sync.onUpdatedToken.dispatch_(token); 181 } 182 183 function onPassphraseRequired(forDecryption) { 184 chrome.sync.onPassphraseRequired.dispatch_(forDecryption); 185 } 186 187 function onPassphraseAccepted(bootstrapToken) { 188 chrome.sync.onPassphraseAccepted.dispatch_(bootstrapToken); 189 } 190 191 function onEncryptionComplete(encrypted_types) { 192 chrome.sync.onEncryptionComplete.dispatch_(encrypted_types); 193 } 194 195 function onMigrationNeededForTypes(model_types) { 196 chrome.sync.onMigrationNeededForTypes.dispatch_(model_types); 197 } 198 199 function onInitializationComplete() { 200 chrome.sync.onInitializationComplete.dispatch_(); 201 } 202 203 function onPaused() { 204 chrome.sync.onPaused.dispatch_(); 205 } 206 207 function onResumed() { 208 chrome.sync.onResumed.dispatch_(); 209 } 210 211 function onStopSyncingPermanently() { 212 chrome.sync.onStopSyncingPermanently.dispatch_(); 213 } 214 215 function onClearServerDataSucceeded() { 216 chrome.sync.onClearServerDataSucceeded(); 217 } 218 219 function onClearServerDataFailed() { 220 chrome.sync.onClearServerDataFailed(); 221 } 222 223 // Function reply handlers. 224 225 function onGetAboutInfoFinished(aboutInfo) { 226 chrome.sync.getAboutInfo_.handleReply(aboutInfo); 227 } 228 229 function onGetNotificationStateFinished(notificationState) { 230 chrome.sync.getNotificationState_.handleReply(notificationState); 231 } 232 233 function onGetRootNodeFinished(rootNode) { 234 chrome.sync.getRootNode_.handleReply(rootNode); 235 } 236 237 function onGetNodeByIdFinished(node) { 238 chrome.sync.getNodeById_.handleReply(node); 239 } 240 241 function onGetNotificationInfoFinished(notificationInfo) { 242 chrome.sync.getNotificationInfo_.handleReply(notificationInfo); 243 } 244