Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: chrome/browser/sync/internal_api/sync_manager.cc

Issue 7633077: Refactor syncapi.h (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Set of patches to refactor syncapi.h Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 #include "chrome/browser/sync/internal_api/sync_manager.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/base64.h"
11 #include "base/json/json_writer.h"
12 #include "base/string_number_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/sync/engine/all_status.h"
15 #include "chrome/browser/sync/engine/change_reorder_buffer.h"
16 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
17 #include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h"
18 #include "chrome/browser/sync/engine/nigori_util.h"
19 #include "chrome/browser/sync/engine/syncapi_internal.h"
20 #include "chrome/browser/sync/engine/syncer_types.h"
21 #include "chrome/browser/sync/engine/sync_scheduler.h"
22 #include "chrome/browser/sync/internal_api/base_node.h"
23 #include "chrome/browser/sync/internal_api/read_node.h"
24 #include "chrome/browser/sync/internal_api/read_transaction.h"
25 #include "chrome/browser/sync/internal_api/syncapi_functions.h"
26 #include "chrome/browser/sync/internal_api/user_share.h"
27 #include "chrome/browser/sync/internal_api/write_node.h"
28 #include "chrome/browser/sync/internal_api/write_transaction.h"
29 #include "chrome/browser/sync/js/js_arg_list.h"
30 #include "chrome/browser/sync/js/js_backend.h"
31 #include "chrome/browser/sync/js/js_event_details.h"
32 #include "chrome/browser/sync/js/js_event_handler.h"
33 #include "chrome/browser/sync/js/js_reply_handler.h"
34 #include "chrome/browser/sync/js/js_sync_manager_observer.h"
35 #include "chrome/browser/sync/js/js_transaction_observer.h"
36 #include "chrome/browser/sync/notifier/sync_notifier.h"
37 #include "chrome/browser/sync/notifier/sync_notifier_observer.h"
38 #include "chrome/browser/sync/protocol/proto_value_conversions.h"
39 #include "chrome/browser/sync/syncable/directory_change_delegate.h"
40 #include "chrome/browser/sync/syncable/directory_manager.h"
41 #include "chrome/browser/sync/syncable/model_type.h"
42 #include "chrome/browser/sync/syncable/syncable.h"
43 #include "chrome/browser/sync/util/cryptographer.h"
44 #include "chrome/browser/sync/weak_handle.h"
45 #include "net/base/network_change_notifier.h"
46
47 using std::string;
48 using std::vector;
49
50 using base::TimeDelta;
51 using browser_sync::AllStatus;
52 using browser_sync::Cryptographer;
53 using browser_sync::JsArgList;
54 using browser_sync::JsBackend;
55 using browser_sync::JsEventDetails;
56 using browser_sync::JsEventHandler;
57 using browser_sync::JsEventHandler;
58 using browser_sync::JsReplyHandler;
59 using browser_sync::JsSyncManagerObserver;
60 using browser_sync::JsTransactionObserver;
61 using browser_sync::ModelSafeWorkerRegistrar;
62 using browser_sync::kNigoriTag;
63 using browser_sync::KeyParams;
64 using browser_sync::ModelSafeRoutingInfo;
65 using browser_sync::ServerConnectionEvent;
66 using browser_sync::ServerConnectionEventListener;
67 using browser_sync::SyncEngineEvent;
68 using browser_sync::SyncEngineEventListener;
69 using browser_sync::SyncScheduler;
70 using browser_sync::Syncer;
71 using browser_sync::WeakHandle;
72 using browser_sync::sessions::SyncSessionContext;
73 using syncable::DirectoryManager;
74 using syncable::EntryKernelMutationSet;
75 using syncable::ModelType;
76 using syncable::ModelTypeBitSet;
77 using syncable::SPECIFICS;
78
79 typedef GoogleServiceAuthError AuthError;
80
81 namespace {
82
83 static const int kSyncSchedulerDelayMsec = 250;
84
85 #if defined(OS_CHROMEOS)
86 static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000;
87 #endif // OS_CHROMEOS
88
89 } // namespace
90
91 namespace sync_api {
92
93 SyncManager::ChangeRecord::ChangeRecord()
94 : id(kInvalidId), action(ACTION_ADD) {}
95
96 SyncManager::ChangeRecord::~ChangeRecord() {}
97
98 DictionaryValue* SyncManager::ChangeRecord::ToValue(
99 const BaseTransaction* trans) const {
100 DictionaryValue* value = new DictionaryValue();
101 std::string action_str;
102 switch (action) {
103 case ACTION_ADD:
104 action_str = "Add";
105 break;
106 case ACTION_DELETE:
107 action_str = "Delete";
108 break;
109 case ACTION_UPDATE:
110 action_str = "Update";
111 break;
112 default:
113 NOTREACHED();
114 action_str = "Unknown";
115 break;
116 }
117 value->SetString("action", action_str);
118 Value* node_value = NULL;
119 if (action == ACTION_DELETE) {
120 DictionaryValue* node_dict = new DictionaryValue();
121 node_dict->SetString("id", base::Int64ToString(id));
122 node_dict->Set("specifics",
123 browser_sync::EntitySpecificsToValue(specifics));
124 if (extra.get()) {
125 node_dict->Set("extra", extra->ToValue());
126 }
127 node_value = node_dict;
128 } else {
129 ReadNode node(trans);
130 if (node.InitByIdLookup(id)) {
131 node_value = node.GetDetailsAsValue();
132 }
133 }
134 if (!node_value) {
135 NOTREACHED();
136 node_value = Value::CreateNullValue();
137 }
138 value->Set("node", node_value);
139 return value;
140 }
141
142 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {}
143
144 SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData(
145 const sync_pb::PasswordSpecificsData& data)
146 : unencrypted_(data) {
147 }
148
149 SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {}
150
151 DictionaryValue* SyncManager::ExtraPasswordChangeRecordData::ToValue() const {
152 return browser_sync::PasswordSpecificsDataToValue(unencrypted_);
153 }
154
155 const sync_pb::PasswordSpecificsData&
156 SyncManager::ExtraPasswordChangeRecordData::unencrypted() const {
157 return unencrypted_;
158 }
159
160 //////////////////////////////////////////////////////////////////////////
161 // SyncManager's implementation: SyncManager::SyncInternal
162 class SyncManager::SyncInternal
163 : public net::NetworkChangeNotifier::IPAddressObserver,
164 public sync_notifier::SyncNotifierObserver,
165 public JsBackend,
166 public SyncEngineEventListener,
167 public ServerConnectionEventListener,
168 public syncable::DirectoryChangeDelegate {
169 static const int kDefaultNudgeDelayMilliseconds;
170 static const int kPreferencesNudgeDelayMilliseconds;
171 public:
172 explicit SyncInternal(const std::string& name)
173 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
174 registrar_(NULL),
175 initialized_(false),
176 setup_for_test_mode_(false),
177 observing_ip_address_changes_(false) {
178 // Pre-fill |notification_info_map_|.
179 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
180 i < syncable::MODEL_TYPE_COUNT; ++i) {
181 notification_info_map_.insert(
182 std::make_pair(syncable::ModelTypeFromInt(i), NotificationInfo()));
183 }
184
185 // Bind message handlers.
186 BindJsMessageHandler(
187 "getNotificationState",
188 &SyncManager::SyncInternal::GetNotificationState);
189 BindJsMessageHandler(
190 "getNotificationInfo",
191 &SyncManager::SyncInternal::GetNotificationInfo);
192 BindJsMessageHandler(
193 "getRootNodeDetails",
194 &SyncManager::SyncInternal::GetRootNodeDetails);
195 BindJsMessageHandler(
196 "getNodeSummariesById",
197 &SyncManager::SyncInternal::GetNodeSummariesById);
198 BindJsMessageHandler(
199 "getNodeDetailsById",
200 &SyncManager::SyncInternal::GetNodeDetailsById);
201 BindJsMessageHandler(
202 "getChildNodeIds",
203 &SyncManager::SyncInternal::GetChildNodeIds);
204 BindJsMessageHandler(
205 "findNodesContainingString",
206 &SyncManager::SyncInternal::FindNodesContainingString);
207 }
208
209 virtual ~SyncInternal() {
210 CHECK(!initialized_);
211 }
212
213 bool Init(const FilePath& database_location,
214 const WeakHandle<JsEventHandler>& event_handler,
215 const std::string& sync_server_and_path,
216 int port,
217 bool use_ssl,
218 HttpPostProviderFactory* post_factory,
219 ModelSafeWorkerRegistrar* model_safe_worker_registrar,
220 const std::string& user_agent,
221 const SyncCredentials& credentials,
222 sync_notifier::SyncNotifier* sync_notifier,
223 const std::string& restored_key_for_bootstrapping,
224 bool setup_for_test_mode);
225
226 // Sign into sync with given credentials.
227 // We do not verify the tokens given. After this call, the tokens are set
228 // and the sync DB is open. True if successful, false if something
229 // went wrong.
230 bool SignIn(const SyncCredentials& credentials);
231
232 // Update tokens that we're using in Sync. Email must stay the same.
233 void UpdateCredentials(const SyncCredentials& credentials);
234
235 // Called when the user disables or enables a sync type.
236 void UpdateEnabledTypes();
237
238 // Tell the sync engine to start the syncing process.
239 void StartSyncingNormally();
240
241 // Whether or not the Nigori node is encrypted using an explicit passphrase.
242 bool IsUsingExplicitPassphrase();
243
244 // Update the Cryptographer from the current nigori node.
245 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so
246 // should only be called after syncapi is fully initialized.
247 // Returns true if cryptographer is ready, false otherwise.
248 bool UpdateCryptographerFromNigori();
249
250 // Set the datatypes we want to encrypt and encrypt any nodes as necessary.
251 // Note: |encrypted_types| will be unioned with the current set of encrypted
252 // types, as we do not currently support decrypting datatypes.
253 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types);
254
255 // Try to set the current passphrase to |passphrase|, and record whether
256 // it is an explicit passphrase or implicitly using gaia in the Nigori
257 // node.
258 void SetPassphrase(const std::string& passphrase, bool is_explicit);
259
260 // Call periodically from a database-safe thread to persist recent changes
261 // to the syncapi model.
262 void SaveChanges();
263
264 // DirectoryChangeDelegate implementation.
265 // This listener is called upon completion of a syncable transaction, and
266 // builds the list of sync-engine initiated changes that will be forwarded to
267 // the SyncManager's Observers.
268 virtual void HandleTransactionCompleteChangeEvent(
269 const ModelTypeBitSet& models_with_changes);
270 virtual ModelTypeBitSet HandleTransactionEndingChangeEvent(
271 syncable::BaseTransaction* trans);
272 virtual void HandleCalculateChangesChangeEventFromSyncApi(
273 const EntryKernelMutationSet& mutations,
274 syncable::BaseTransaction* trans);
275 virtual void HandleCalculateChangesChangeEventFromSyncer(
276 const EntryKernelMutationSet& mutations,
277 syncable::BaseTransaction* trans);
278
279 // Listens for notifications from the ServerConnectionManager
280 void HandleServerConnectionEvent(const ServerConnectionEvent& event);
281
282 // Open the directory named with username_for_share
283 bool OpenDirectory();
284
285 // SyncNotifierObserver implementation.
286 virtual void OnNotificationStateChange(
287 bool notifications_enabled);
288
289 virtual void OnIncomingNotification(
290 const syncable::ModelTypePayloadMap& type_payloads);
291
292 virtual void StoreState(const std::string& cookie);
293
294 // Thread-safe observers_ accessors.
295 void CopyObservers(ObserverList<SyncManager::Observer>* observers_copy);
296 bool HaveObservers() const;
297 void AddObserver(SyncManager::Observer* observer);
298 void RemoveObserver(SyncManager::Observer* observer);
299
300 // Accessors for the private members.
301 DirectoryManager* dir_manager() { return share_.dir_manager.get(); }
302 SyncAPIServerConnectionManager* connection_manager() {
303 return connection_manager_.get();
304 }
305 SyncScheduler* scheduler() { return scheduler_.get(); }
306 UserShare* GetUserShare() {
307 DCHECK(initialized_);
308 return &share_;
309 }
310
311 // Return the currently active (validated) username for use with syncable
312 // types.
313 const std::string& username_for_share() const {
314 return share_.name;
315 }
316
317 Status GetStatus();
318
319 void RequestNudge(const tracked_objects::Location& nudge_location);
320
321 void RequestNudgeForDataType(
322 const tracked_objects::Location& nudge_location,
323 const ModelType& type);
324
325 void RequestEarlyExit();
326
327 // See SyncManager::Shutdown for information.
328 void Shutdown();
329
330 // If this is a deletion for a password, sets the legacy
331 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets
332 // |buffer|'s specifics field to contain the unencrypted data.
333 void SetExtraChangeRecordData(int64 id,
334 syncable::ModelType type,
335 ChangeReorderBuffer* buffer,
336 Cryptographer* cryptographer,
337 const syncable::EntryKernel& original,
338 bool existed_before,
339 bool exists_now);
340
341 // Called only by our NetworkChangeNotifier.
342 virtual void OnIPAddressChanged();
343
344 bool InitialSyncEndedForAllEnabledTypes() {
345 syncable::ModelTypeSet types;
346 ModelSafeRoutingInfo enabled_types;
347 registrar_->GetModelSafeRoutingInfo(&enabled_types);
348 for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin();
349 i != enabled_types.end(); ++i) {
350 types.insert(i->first);
351 }
352
353 return InitialSyncEndedForTypes(types, &share_);
354 }
355
356 // SyncEngineEventListener implementation.
357 virtual void OnSyncEngineEvent(const SyncEngineEvent& event);
358
359 // ServerConnectionEventListener implementation.
360 virtual void OnServerConnectionEvent(const ServerConnectionEvent& event);
361
362 // JsBackend implementation.
363 virtual void SetJsEventHandler(
364 const WeakHandle<JsEventHandler>& event_handler) OVERRIDE;
365 virtual void ProcessJsMessage(
366 const std::string& name, const JsArgList& args,
367 const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE;
368
369 private:
370 struct NotificationInfo {
371 int total_count;
372 std::string payload;
373
374 NotificationInfo() : total_count(0) {}
375
376 ~NotificationInfo() {}
377
378 // Returned pointer owned by the caller.
379 DictionaryValue* ToValue() const {
380 DictionaryValue* value = new DictionaryValue();
381 value->SetInteger("totalCount", total_count);
382 value->SetString("payload", payload);
383 return value;
384 }
385 };
386
387 typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap;
388 typedef JsArgList
389 (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&);
390 typedef base::Callback<JsArgList(JsArgList)> JsMessageHandler;
391 typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap;
392
393 // Helper to call OnAuthError when no authentication credentials are
394 // available.
395 void RaiseAuthNeededEvent();
396
397 // Determine if the parents or predecessors differ between the old and new
398 // versions of an entry stored in |a| and |b|. Note that a node's index may
399 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but
400 // the relative order is unchanged). To handle such cases, we rely on the
401 // caller to treat a position update on any sibling as updating the positions
402 // of all siblings.
403 static bool VisiblePositionsDiffer(
404 const syncable::EntryKernelMutation& mutation) {
405 const syncable::EntryKernel& a = mutation.original;
406 const syncable::EntryKernel& b = mutation.mutated;
407 // If the datatype isn't one where the browser model cares about position,
408 // don't bother notifying that data model of position-only changes.
409 if (!ShouldMaintainPosition(
410 syncable::GetModelTypeFromSpecifics(b.ref(SPECIFICS))))
411 return false;
412 if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID))
413 return true;
414 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID))
415 return true;
416 return false;
417 }
418
419 // Determine if any of the fields made visible to clients of the Sync API
420 // differ between the versions of an entry stored in |a| and |b|. A return
421 // value of false means that it should be OK to ignore this change.
422 static bool VisiblePropertiesDiffer(
423 const syncable::EntryKernelMutation& mutation,
424 Cryptographer* cryptographer) {
425 const syncable::EntryKernel& a = mutation.original;
426 const syncable::EntryKernel& b = mutation.mutated;
427 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS);
428 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS);
429 DCHECK_EQ(syncable::GetModelTypeFromSpecifics(a_specifics),
430 syncable::GetModelTypeFromSpecifics(b_specifics));
431 syncable::ModelType model_type =
432 syncable::GetModelTypeFromSpecifics(b_specifics);
433 // Suppress updates to items that aren't tracked by any browser model.
434 if (model_type < syncable::FIRST_REAL_MODEL_TYPE ||
435 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) {
436 return false;
437 }
438 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR))
439 return true;
440 if (!AreSpecificsEqual(cryptographer,
441 a.ref(syncable::SPECIFICS),
442 b.ref(syncable::SPECIFICS))) {
443 return true;
444 }
445 // We only care if the name has changed if neither specifics is encrypted
446 // (encrypted nodes blow away the NON_UNIQUE_NAME).
447 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() &&
448 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME))
449 return true;
450 if (VisiblePositionsDiffer(mutation))
451 return true;
452 return false;
453 }
454
455 bool ChangeBuffersAreEmpty() {
456 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
457 if (!change_buffers_[i].IsEmpty())
458 return false;
459 }
460 return true;
461 }
462
463 void CheckServerReachable() {
464 if (connection_manager()) {
465 connection_manager()->CheckServerReachable();
466 } else {
467 NOTREACHED() << "Should be valid connection manager!";
468 }
469 }
470
471 void ReEncryptEverything(WriteTransaction* trans);
472
473 // Initializes (bootstraps) the Cryptographer if NIGORI has finished
474 // initial sync so that it can immediately start encrypting / decrypting.
475 // If the restored key is incompatible with the current version of the NIGORI
476 // node (which could happen if a restart occurred just after an update to
477 // NIGORI was downloaded and the user must enter a new passphrase to decrypt)
478 // then we will raise OnPassphraseRequired and set pending keys for
479 // decryption. Otherwise, the cryptographer is made ready (is_ready()).
480 void BootstrapEncryption(const std::string& restored_key_for_bootstrapping);
481
482 // Called for every notification. This updates the notification statistics
483 // to be displayed in about:sync.
484 void UpdateNotificationInfo(
485 const syncable::ModelTypePayloadMap& type_payloads);
486
487 // Checks for server reachabilty and requests a nudge.
488 void OnIPAddressChangedImpl();
489
490 // Helper function used only by the constructor.
491 void BindJsMessageHandler(
492 const std::string& name, UnboundJsMessageHandler unbound_message_handler);
493
494 // Returned pointer is owned by the caller.
495 static DictionaryValue* NotificationInfoToValue(
496 const NotificationInfoMap& notification_info);
497
498 // JS message handlers.
499 JsArgList GetNotificationState(const JsArgList& args);
500 JsArgList GetNotificationInfo(const JsArgList& args);
501 JsArgList GetRootNodeDetails(const JsArgList& args);
502 JsArgList GetNodeSummariesById(const JsArgList& args);
503 JsArgList GetNodeDetailsById(const JsArgList& args);
504 JsArgList GetChildNodeIds(const JsArgList& args);
505 JsArgList FindNodesContainingString(const JsArgList& args);
506
507 const std::string name_;
508
509 base::ThreadChecker thread_checker_;
510
511 base::WeakPtrFactory<SyncInternal> weak_ptr_factory_;
512
513 // Thread-safe handle used by
514 // HandleCalculateChangesChangeEventFromSyncApi(), which can be
515 // called from any thread. Valid only between between calls to
516 // Init() and Shutdown().
517 //
518 // TODO(akalin): Ideally, we wouldn't need to store this; instead,
519 // we'd have another worker class which implements
520 // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a
521 // WeakHandle when we construct it.
522 WeakHandle<SyncInternal> weak_handle_this_;
523
524 // We couple the DirectoryManager and username together in a UserShare member
525 // so we can return a handle to share_ to clients of the API for use when
526 // constructing any transaction type.
527 UserShare share_;
528
529 // We have to lock around every observers_ access because it can get accessed
530 // from any thread and added to/removed from on the core thread.
531 mutable base::Lock observers_lock_;
532 ObserverList<SyncManager::Observer> observers_;
533
534 // The ServerConnectionManager used to abstract communication between the
535 // client (the Syncer) and the sync server.
536 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_;
537
538 // The scheduler that runs the Syncer. Needs to be explicitly
539 // Start()ed.
540 scoped_ptr<SyncScheduler> scheduler_;
541
542 // The SyncNotifier which notifies us when updates need to be downloaded.
543 scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_;
544
545 // A multi-purpose status watch object that aggregates stats from various
546 // sync components.
547 AllStatus allstatus_;
548
549 // Each element of this array is a store of change records produced by
550 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are
551 // segregated by model type, and are stored here to be processed and
552 // forwarded to the observer slightly later, at the TRANSACTION_ENDING
553 // step by HandleTransactionEndingChangeEvent. The list is cleared in the
554 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent.
555 ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT];
556
557 // The entity that provides us with information about which types to sync.
558 // The instance is shared between the SyncManager and the Syncer.
559 ModelSafeWorkerRegistrar* registrar_;
560
561 // Set to true once Init has been called.
562 bool initialized_;
563
564 // True if the SyncManager should be running in test mode (no sync
565 // scheduler actually communicating with the server).
566 bool setup_for_test_mode_;
567
568 // Whether we should respond to an IP address change notification.
569 bool observing_ip_address_changes_;
570
571 // Map used to store the notification info to be displayed in
572 // about:sync page.
573 NotificationInfoMap notification_info_map_;
574
575 // These are for interacting with chrome://sync-internals.
576 JsMessageHandlerMap js_message_handlers_;
577 WeakHandle<JsEventHandler> js_event_handler_;
578 JsSyncManagerObserver js_sync_manager_observer_;
579 JsTransactionObserver js_transaction_observer_;
580 };
581 const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200;
582 const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000;
583
584 SyncManager::Observer::~Observer() {}
585
586 SyncManager::SyncManager(const std::string& name)
587 : data_(new SyncInternal(name)) {}
588
589 SyncManager::Status::Status()
590 : summary(INVALID),
591 authenticated(false),
592 server_up(false),
593 server_reachable(false),
594 server_broken(false),
595 notifications_enabled(false),
596 notifications_received(0),
597 notifiable_commits(0),
598 max_consecutive_errors(0),
599 unsynced_count(0),
600 conflicting_count(0),
601 syncing(false),
602 initial_sync_ended(false),
603 syncer_stuck(false),
604 updates_available(0),
605 updates_received(0),
606 tombstone_updates_received(0),
607 disk_full(false),
608 num_local_overwrites_total(0),
609 num_server_overwrites_total(0),
610 nonempty_get_updates(0),
611 empty_get_updates(0),
612 useless_sync_cycles(0),
613 useful_sync_cycles(0),
614 cryptographer_ready(false),
615 crypto_has_pending_keys(false) {
616 }
617
618 SyncManager::Status::~Status() {
619 }
620
621 bool SyncManager::Init(
622 const FilePath& database_location,
623 const WeakHandle<JsEventHandler>& event_handler,
624 const std::string& sync_server_and_path,
625 int sync_server_port,
626 bool use_ssl,
627 HttpPostProviderFactory* post_factory,
628 ModelSafeWorkerRegistrar* registrar,
629 const std::string& user_agent,
630 const SyncCredentials& credentials,
631 sync_notifier::SyncNotifier* sync_notifier,
632 const std::string& restored_key_for_bootstrapping,
633 bool setup_for_test_mode) {
634 DCHECK(post_factory);
635 VLOG(1) << "SyncManager starting Init...";
636 string server_string(sync_server_and_path);
637 return data_->Init(database_location,
638 event_handler,
639 server_string,
640 sync_server_port,
641 use_ssl,
642 post_factory,
643 registrar,
644 user_agent,
645 credentials,
646 sync_notifier,
647 restored_key_for_bootstrapping,
648 setup_for_test_mode);
649 }
650
651 void SyncManager::UpdateCredentials(const SyncCredentials& credentials) {
652 data_->UpdateCredentials(credentials);
653 }
654
655 void SyncManager::UpdateEnabledTypes() {
656 data_->UpdateEnabledTypes();
657 }
658
659 bool SyncManager::InitialSyncEndedForAllEnabledTypes() {
660 return data_->InitialSyncEndedForAllEnabledTypes();
661 }
662
663 void SyncManager::StartSyncingNormally() {
664 data_->StartSyncingNormally();
665 }
666
667 void SyncManager::SetPassphrase(const std::string& passphrase,
668 bool is_explicit) {
669 data_->SetPassphrase(passphrase, is_explicit);
670 }
671
672 void SyncManager::EncryptDataTypes(
673 const syncable::ModelTypeSet& encrypted_types) {
674 data_->EncryptDataTypes(encrypted_types);
675 }
676
677 bool SyncManager::IsUsingExplicitPassphrase() {
678 return data_ && data_->IsUsingExplicitPassphrase();
679 }
680
681 void SyncManager::RequestCleanupDisabledTypes() {
682 if (data_->scheduler())
683 data_->scheduler()->ScheduleCleanupDisabledTypes();
684 }
685
686 void SyncManager::RequestClearServerData() {
687 if (data_->scheduler())
688 data_->scheduler()->ScheduleClearUserData();
689 }
690
691 void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types,
692 ConfigureReason reason) {
693 if (!data_->scheduler()) {
694 LOG(INFO)
695 << "SyncManager::RequestConfig: bailing out because scheduler is "
696 << "null";
697 return;
698 }
699 StartConfigurationMode(NULL);
700 data_->scheduler()->ScheduleConfig(types, reason);
701 }
702
703 void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) {
704 if (!data_->scheduler()) {
705 LOG(INFO)
706 << "SyncManager::StartConfigurationMode: could not start "
707 << "configuration mode because because scheduler is null";
708 return;
709 }
710 data_->scheduler()->Start(
711 browser_sync::SyncScheduler::CONFIGURATION_MODE, callback);
712 }
713
714 const std::string& SyncManager::GetAuthenticatedUsername() {
715 DCHECK(data_);
716 return data_->username_for_share();
717 }
718
719 bool SyncManager::SyncInternal::Init(
720 const FilePath& database_location,
721 const WeakHandle<JsEventHandler>& event_handler,
722 const std::string& sync_server_and_path,
723 int port,
724 bool use_ssl,
725 HttpPostProviderFactory* post_factory,
726 ModelSafeWorkerRegistrar* model_safe_worker_registrar,
727 const std::string& user_agent,
728 const SyncCredentials& credentials,
729 sync_notifier::SyncNotifier* sync_notifier,
730 const std::string& restored_key_for_bootstrapping,
731 bool setup_for_test_mode) {
732 CHECK(!initialized_);
733
734 DCHECK(thread_checker_.CalledOnValidThread());
735
736 VLOG(1) << "Starting SyncInternal initialization.";
737
738 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
739
740 registrar_ = model_safe_worker_registrar;
741 setup_for_test_mode_ = setup_for_test_mode;
742
743 sync_notifier_.reset(sync_notifier);
744
745 AddObserver(&js_sync_manager_observer_);
746 SetJsEventHandler(event_handler);
747
748 share_.dir_manager.reset(new DirectoryManager(database_location));
749
750 connection_manager_.reset(new SyncAPIServerConnectionManager(
751 sync_server_and_path, port, use_ssl, user_agent, post_factory));
752
753 net::NetworkChangeNotifier::AddIPAddressObserver(this);
754 observing_ip_address_changes_ = true;
755
756 connection_manager()->AddListener(this);
757
758 // TODO(akalin): CheckServerReachable() can block, which may cause jank if we
759 // try to shut down sync. Fix this.
760 MessageLoop::current()->PostTask(
761 FROM_HERE, base::Bind(&SyncInternal::CheckServerReachable,
762 weak_ptr_factory_.GetWeakPtr()));
763
764 // Test mode does not use a syncer context or syncer thread.
765 if (!setup_for_test_mode_) {
766 // Build a SyncSessionContext and store the worker in it.
767 VLOG(1) << "Sync is bringing up SyncSessionContext.";
768 std::vector<SyncEngineEventListener*> listeners;
769 listeners.push_back(&allstatus_);
770 listeners.push_back(this);
771 SyncSessionContext* context = new SyncSessionContext(
772 connection_manager_.get(),
773 dir_manager(),
774 model_safe_worker_registrar,
775 listeners);
776 context->set_account_name(credentials.email);
777 // The SyncScheduler takes ownership of |context|.
778 scheduler_.reset(new SyncScheduler(name_, context, new Syncer()));
779 }
780
781 bool signed_in = SignIn(credentials);
782
783 if (signed_in && scheduler()) {
784 scheduler()->Start(
785 browser_sync::SyncScheduler::CONFIGURATION_MODE, NULL);
786 }
787
788 initialized_ = true;
789
790 // Notify that initialization is complete.
791 ObserverList<SyncManager::Observer> temp_obs_list;
792 CopyObservers(&temp_obs_list);
793 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
794 OnInitializationComplete(
795 WeakHandle<JsBackend>(weak_ptr_factory_.GetWeakPtr())));
796
797 // The following calls check that initialized_ is true.
798
799 BootstrapEncryption(restored_key_for_bootstrapping);
800
801 sync_notifier_->AddObserver(this);
802
803 return signed_in;
804 }
805
806 void SyncManager::SyncInternal::BootstrapEncryption(
807 const std::string& restored_key_for_bootstrapping) {
808 // Cryptographer should only be accessed while holding a transaction.
809 ReadTransaction trans(FROM_HERE, GetUserShare());
810 Cryptographer* cryptographer = trans.GetCryptographer();
811
812 // Set the bootstrap token before bailing out if nigori node is not there.
813 // This could happen if server asked us to migrate nigri.
814 cryptographer->Bootstrap(restored_key_for_bootstrapping);
815 }
816
817 bool SyncManager::SyncInternal::UpdateCryptographerFromNigori() {
818 DCHECK(initialized_);
819 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
820 if (!lookup.good()) {
821 NOTREACHED() << "BootstrapEncryption: lookup not good so bailing out";
822 return false;
823 }
824 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI))
825 return false; // Should only happen during first time sync.
826
827 ReadTransaction trans(FROM_HERE, GetUserShare());
828 Cryptographer* cryptographer = trans.GetCryptographer();
829
830 ReadNode node(&trans);
831 if (!node.InitByTagLookup(kNigoriTag)) {
832 NOTREACHED();
833 return false;
834 }
835 Cryptographer::UpdateResult result =
836 cryptographer->Update(node.GetNigoriSpecifics());
837 if (result == Cryptographer::NEEDS_PASSPHRASE) {
838 ObserverList<SyncManager::Observer> temp_obs_list;
839 CopyObservers(&temp_obs_list);
840 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
841 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
842 }
843
844 allstatus_.SetCryptographerReady(cryptographer->is_ready());
845 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
846
847 return cryptographer->is_ready();
848 }
849
850 void SyncManager::SyncInternal::StartSyncingNormally() {
851 // Start the sync scheduler. This won't actually result in any
852 // syncing until at least the DirectoryManager broadcasts the OPENED
853 // event, and a valid server connection is detected.
854 if (scheduler()) // NULL during certain unittests.
855 scheduler()->Start(SyncScheduler::NORMAL_MODE, NULL);
856 }
857
858 bool SyncManager::SyncInternal::OpenDirectory() {
859 DCHECK(!initialized_) << "Should only happen once";
860
861 bool share_opened = dir_manager()->Open(username_for_share(), this);
862 DCHECK(share_opened);
863 if (!share_opened) {
864 ObserverList<SyncManager::Observer> temp_obs_list;
865 CopyObservers(&temp_obs_list);
866 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
867 OnStopSyncingPermanently());
868
869 LOG(ERROR) << "Could not open share for:" << username_for_share();
870 return false;
871 }
872
873 // Database has to be initialized for the guid to be available.
874 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
875 if (!lookup.good()) {
876 NOTREACHED();
877 return false;
878 }
879
880 connection_manager()->set_client_id(lookup->cache_guid());
881 lookup->AddTransactionObserver(&js_transaction_observer_);
882 return true;
883 }
884
885 bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) {
886 DCHECK(thread_checker_.CalledOnValidThread());
887 DCHECK(share_.name.empty());
888 share_.name = credentials.email;
889
890 VLOG(1) << "Signing in user: " << username_for_share();
891 if (!OpenDirectory())
892 return false;
893
894 // Retrieve and set the sync notifier state. This should be done
895 // only after OpenDirectory is called.
896 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
897 std::string unique_id;
898 std::string state;
899 if (lookup.good()) {
900 unique_id = lookup->cache_guid();
901 state = lookup->GetNotificationState();
902 VLOG(1) << "Read notification unique ID: " << unique_id;
903 if (VLOG_IS_ON(1)) {
904 std::string encoded_state;
905 base::Base64Encode(state, &encoded_state);
906 VLOG(1) << "Read notification state: " << encoded_state;
907 }
908 } else {
909 LOG(ERROR) << "Could not read notification unique ID/state";
910 }
911 sync_notifier_->SetUniqueId(unique_id);
912 sync_notifier_->SetState(state);
913
914 UpdateCredentials(credentials);
915 UpdateEnabledTypes();
916 return true;
917 }
918
919 void SyncManager::SyncInternal::UpdateCredentials(
920 const SyncCredentials& credentials) {
921 DCHECK(thread_checker_.CalledOnValidThread());
922 DCHECK_EQ(credentials.email, share_.name);
923 DCHECK(!credentials.email.empty());
924 DCHECK(!credentials.sync_token.empty());
925
926 observing_ip_address_changes_ = true;
927 if (connection_manager()->set_auth_token(credentials.sync_token)) {
928 sync_notifier_->UpdateCredentials(
929 credentials.email, credentials.sync_token);
930 if (!setup_for_test_mode_) {
931 CheckServerReachable();
932 }
933 }
934 }
935
936 void SyncManager::SyncInternal::UpdateEnabledTypes() {
937 DCHECK(thread_checker_.CalledOnValidThread());
938 ModelSafeRoutingInfo routes;
939 registrar_->GetModelSafeRoutingInfo(&routes);
940 syncable::ModelTypeSet enabled_types;
941 for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
942 it != routes.end(); ++it) {
943 enabled_types.insert(it->first);
944 }
945 sync_notifier_->UpdateEnabledTypes(enabled_types);
946 }
947
948 void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
949 ObserverList<SyncManager::Observer> temp_obs_list;
950 CopyObservers(&temp_obs_list);
951 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
952 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)));
953 }
954
955 void SyncManager::SyncInternal::SetPassphrase(
956 const std::string& passphrase, bool is_explicit) {
957 // We do not accept empty passphrases.
958 if (passphrase.empty()) {
959 VLOG(1) << "Rejecting empty passphrase.";
960 ObserverList<SyncManager::Observer> temp_obs_list;
961 CopyObservers(&temp_obs_list);
962 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
963 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED));
964 return;
965 }
966
967 // All accesses to the cryptographer are protected by a transaction.
968 WriteTransaction trans(FROM_HERE, GetUserShare());
969 Cryptographer* cryptographer = trans.GetCryptographer();
970 KeyParams params = {"localhost", "dummy", passphrase};
971
972 WriteNode node(&trans);
973 if (!node.InitByTagLookup(kNigoriTag)) {
974 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
975 NOTREACHED();
976 return;
977 }
978
979 if (cryptographer->has_pending_keys()) {
980 bool suceeded = false;
981
982 // See if the explicit flag matches what is set in nigori. If not we dont
983 // even try the passphrase. Note: This could mean that we wont try setting
984 // the gaia password as passphrase if custom is elected by the user. Which
985 // is fine because nigori node has all the old passwords in it.
986 if (node.GetNigoriSpecifics().using_explicit_passphrase() == is_explicit) {
987 if (cryptographer->DecryptPendingKeys(params)) {
988 suceeded = true;
989 } else {
990 VLOG(1) << "Passphrase failed to decrypt pending keys.";
991 }
992 } else {
993 VLOG(1) << "Not trying the passphrase because the explicit flags dont "
994 << "match. Nigori node's explicit flag is "
995 << node.GetNigoriSpecifics().using_explicit_passphrase();
996 }
997
998 if (!suceeded) {
999 ObserverList<SyncManager::Observer> temp_obs_list;
1000 CopyObservers(&temp_obs_list);
1001 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1002 OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED));
1003 return;
1004 }
1005
1006 // Nudge the syncer so that encrypted datatype updates that were waiting for
1007 // this passphrase get applied as soon as possible.
1008 RequestNudge(FROM_HERE);
1009 } else {
1010 VLOG(1) << "No pending keys, adding provided passphrase.";
1011
1012 // Prevent an implicit SetPassphrase request from changing an explicitly
1013 // set passphrase.
1014 if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase())
1015 return;
1016
1017 cryptographer->AddKey(params);
1018
1019 // TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require
1020 // messing with the Nigori node, because we can't call SetPassphrase until
1021 // download conditions are met vs Cryptographer init. It seems like it's
1022 // safe to defer this work.
1023 sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics());
1024 specifics.clear_encrypted();
1025 cryptographer->GetKeys(specifics.mutable_encrypted());
1026 specifics.set_using_explicit_passphrase(is_explicit);
1027 node.SetNigoriSpecifics(specifics);
1028 ReEncryptEverything(&trans);
1029 }
1030
1031 VLOG(1) << "Passphrase accepted, bootstrapping encryption.";
1032 std::string bootstrap_token;
1033 cryptographer->GetBootstrapToken(&bootstrap_token);
1034 ObserverList<SyncManager::Observer> temp_obs_list;
1035 CopyObservers(&temp_obs_list);
1036 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1037 OnPassphraseAccepted(bootstrap_token));
1038 }
1039
1040 bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
1041 ReadTransaction trans(FROM_HERE, &share_);
1042 ReadNode node(&trans);
1043 if (!node.InitByTagLookup(kNigoriTag)) {
1044 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
1045 NOTREACHED();
1046 return false;
1047 }
1048
1049 return node.GetNigoriSpecifics().using_explicit_passphrase();
1050 }
1051
1052 void SyncManager::SyncInternal::EncryptDataTypes(
1053 const syncable::ModelTypeSet& encrypted_types) {
1054 DCHECK(initialized_);
1055 VLOG(1) << "Attempting to encrypt datatypes "
1056 << syncable::ModelTypeSetToString(encrypted_types);
1057
1058 WriteTransaction trans(FROM_HERE, GetUserShare());
1059 WriteNode node(&trans);
1060 if (!node.InitByTagLookup(kNigoriTag)) {
1061 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not "
1062 << "found.";
1063 return;
1064 }
1065
1066 Cryptographer* cryptographer = trans.GetCryptographer();
1067
1068 if (!cryptographer->is_initialized()) {
1069 VLOG(1) << "Attempting to encrypt datatypes when cryptographer not "
1070 << "initialized, prompting for passphrase.";
1071 ObserverList<SyncManager::Observer> temp_obs_list;
1072 CopyObservers(&temp_obs_list);
1073 // TODO(zea): this isn't really decryption, but that's the only way we have
1074 // to prompt the user for a passsphrase. See http://crbug.com/91379.
1075 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1076 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
1077 return;
1078 }
1079
1080 // Update the Nigori node's set of encrypted datatypes.
1081 // Note, we merge the current encrypted types with those requested. Once a
1082 // datatypes is marked as needing encryption, it is never unmarked.
1083 sync_pb::NigoriSpecifics nigori;
1084 nigori.CopyFrom(node.GetNigoriSpecifics());
1085 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans);
1086 syncable::ModelTypeSet newly_encrypted_types;
1087 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(),
1088 encrypted_types.begin(), encrypted_types.end(),
1089 std::inserter(newly_encrypted_types,
1090 newly_encrypted_types.begin()));
1091 allstatus_.SetEncryptedTypes(newly_encrypted_types);
1092 if (newly_encrypted_types == current_encrypted_types) {
1093 // Set of encrypted types has not changed, just notify and return.
1094 ObserverList<SyncManager::Observer> temp_obs_list;
1095 CopyObservers(&temp_obs_list);
1096 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1097 OnEncryptionComplete(current_encrypted_types));
1098 return;
1099 }
1100 syncable::FillNigoriEncryptedTypes(newly_encrypted_types, &nigori);
1101 node.SetNigoriSpecifics(nigori);
1102
1103 cryptographer->SetEncryptedTypes(nigori);
1104
1105 // TODO(zea): only reencrypt this datatype? ReEncrypting everything is a
1106 // safer approach, and should not impact anything that is already encrypted
1107 // (redundant changes are ignored).
1108 ReEncryptEverything(&trans);
1109 return;
1110 }
1111
1112 // TODO(zea): Add unit tests that ensure no sync changes are made when not
1113 // needed.
1114 void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
1115 syncable::ModelTypeSet encrypted_types =
1116 GetEncryptedTypes(trans);
1117 ModelSafeRoutingInfo routes;
1118 registrar_->GetModelSafeRoutingInfo(&routes);
1119 std::string tag;
1120 for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin();
1121 iter != encrypted_types.end(); ++iter) {
1122 if (*iter == syncable::PASSWORDS || routes.count(*iter) == 0)
1123 continue;
1124 ReadNode type_root(trans);
1125 tag = syncable::ModelTypeToRootTag(*iter);
1126 if (!type_root.InitByTagLookup(tag)) {
1127 NOTREACHED();
1128 return;
1129 }
1130
1131 // Iterate through all children of this datatype.
1132 std::queue<int64> to_visit;
1133 int64 child_id = type_root.GetFirstChildId();
1134 to_visit.push(child_id);
1135 while (!to_visit.empty()) {
1136 child_id = to_visit.front();
1137 to_visit.pop();
1138 if (child_id == kInvalidId)
1139 continue;
1140
1141 WriteNode child(trans);
1142 if (!child.InitByIdLookup(child_id)) {
1143 NOTREACHED();
1144 continue;
1145 }
1146 if (child.GetIsFolder()) {
1147 to_visit.push(child.GetFirstChildId());
1148 }
1149 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) {
1150 // Rewrite the specifics of the node with encrypted data if necessary
1151 // (only rewrite the non-unique folders).
1152 child.ResetFromSpecifics();
1153 }
1154 to_visit.push(child.GetSuccessorId());
1155 }
1156 }
1157
1158 if (routes.count(syncable::PASSWORDS) > 0) {
1159 // Passwords are encrypted with their own legacy scheme.
1160 ReadNode passwords_root(trans);
1161 std::string passwords_tag =
1162 syncable::ModelTypeToRootTag(syncable::PASSWORDS);
1163 // It's possible we'll have the password routing info and not the password
1164 // root if we attempted to SetPassphrase before passwords was enabled.
1165 if (passwords_root.InitByTagLookup(passwords_tag)) {
1166 int64 child_id = passwords_root.GetFirstChildId();
1167 while (child_id != kInvalidId) {
1168 WriteNode child(trans);
1169 if (!child.InitByIdLookup(child_id)) {
1170 NOTREACHED();
1171 return;
1172 }
1173 child.SetPasswordSpecifics(child.GetPasswordSpecifics());
1174 child_id = child.GetSuccessorId();
1175 }
1176 }
1177 }
1178
1179 ObserverList<SyncManager::Observer> temp_obs_list;
1180 CopyObservers(&temp_obs_list);
1181 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1182 OnEncryptionComplete(encrypted_types));
1183 }
1184
1185 SyncManager::~SyncManager() {
1186 delete data_;
1187 }
1188
1189 void SyncManager::AddObserver(Observer* observer) {
1190 data_->AddObserver(observer);
1191 }
1192
1193 void SyncManager::RemoveObserver(Observer* observer) {
1194 data_->RemoveObserver(observer);
1195 }
1196
1197 void SyncManager::RequestEarlyExit() {
1198 data_->RequestEarlyExit();
1199 }
1200
1201 void SyncManager::SyncInternal::RequestEarlyExit() {
1202 if (scheduler()) {
1203 scheduler()->RequestEarlyExit();
1204 }
1205 }
1206
1207 void SyncManager::Shutdown() {
1208 data_->Shutdown();
1209 }
1210
1211 void SyncManager::SyncInternal::Shutdown() {
1212 DCHECK(thread_checker_.CalledOnValidThread());
1213
1214 // Prevent any in-flight method calls from running. Also
1215 // invalidates |weak_handle_this_|.
1216 weak_ptr_factory_.InvalidateWeakPtrs();
1217
1218 // Automatically stops the scheduler.
1219 scheduler_.reset();
1220
1221 SetJsEventHandler(WeakHandle<JsEventHandler>());
1222 RemoveObserver(&js_sync_manager_observer_);
1223
1224 if (sync_notifier_.get()) {
1225 sync_notifier_->RemoveObserver(this);
1226 }
1227 sync_notifier_.reset();
1228
1229 if (connection_manager_.get()) {
1230 connection_manager_->RemoveListener(this);
1231 }
1232 connection_manager_.reset();
1233
1234 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
1235 observing_ip_address_changes_ = false;
1236
1237 if (dir_manager()) {
1238 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1239 if (lookup.good()) {
1240 lookup->RemoveTransactionObserver(&js_transaction_observer_);
1241 } else {
1242 NOTREACHED();
1243 }
1244 dir_manager()->FinalSaveChangesForAll();
1245 dir_manager()->Close(username_for_share());
1246 }
1247
1248 // Reset the DirectoryManager and UserSettings so they relinquish sqlite
1249 // handles to backing files.
1250 share_.dir_manager.reset();
1251
1252 setup_for_test_mode_ = false;
1253 registrar_ = NULL;
1254
1255 initialized_ = false;
1256
1257 // We reset this here, since only now we know it will not be
1258 // accessed from other threads (since we shut down everything).
1259 weak_handle_this_.Reset();
1260 }
1261
1262 void SyncManager::SyncInternal::OnIPAddressChanged() {
1263 VLOG(1) << "IP address change detected";
1264 if (!observing_ip_address_changes_) {
1265 VLOG(1) << "IP address change dropped.";
1266 return;
1267 }
1268
1269 #if defined (OS_CHROMEOS)
1270 // TODO(tim): This is a hack to intentionally lose a race with flimflam at
1271 // shutdown, so we don't cause shutdown to wait for our http request.
1272 // http://crosbug.com/8429
1273 MessageLoop::current()->PostDelayedTask(
1274 FROM_HERE,
1275 base::Bind(&SyncInternal::OnIPAddressChangedImpl,
1276 weak_ptr_factory_.GetWeakPtr()),
1277 kChromeOSNetworkChangeReactionDelayHackMsec);
1278 #else
1279 OnIPAddressChangedImpl();
1280 #endif // defined(OS_CHROMEOS)
1281 }
1282
1283 void SyncManager::SyncInternal::OnIPAddressChangedImpl() {
1284 // TODO(akalin): CheckServerReachable() can block, which may cause
1285 // jank if we try to shut down sync. Fix this.
1286 connection_manager()->CheckServerReachable();
1287 }
1288
1289 void SyncManager::SyncInternal::OnServerConnectionEvent(
1290 const ServerConnectionEvent& event) {
1291 allstatus_.HandleServerConnectionEvent(event);
1292 if (event.connection_code ==
1293 browser_sync::HttpResponse::SERVER_CONNECTION_OK) {
1294 ObserverList<SyncManager::Observer> temp_obs_list;
1295 CopyObservers(&temp_obs_list);
1296 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1297 OnAuthError(AuthError::None()));
1298 }
1299
1300 if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) {
1301 observing_ip_address_changes_ = false;
1302 ObserverList<SyncManager::Observer> temp_obs_list;
1303 CopyObservers(&temp_obs_list);
1304 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1305 OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)));
1306 }
1307
1308 if (event.connection_code ==
1309 browser_sync::HttpResponse::SYNC_SERVER_ERROR) {
1310 ObserverList<SyncManager::Observer> temp_obs_list;
1311 CopyObservers(&temp_obs_list);
1312 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1313 OnAuthError(AuthError(AuthError::CONNECTION_FAILED)));
1314 }
1315 }
1316
1317 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent(
1318 const syncable::ModelTypeBitSet& models_with_changes) {
1319 // This notification happens immediately after the transaction mutex is
1320 // released. This allows work to be performed without blocking other threads
1321 // from acquiring a transaction.
1322 if (!HaveObservers())
1323 return;
1324
1325 // Call commit.
1326 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
1327 if (models_with_changes.test(i)) {
1328 ObserverList<SyncManager::Observer> temp_obs_list;
1329 CopyObservers(&temp_obs_list);
1330 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1331 OnChangesComplete(syncable::ModelTypeFromInt(i)));
1332 }
1333 }
1334 }
1335
1336 ModelTypeBitSet SyncManager::SyncInternal::HandleTransactionEndingChangeEvent(
1337 syncable::BaseTransaction* trans) {
1338 // This notification happens immediately before a syncable WriteTransaction
1339 // falls out of scope. It happens while the channel mutex is still held,
1340 // and while the transaction mutex is held, so it cannot be re-entrant.
1341 if (!HaveObservers() || ChangeBuffersAreEmpty())
1342 return ModelTypeBitSet();
1343
1344 // This will continue the WriteTransaction using a read only wrapper.
1345 // This is the last chance for read to occur in the WriteTransaction
1346 // that's closing. This special ReadTransaction will not close the
1347 // underlying transaction.
1348 ReadTransaction read_trans(GetUserShare(), trans);
1349
1350 syncable::ModelTypeBitSet models_with_changes;
1351 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
1352 if (change_buffers_[i].IsEmpty())
1353 continue;
1354
1355 vector<ChangeRecord> ordered_changes;
1356 change_buffers_[i].GetAllChangesInTreeOrder(&read_trans, &ordered_changes);
1357 if (!ordered_changes.empty()) {
1358 ObserverList<SyncManager::Observer> temp_obs_list;
1359 CopyObservers(&temp_obs_list);
1360 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1361 OnChangesApplied(syncable::ModelTypeFromInt(i), &read_trans,
1362 &ordered_changes[0], ordered_changes.size()));
1363 models_with_changes.set(i, true);
1364 }
1365 change_buffers_[i].Clear();
1366 }
1367 return models_with_changes;
1368 }
1369
1370 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi(
1371 const EntryKernelMutationSet& mutations,
1372 syncable::BaseTransaction* trans) {
1373 if (!scheduler()) {
1374 return;
1375 }
1376
1377 // We have been notified about a user action changing a sync model.
1378 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
1379 "CALCULATE_CHANGES called with unapplied old changes.";
1380
1381 // The mutated model type, or UNSPECIFIED if nothing was mutated.
1382 syncable::ModelType mutated_model_type = syncable::UNSPECIFIED;
1383
1384 // Find the first real mutation. We assume that only a single model
1385 // type is mutated per transaction.
1386 for (syncable::EntryKernelMutationSet::const_iterator it =
1387 mutations.begin(); it != mutations.end(); ++it) {
1388 if (!it->mutated.ref(syncable::IS_UNSYNCED)) {
1389 continue;
1390 }
1391
1392 syncable::ModelType model_type =
1393 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS));
1394 if (model_type < syncable::FIRST_REAL_MODEL_TYPE) {
1395 NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
1396 continue;
1397 }
1398
1399 // Found real mutation.
1400 if (mutated_model_type == syncable::UNSPECIFIED) {
1401 mutated_model_type = model_type;
1402 break;
1403 }
1404 }
1405
1406 // Nudge if necessary.
1407 if (mutated_model_type != syncable::UNSPECIFIED) {
1408 if (weak_handle_this_.IsInitialized()) {
1409 weak_handle_this_.Call(FROM_HERE,
1410 &SyncInternal::RequestNudgeForDataType,
1411 FROM_HERE,
1412 mutated_model_type);
1413 } else {
1414 NOTREACHED();
1415 }
1416 }
1417 }
1418
1419 void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id,
1420 syncable::ModelType type, ChangeReorderBuffer* buffer,
1421 Cryptographer* cryptographer, const syncable::EntryKernel& original,
1422 bool existed_before, bool exists_now) {
1423 // If this is a deletion and the datatype was encrypted, we need to decrypt it
1424 // and attach it to the buffer.
1425 if (!exists_now && existed_before) {
1426 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
1427 if (type == syncable::PASSWORDS) {
1428 // Passwords must use their own legacy ExtraPasswordChangeRecordData.
1429 scoped_ptr<sync_pb::PasswordSpecificsData> data(
1430 DecryptPasswordSpecifics(original_specifics, cryptographer));
1431 if (!data.get()) {
1432 NOTREACHED();
1433 return;
1434 }
1435 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
1436 } else if (original_specifics.has_encrypted()) {
1437 // All other datatypes can just create a new unencrypted specifics and
1438 // attach it.
1439 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
1440 if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
1441 NOTREACHED();
1442 return;
1443 }
1444 }
1445 buffer->SetSpecificsForId(id, original_specifics);
1446 }
1447 }
1448
1449 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
1450 const EntryKernelMutationSet& mutations,
1451 syncable::BaseTransaction* trans) {
1452 // We only expect one notification per sync step, so change_buffers_ should
1453 // contain no pending entries.
1454 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
1455 "CALCULATE_CHANGES called with unapplied old changes.";
1456
1457 Cryptographer* crypto = dir_manager()->GetCryptographer(trans);
1458 for (syncable::EntryKernelMutationSet::const_iterator it =
1459 mutations.begin(); it != mutations.end(); ++it) {
1460 bool existed_before = !it->original.ref(syncable::IS_DEL);
1461 bool exists_now = !it->mutated.ref(syncable::IS_DEL);
1462
1463 // Omit items that aren't associated with a model.
1464 syncable::ModelType type =
1465 syncable::GetModelTypeFromSpecifics(it->mutated.ref(SPECIFICS));
1466 if (type < syncable::FIRST_REAL_MODEL_TYPE)
1467 continue;
1468
1469 int64 id = it->original.ref(syncable::META_HANDLE);
1470 if (exists_now && !existed_before)
1471 change_buffers_[type].PushAddedItem(id);
1472 else if (!exists_now && existed_before)
1473 change_buffers_[type].PushDeletedItem(id);
1474 else if (exists_now && existed_before &&
1475 VisiblePropertiesDiffer(*it, crypto)) {
1476 change_buffers_[type].PushUpdatedItem(
1477 id, VisiblePositionsDiffer(*it));
1478 }
1479
1480 SetExtraChangeRecordData(id, type, &change_buffers_[type], crypto,
1481 it->original, existed_before, exists_now);
1482 }
1483 }
1484
1485 SyncManager::Status SyncManager::SyncInternal::GetStatus() {
1486 return allstatus_.status();
1487 }
1488
1489 void SyncManager::SyncInternal::RequestNudge(
1490 const tracked_objects::Location& location) {
1491 if (scheduler())
1492 scheduler()->ScheduleNudge(
1493 TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL,
1494 ModelTypeBitSet(), location);
1495 }
1496
1497 void SyncManager::SyncInternal::RequestNudgeForDataType(
1498 const tracked_objects::Location& nudge_location,
1499 const ModelType& type) {
1500 if (!scheduler()) {
1501 NOTREACHED();
1502 return;
1503 }
1504 base::TimeDelta nudge_delay;
1505 switch (type) {
1506 case syncable::PREFERENCES:
1507 nudge_delay =
1508 TimeDelta::FromMilliseconds(kPreferencesNudgeDelayMilliseconds);
1509 break;
1510 case syncable::SESSIONS:
1511 nudge_delay = scheduler()->sessions_commit_delay();
1512 break;
1513 default:
1514 nudge_delay =
1515 TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds);
1516 break;
1517 }
1518 syncable::ModelTypeBitSet types;
1519 types.set(type);
1520 scheduler()->ScheduleNudge(nudge_delay,
1521 browser_sync::NUDGE_SOURCE_LOCAL,
1522 types,
1523 nudge_location);
1524 }
1525
1526 void SyncManager::SyncInternal::OnSyncEngineEvent(
1527 const SyncEngineEvent& event) {
1528 DCHECK(thread_checker_.CalledOnValidThread());
1529 if (!HaveObservers()) {
1530 LOG(INFO)
1531 << "OnSyncEngineEvent returning because observers_.size() is zero";
1532 return;
1533 }
1534
1535 // Only send an event if this is due to a cycle ending and this cycle
1536 // concludes a canonical "sync" process; that is, based on what is known
1537 // locally we are "all happy" and up-to-date. There may be new changes on
1538 // the server, but we'll get them on a subsequent sync.
1539 //
1540 // Notifications are sent at the end of every sync cycle, regardless of
1541 // whether we should sync again.
1542 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
1543 ModelSafeRoutingInfo enabled_types;
1544 registrar_->GetModelSafeRoutingInfo(&enabled_types);
1545 {
1546 // Check to see if we need to notify the frontend that we have newly
1547 // encrypted types or that we require a passphrase.
1548 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
1549 Cryptographer* cryptographer = trans.GetCryptographer();
1550 // If we've completed a sync cycle and the cryptographer isn't ready
1551 // yet, prompt the user for a passphrase.
1552 if (cryptographer->has_pending_keys()) {
1553 VLOG(1) << "OnPassPhraseRequired Sent";
1554 ObserverList<SyncManager::Observer> temp_obs_list;
1555 CopyObservers(&temp_obs_list);
1556 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1557 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
1558 } else if (!cryptographer->is_ready() &&
1559 event.snapshot->initial_sync_ended.test(syncable::NIGORI)) {
1560 VLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
1561 << "ready";
1562 ObserverList<SyncManager::Observer> temp_obs_list;
1563 CopyObservers(&temp_obs_list);
1564 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1565 OnPassphraseRequired(sync_api::REASON_ENCRYPTION));
1566 }
1567
1568 allstatus_.SetCryptographerReady(cryptographer->is_ready());
1569 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
1570 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes());
1571
1572 // If everything is in order(we have the passphrase) then there is no
1573 // need to inform the listeners. They will just wait for sync
1574 // completion event and if no errors have been raised it means
1575 // encryption was succesful.
1576 }
1577
1578 if (!initialized_) {
1579 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not "
1580 << "initialized";
1581 return;
1582 }
1583
1584 if (!event.snapshot->has_more_to_sync) {
1585 VLOG(1) << "OnSyncCycleCompleted sent";
1586 ObserverList<SyncManager::Observer> temp_obs_list;
1587 CopyObservers(&temp_obs_list);
1588 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1589 OnSyncCycleCompleted(event.snapshot));
1590 }
1591
1592 // This is here for tests, which are still using p2p notifications.
1593 //
1594 // TODO(chron): Consider changing this back to track has_more_to_sync
1595 // only notify peers if a successful commit has occurred.
1596 bool is_notifiable_commit =
1597 (event.snapshot->syncer_status.num_successful_commits > 0);
1598 if (is_notifiable_commit) {
1599 allstatus_.IncrementNotifiableCommits();
1600 if (sync_notifier_.get()) {
1601 sync_notifier_->SendNotification();
1602 } else {
1603 VLOG(1) << "Not sending notification: sync_notifier_ is NULL";
1604 }
1605 }
1606 }
1607
1608 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
1609 ObserverList<SyncManager::Observer> temp_obs_list;
1610 CopyObservers(&temp_obs_list);
1611 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1612 OnStopSyncingPermanently());
1613 return;
1614 }
1615
1616 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) {
1617 ObserverList<SyncManager::Observer> temp_obs_list;
1618 CopyObservers(&temp_obs_list);
1619 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1620 OnClearServerDataSucceeded());
1621 return;
1622 }
1623
1624 if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) {
1625 ObserverList<SyncManager::Observer> temp_obs_list;
1626 CopyObservers(&temp_obs_list);
1627 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1628 OnClearServerDataFailed());
1629 return;
1630 }
1631
1632 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) {
1633 ObserverList<SyncManager::Observer> temp_obs_list;
1634 CopyObservers(&temp_obs_list);
1635 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1636 OnUpdatedToken(event.updated_token));
1637 return;
1638 }
1639 }
1640
1641 void SyncManager::SyncInternal::SetJsEventHandler(
1642 const WeakHandle<JsEventHandler>& event_handler) {
1643 js_event_handler_ = event_handler;
1644 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
1645 js_transaction_observer_.SetJsEventHandler(js_event_handler_);
1646 }
1647
1648 void SyncManager::SyncInternal::ProcessJsMessage(
1649 const std::string& name, const JsArgList& args,
1650 const WeakHandle<JsReplyHandler>& reply_handler) {
1651 if (!initialized_) {
1652 NOTREACHED();
1653 return;
1654 }
1655
1656 if (!reply_handler.IsInitialized()) {
1657 VLOG(1) << "Uninitialized reply handler; dropping unknown message "
1658 << name << " with args " << args.ToString();
1659 return;
1660 }
1661
1662 JsMessageHandler js_message_handler = js_message_handlers_[name];
1663 if (js_message_handler.is_null()) {
1664 VLOG(1) << "Dropping unknown message " << name
1665 << " with args " << args.ToString();
1666 return;
1667 }
1668
1669 reply_handler.Call(FROM_HERE,
1670 &JsReplyHandler::HandleJsReply,
1671 name, js_message_handler.Run(args));
1672 }
1673
1674 void SyncManager::SyncInternal::BindJsMessageHandler(
1675 const std::string& name,
1676 UnboundJsMessageHandler unbound_message_handler) {
1677 js_message_handlers_[name] =
1678 base::Bind(unbound_message_handler, base::Unretained(this));
1679 }
1680
1681 DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue(
1682 const NotificationInfoMap& notification_info) {
1683 DictionaryValue* value = new DictionaryValue();
1684
1685 for (NotificationInfoMap::const_iterator it = notification_info.begin();
1686 it != notification_info.end(); ++it) {
1687 const std::string& model_type_str =
1688 syncable::ModelTypeToString(it->first);
1689 value->Set(model_type_str, it->second.ToValue());
1690 }
1691
1692 return value;
1693 }
1694
1695 JsArgList SyncManager::SyncInternal::GetNotificationState(
1696 const JsArgList& args) {
1697 bool notifications_enabled = allstatus_.status().notifications_enabled;
1698 ListValue return_args;
1699 return_args.Append(Value::CreateBooleanValue(notifications_enabled));
1700 return JsArgList(&return_args);
1701 }
1702
1703 JsArgList SyncManager::SyncInternal::GetNotificationInfo(
1704 const JsArgList& args) {
1705 ListValue return_args;
1706 return_args.Append(NotificationInfoToValue(notification_info_map_));
1707 return JsArgList(&return_args);
1708 }
1709
1710 JsArgList SyncManager::SyncInternal::GetRootNodeDetails(
1711 const JsArgList& args) {
1712 ReadTransaction trans(FROM_HERE, GetUserShare());
1713 ReadNode root(&trans);
1714 root.InitByRootLookup();
1715 ListValue return_args;
1716 return_args.Append(root.GetDetailsAsValue());
1717 return JsArgList(&return_args);
1718 }
1719
1720 namespace {
1721
1722 int64 GetId(const ListValue& ids, int i) {
1723 std::string id_str;
1724 if (!ids.GetString(i, &id_str)) {
1725 return kInvalidId;
1726 }
1727 int64 id = kInvalidId;
1728 if (!base::StringToInt64(id_str, &id)) {
1729 return kInvalidId;
1730 }
1731 return id;
1732 }
1733
1734 JsArgList GetNodeInfoById(const JsArgList& args,
1735 UserShare* user_share,
1736 DictionaryValue* (BaseNode::*info_getter)() const) {
1737 CHECK(info_getter);
1738 ListValue return_args;
1739 ListValue* node_summaries = new ListValue();
1740 return_args.Append(node_summaries);
1741 ListValue* id_list = NULL;
1742 ReadTransaction trans(FROM_HERE, user_share);
1743 if (args.Get().GetList(0, &id_list)) {
1744 CHECK(id_list);
1745 for (size_t i = 0; i < id_list->GetSize(); ++i) {
1746 int64 id = GetId(*id_list, i);
1747 if (id == kInvalidId) {
1748 continue;
1749 }
1750 ReadNode node(&trans);
1751 if (!node.InitByIdLookup(id)) {
1752 continue;
1753 }
1754 node_summaries->Append((node.*info_getter)());
1755 }
1756 }
1757 return JsArgList(&return_args);
1758 }
1759
1760 } // namespace
1761
1762 JsArgList SyncManager::SyncInternal::GetNodeSummariesById(
1763 const JsArgList& args) {
1764 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue);
1765 }
1766
1767 JsArgList SyncManager::SyncInternal::GetNodeDetailsById(
1768 const JsArgList& args) {
1769 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue);
1770 }
1771
1772 JsArgList SyncManager::SyncInternal::GetChildNodeIds(
1773 const JsArgList& args) {
1774 ListValue return_args;
1775 ListValue* child_ids = new ListValue();
1776 return_args.Append(child_ids);
1777 int64 id = GetId(args.Get(), 0);
1778 if (id != kInvalidId) {
1779 ReadTransaction trans(FROM_HERE, GetUserShare());
1780 syncable::Directory::ChildHandles child_handles;
1781 trans.GetLookup()->GetChildHandlesByHandle(trans.GetWrappedTrans(),
1782 id, &child_handles);
1783 for (syncable::Directory::ChildHandles::const_iterator it =
1784 child_handles.begin(); it != child_handles.end(); ++it) {
1785 child_ids->Append(Value::CreateStringValue(
1786 base::Int64ToString(*it)));
1787 }
1788 }
1789 return JsArgList(&return_args);
1790 }
1791
1792 JsArgList SyncManager::SyncInternal::FindNodesContainingString(
1793 const JsArgList& args) {
1794 std::string query;
1795 ListValue return_args;
1796 if (!args.Get().GetString(0, &query)) {
1797 return_args.Append(new ListValue());
1798 return JsArgList(&return_args);
1799 }
1800
1801 // Convert the query string to lower case to perform case insensitive
1802 // searches.
1803 std::string lowercase_query = query;
1804 StringToLowerASCII(&lowercase_query);
1805
1806 ListValue* result = new ListValue();
1807 return_args.Append(result);
1808
1809 ReadTransaction trans(FROM_HERE, GetUserShare());
1810 std::vector<const syncable::EntryKernel*> entry_kernels;
1811 trans.GetLookup()->GetAllEntryKernels(trans.GetWrappedTrans(),
1812 &entry_kernels);
1813
1814 for (std::vector<const syncable::EntryKernel*>::const_iterator it =
1815 entry_kernels.begin(); it != entry_kernels.end(); ++it) {
1816 if ((*it)->ContainsString(lowercase_query)) {
1817 result->Append(new StringValue(base::Int64ToString(
1818 (*it)->ref(syncable::META_HANDLE))));
1819 }
1820 }
1821
1822 return JsArgList(&return_args);
1823 }
1824
1825 void SyncManager::SyncInternal::OnNotificationStateChange(
1826 bool notifications_enabled) {
1827 VLOG(1) << "P2P: Notifications enabled = "
1828 << (notifications_enabled ? "true" : "false");
1829 allstatus_.SetNotificationsEnabled(notifications_enabled);
1830 if (scheduler()) {
1831 scheduler()->set_notifications_enabled(notifications_enabled);
1832 }
1833 if (js_event_handler_.IsInitialized()) {
1834 DictionaryValue details;
1835 details.Set("enabled", Value::CreateBooleanValue(notifications_enabled));
1836 js_event_handler_.Call(FROM_HERE,
1837 &JsEventHandler::HandleJsEvent,
1838 "onNotificationStateChange",
1839 JsEventDetails(&details));
1840 }
1841 }
1842
1843 void SyncManager::SyncInternal::UpdateNotificationInfo(
1844 const syncable::ModelTypePayloadMap& type_payloads) {
1845 for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin();
1846 it != type_payloads.end(); ++it) {
1847 NotificationInfo* info = &notification_info_map_[it->first];
1848 info->total_count++;
1849 info->payload = it->second;
1850 }
1851 }
1852
1853 void SyncManager::SyncInternal::OnIncomingNotification(
1854 const syncable::ModelTypePayloadMap& type_payloads) {
1855 if (!type_payloads.empty()) {
1856 if (scheduler()) {
1857 scheduler()->ScheduleNudgeWithPayloads(
1858 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec),
1859 browser_sync::NUDGE_SOURCE_NOTIFICATION,
1860 type_payloads, FROM_HERE);
1861 }
1862 allstatus_.IncrementNotificationsReceived();
1863 UpdateNotificationInfo(type_payloads);
1864 } else {
1865 LOG(WARNING) << "Sync received notification without any type information.";
1866 }
1867
1868 if (js_event_handler_.IsInitialized()) {
1869 DictionaryValue details;
1870 ListValue* changed_types = new ListValue();
1871 details.Set("changedTypes", changed_types);
1872 for (syncable::ModelTypePayloadMap::const_iterator
1873 it = type_payloads.begin();
1874 it != type_payloads.end(); ++it) {
1875 const std::string& model_type_str =
1876 syncable::ModelTypeToString(it->first);
1877 changed_types->Append(Value::CreateStringValue(model_type_str));
1878 }
1879 js_event_handler_.Call(FROM_HERE,
1880 &JsEventHandler::HandleJsEvent,
1881 "onIncomingNotification",
1882 JsEventDetails(&details));
1883 }
1884 }
1885
1886 void SyncManager::SyncInternal::StoreState(
1887 const std::string& state) {
1888 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1889 if (!lookup.good()) {
1890 LOG(ERROR) << "Could not write notification state";
1891 // TODO(akalin): Propagate result callback all the way to this
1892 // function and call it with "false" to signal failure.
1893 return;
1894 }
1895 if (VLOG_IS_ON(1)) {
1896 std::string encoded_state;
1897 base::Base64Encode(state, &encoded_state);
1898 VLOG(1) << "Writing notification state: " << encoded_state;
1899 }
1900 lookup->SetNotificationState(state);
1901 lookup->SaveChanges();
1902 }
1903
1904 // Note: it is possible that an observer will remove itself after we have made
1905 // a copy, but before the copy is consumed. This could theoretically result
1906 // in accessing a garbage pointer, but can only occur when an about:sync window
1907 // is closed in the middle of a notification.
1908 // See crbug.com/85481.
1909 void SyncManager::SyncInternal::CopyObservers(
1910 ObserverList<SyncManager::Observer>* observers_copy) {
1911 DCHECK_EQ(0U, observers_copy->size());
1912 base::AutoLock lock(observers_lock_);
1913 if (observers_.size() == 0)
1914 return;
1915 ObserverListBase<SyncManager::Observer>::Iterator it(observers_);
1916 SyncManager::Observer* obs;
1917 while ((obs = it.GetNext()) != NULL)
1918 observers_copy->AddObserver(obs);
1919 }
1920
1921 bool SyncManager::SyncInternal::HaveObservers() const {
1922 base::AutoLock lock(observers_lock_);
1923 return observers_.size() > 0;
1924 }
1925
1926 void SyncManager::SyncInternal::AddObserver(
1927 SyncManager::Observer* observer) {
1928 base::AutoLock lock(observers_lock_);
1929 observers_.AddObserver(observer);
1930 }
1931
1932 void SyncManager::SyncInternal::RemoveObserver(
1933 SyncManager::Observer* observer) {
1934 base::AutoLock lock(observers_lock_);
1935 observers_.RemoveObserver(observer);
1936 }
1937
1938 SyncManager::Status::Summary SyncManager::GetStatusSummary() const {
1939 return data_->GetStatus().summary;
1940 }
1941
1942 SyncManager::Status SyncManager::GetDetailedStatus() const {
1943 return data_->GetStatus();
1944 }
1945
1946 SyncManager::SyncInternal* SyncManager::GetImpl() const { return data_; }
1947
1948 void SyncManager::SaveChanges() {
1949 data_->SaveChanges();
1950 }
1951
1952 void SyncManager::SyncInternal::SaveChanges() {
1953 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1954 if (!lookup.good()) {
1955 DCHECK(false) << "ScopedDirLookup creation failed; Unable to SaveChanges";
1956 return;
1957 }
1958 lookup->SaveChanges();
1959 }
1960
1961 UserShare* SyncManager::GetUserShare() const {
1962 return data_->GetUserShare();
1963 }
1964
1965 void SyncManager::RefreshEncryption() {
1966 if (data_->UpdateCryptographerFromNigori())
1967 data_->EncryptDataTypes(syncable::ModelTypeSet());
1968 }
1969
1970 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const {
1971 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
1972 return GetEncryptedTypes(&trans);
1973 }
1974
1975 bool SyncManager::HasUnsyncedItems() const {
1976 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
1977 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
1978 }
1979
1980 void SyncManager::LogUnsyncedItems(int level) const {
1981 std::vector<int64> unsynced_handles;
1982 sync_api::ReadTransaction trans(FROM_HERE, GetUserShare());
1983 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles(
1984 trans.GetWrappedTrans(), &unsynced_handles);
1985
1986 for (std::vector<int64>::const_iterator it = unsynced_handles.begin();
1987 it != unsynced_handles.end(); ++it) {
1988 ReadNode node(&trans);
1989 if (node.InitByIdLookup(*it)) {
1990 scoped_ptr<DictionaryValue> value(node.GetDetailsAsValue());
1991 std::string info;
1992 base::JSONWriter::Write(value.get(), true, &info);
1993 VLOG(level) << info;
1994 }
1995 }
1996 }
1997
1998 void SyncManager::TriggerOnNotificationStateChangeForTest(
1999 bool notifications_enabled) {
2000 data_->OnNotificationStateChange(notifications_enabled);
2001 }
2002
2003 void SyncManager::TriggerOnIncomingNotificationForTest(
2004 const syncable::ModelTypeBitSet& model_types) {
2005 syncable::ModelTypePayloadMap model_types_with_payloads =
2006 syncable::ModelTypePayloadMapFromBitSet(model_types,
2007 std::string());
2008
2009 data_->OnIncomingNotification(model_types_with_payloads);
2010 }
2011
2012 } // namespace sync_api
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698