Index: chrome/browser/sync/internal_api/sync_manager.cc |
diff --git a/chrome/browser/sync/internal_api/sync_manager.cc b/chrome/browser/sync/internal_api/sync_manager.cc |
deleted file mode 100644 |
index 6e6f17664eaebac9f8336f9fb3ad74c1feb10ae8..0000000000000000000000000000000000000000 |
--- a/chrome/browser/sync/internal_api/sync_manager.cc |
+++ /dev/null |
@@ -1,2575 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/sync/internal_api/sync_manager.h" |
- |
-#include <string> |
- |
-#include "base/base64.h" |
-#include "base/bind.h" |
-#include "base/callback.h" |
-#include "base/command_line.h" |
-#include "base/compiler_specific.h" |
-#include "base/json/json_writer.h" |
-#include "base/memory/ref_counted.h" |
-#include "base/metrics/histogram.h" |
-#include "base/observer_list.h" |
-#include "base/string_number_conversions.h" |
-#include "base/values.h" |
-#include "chrome/browser/sync/internal_api/all_status.h" |
-#include "chrome/browser/sync/internal_api/base_node.h" |
-#include "chrome/browser/sync/internal_api/change_reorder_buffer.h" |
-#include "chrome/browser/sync/internal_api/configure_reason.h" |
-#include "chrome/browser/sync/internal_api/debug_info_event_listener.h" |
-#include "chrome/browser/sync/internal_api/js_mutation_event_observer.h" |
-#include "chrome/browser/sync/internal_api/js_sync_manager_observer.h" |
-#include "chrome/browser/sync/internal_api/read_node.h" |
-#include "chrome/browser/sync/internal_api/read_transaction.h" |
-#include "chrome/browser/sync/internal_api/syncapi_internal.h" |
-#include "chrome/browser/sync/internal_api/syncapi_server_connection_manager.h" |
-#include "chrome/browser/sync/internal_api/user_share.h" |
-#include "chrome/browser/sync/internal_api/write_node.h" |
-#include "chrome/browser/sync/internal_api/write_transaction.h" |
-#include "net/base/network_change_notifier.h" |
-#include "sync/engine/net/server_connection_manager.h" |
-#include "sync/engine/nigori_util.h" |
-#include "sync/engine/polling_constants.h" |
-#include "sync/engine/sync_scheduler.h" |
-#include "sync/engine/syncer_types.h" |
-#include "sync/js/js_arg_list.h" |
-#include "sync/js/js_backend.h" |
-#include "sync/js/js_event_details.h" |
-#include "sync/js/js_event_handler.h" |
-#include "sync/js/js_reply_handler.h" |
-#include "sync/notifier/sync_notifier.h" |
-#include "sync/notifier/sync_notifier_observer.h" |
-#include "sync/protocol/encryption.pb.h" |
-#include "sync/protocol/proto_value_conversions.h" |
-#include "sync/protocol/sync.pb.h" |
-#include "sync/syncable/directory_change_delegate.h" |
-#include "sync/syncable/model_type.h" |
-#include "sync/syncable/model_type_payload_map.h" |
-#include "sync/syncable/syncable.h" |
-#include "sync/util/cryptographer.h" |
-#include "sync/util/get_session_name.h" |
-#include "sync/util/time.h" |
- |
-using base::TimeDelta; |
-using browser_sync::AllStatus; |
-using browser_sync::Cryptographer; |
-using browser_sync::Encryptor; |
-using browser_sync::JsArgList; |
-using browser_sync::JsBackend; |
-using browser_sync::JsEventDetails; |
-using browser_sync::JsEventHandler; |
-using browser_sync::JsEventHandler; |
-using browser_sync::JsReplyHandler; |
-using browser_sync::JsMutationEventObserver; |
-using browser_sync::JsSyncManagerObserver; |
-using browser_sync::ModelSafeWorkerRegistrar; |
-using browser_sync::kNigoriTag; |
-using browser_sync::KeyParams; |
-using browser_sync::ModelSafeRoutingInfo; |
-using browser_sync::ReportUnrecoverableErrorFunction; |
-using browser_sync::ServerConnectionEvent; |
-using browser_sync::ServerConnectionEventListener; |
-using browser_sync::SyncEngineEvent; |
-using browser_sync::SyncEngineEventListener; |
-using browser_sync::SyncScheduler; |
-using browser_sync::Syncer; |
-using browser_sync::UnrecoverableErrorHandler; |
-using browser_sync::WeakHandle; |
-using browser_sync::sessions::SyncSessionContext; |
-using syncable::ImmutableWriteTransactionInfo; |
-using syncable::ModelType; |
-using syncable::ModelTypeSet; |
-using syncable::SPECIFICS; |
-using sync_pb::GetUpdatesCallerInfo; |
- |
-namespace { |
- |
-// Delays for syncer nudges. |
-static const int kSyncRefreshDelayMsec = 500; |
-static const int kSyncSchedulerDelayMsec = 250; |
- |
-GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( |
- sync_api::ConfigureReason reason) { |
- switch (reason) { |
- case sync_api::CONFIGURE_REASON_RECONFIGURATION: |
- return GetUpdatesCallerInfo::RECONFIGURATION; |
- case sync_api::CONFIGURE_REASON_MIGRATION: |
- return GetUpdatesCallerInfo::MIGRATION; |
- case sync_api::CONFIGURE_REASON_NEW_CLIENT: |
- return GetUpdatesCallerInfo::NEW_CLIENT; |
- case sync_api::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE: |
- return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE; |
- default: |
- NOTREACHED(); |
- } |
- |
- return GetUpdatesCallerInfo::UNKNOWN; |
-} |
- |
-// The maximum number of times we will automatically overwrite the nigori node |
-// because the encryption keys don't match (per chrome instantiation). |
-static const int kNigoriOverwriteLimit = 10; |
- |
-} // namespace |
- |
-namespace sync_api { |
- |
-const int SyncManager::kDefaultNudgeDelayMilliseconds = 200; |
-const int SyncManager::kPreferencesNudgeDelayMilliseconds = 2000; |
- |
-// Maximum count and size for traffic recorder. |
-const unsigned int kMaxMessagesToRecord = 10; |
-const unsigned int kMaxMessageSizeToRecord = 5 * 1024; |
- |
-////////////////////////////////////////////////////////////////////////// |
-// SyncManager's implementation: SyncManager::SyncInternal |
-class SyncManager::SyncInternal |
- : public net::NetworkChangeNotifier::IPAddressObserver, |
- public browser_sync::Cryptographer::Observer, |
- public sync_notifier::SyncNotifierObserver, |
- public JsBackend, |
- public SyncEngineEventListener, |
- public ServerConnectionEventListener, |
- public syncable::DirectoryChangeDelegate { |
- public: |
- explicit SyncInternal(const std::string& name) |
- : name_(name), |
- weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
- enable_sync_tabs_for_other_clients_(false), |
- registrar_(NULL), |
- change_delegate_(NULL), |
- initialized_(false), |
- testing_mode_(NON_TEST), |
- observing_ip_address_changes_(false), |
- traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord), |
- encryptor_(NULL), |
- unrecoverable_error_handler_(NULL), |
- report_unrecoverable_error_function_(NULL), |
- created_on_loop_(MessageLoop::current()), |
- nigori_overwrite_count_(0) { |
- // Pre-fill |notification_info_map_|. |
- for (int i = syncable::FIRST_REAL_MODEL_TYPE; |
- i < syncable::MODEL_TYPE_COUNT; ++i) { |
- notification_info_map_.insert( |
- std::make_pair(syncable::ModelTypeFromInt(i), NotificationInfo())); |
- } |
- |
- // Bind message handlers. |
- BindJsMessageHandler( |
- "getNotificationState", |
- &SyncManager::SyncInternal::GetNotificationState); |
- BindJsMessageHandler( |
- "getNotificationInfo", |
- &SyncManager::SyncInternal::GetNotificationInfo); |
- BindJsMessageHandler( |
- "getRootNodeDetails", |
- &SyncManager::SyncInternal::GetRootNodeDetails); |
- BindJsMessageHandler( |
- "getNodeSummariesById", |
- &SyncManager::SyncInternal::GetNodeSummariesById); |
- BindJsMessageHandler( |
- "getNodeDetailsById", |
- &SyncManager::SyncInternal::GetNodeDetailsById); |
- BindJsMessageHandler( |
- "getAllNodes", |
- &SyncManager::SyncInternal::GetAllNodes); |
- BindJsMessageHandler( |
- "getChildNodeIds", |
- &SyncManager::SyncInternal::GetChildNodeIds); |
- BindJsMessageHandler( |
- "getClientServerTraffic", |
- &SyncManager::SyncInternal::GetClientServerTraffic); |
- } |
- |
- virtual ~SyncInternal() { |
- CHECK(!initialized_); |
- } |
- |
- bool Init(const FilePath& database_location, |
- const WeakHandle<JsEventHandler>& event_handler, |
- const std::string& sync_server_and_path, |
- int port, |
- bool use_ssl, |
- const scoped_refptr<base::TaskRunner>& blocking_task_runner, |
- HttpPostProviderFactory* post_factory, |
- ModelSafeWorkerRegistrar* model_safe_worker_registrar, |
- browser_sync::ExtensionsActivityMonitor* |
- extensions_activity_monitor, |
- ChangeDelegate* change_delegate, |
- const std::string& user_agent, |
- const SyncCredentials& credentials, |
- bool enable_sync_tabs_for_other_clients, |
- sync_notifier::SyncNotifier* sync_notifier, |
- const std::string& restored_key_for_bootstrapping, |
- TestingMode testing_mode, |
- Encryptor* encryptor, |
- UnrecoverableErrorHandler* unrecoverable_error_handler, |
- ReportUnrecoverableErrorFunction |
- report_unrecoverable_error_function); |
- |
- // Sign into sync with given credentials. |
- // We do not verify the tokens given. After this call, the tokens are set |
- // and the sync DB is open. True if successful, false if something |
- // went wrong. |
- bool SignIn(const SyncCredentials& credentials); |
- |
- // Update tokens that we're using in Sync. Email must stay the same. |
- void UpdateCredentials(const SyncCredentials& credentials); |
- |
- // Called when the user disables or enables a sync type. |
- void UpdateEnabledTypes(); |
- |
- // Conditionally sets the flag in the Nigori node which instructs other |
- // clients to start syncing tabs. |
- void MaybeSetSyncTabsInNigoriNode(ModelTypeSet enabled_types); |
- |
- // Tell the sync engine to start the syncing process. |
- void StartSyncingNormally(); |
- |
- // Whether or not the Nigori node is encrypted using an explicit passphrase. |
- bool IsUsingExplicitPassphrase(); |
- |
- // Update the Cryptographer from the current nigori node and write back any |
- // necessary changes to the nigori node. We also detect missing encryption |
- // keys and write them into the nigori node. |
- // Also updates or adds the device information into the nigori node. |
- // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so |
- // should only be called after syncapi is fully initialized. |
- // Calls the callback argument with true if cryptographer is ready, false |
- // otherwise. |
- void UpdateCryptographerAndNigori( |
- const std::string& chrome_version, |
- const base::Closure& done_callback); |
- |
- // Stores the current set of encryption keys (if the cryptographer is ready) |
- // and encrypted types into the nigori node. |
- void UpdateNigoriEncryptionState(Cryptographer* cryptographer, |
- WriteNode* nigori_node); |
- |
- // Updates the nigori node with any new encrypted types and then |
- // encrypts the nodes for those new data types as well as other |
- // nodes that should be encrypted but aren't. Triggers |
- // OnPassphraseRequired if the cryptographer isn't ready. |
- void RefreshEncryption(); |
- |
- // Re-encrypts the encrypted data types using the passed passphrase, and sets |
- // a flag in the nigori node specifying whether the current passphrase is |
- // explicit (custom passphrase) or non-explicit (GAIA). If the existing |
- // encryption passphrase is "explicit", the data cannot be re-encrypted and |
- // SetEncryptionPassphrase will do nothing. |
- // If !is_explicit and there are pending keys, we will attempt to decrypt them |
- // using this passphrase. If this fails, we will save this encryption key to |
- // be applied later after the pending keys are resolved. |
- // Calls FinishSetPassphrase at the end, which notifies observers of the |
- // result of the set passphrase operation, updates the nigori node, and does |
- // re-encryption. |
- void SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit); |
- |
- // Provides a passphrase for decrypting the user's existing sync data. Calls |
- // FinishSetPassphrase at the end, which notifies observers of the result of |
- // the set passphrase operation, updates the nigori node, and does |
- // re-encryption. |
- void SetDecryptionPassphrase(const std::string& passphrase); |
- |
- // The final step of SetEncryptionPassphrase and SetDecryptionPassphrase that |
- // notifies observers of the result of the set passphrase operation, updates |
- // the nigori node, and does re-encryption. |
- // |success|: true if the operation was successful and false otherwise. If |
- // success == false, we send an OnPassphraseRequired notification. |
- // |bootstrap_token|: used to inform observers if the cryptographer's |
- // bootstrap token was updated. |
- // |is_explicit|: used to differentiate between a custom passphrase (true) and |
- // a GAIA passphrase that is implicitly used for encryption |
- // (false). |
- // |trans| and |nigori_node|: used to access data in the cryptographer. |
- void FinishSetPassphrase( |
- bool success, |
- const std::string& bootstrap_token, |
- bool is_explicit, |
- WriteTransaction* trans, |
- WriteNode* nigori_node); |
- |
- // Call periodically from a database-safe thread to persist recent changes |
- // to the syncapi model. |
- void SaveChanges(); |
- |
- // DirectoryChangeDelegate implementation. |
- // This listener is called upon completion of a syncable transaction, and |
- // builds the list of sync-engine initiated changes that will be forwarded to |
- // the SyncManager's Observers. |
- virtual void HandleTransactionCompleteChangeEvent( |
- ModelTypeSet models_with_changes) OVERRIDE; |
- virtual ModelTypeSet HandleTransactionEndingChangeEvent( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) OVERRIDE; |
- virtual void HandleCalculateChangesChangeEventFromSyncApi( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) OVERRIDE; |
- virtual void HandleCalculateChangesChangeEventFromSyncer( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) OVERRIDE; |
- |
- // Open the directory named with username_for_share |
- bool OpenDirectory(); |
- |
- // Cryptographer::Observer implementation. |
- virtual void OnEncryptedTypesChanged( |
- syncable::ModelTypeSet encrypted_types, |
- bool encrypt_everything) OVERRIDE; |
- |
- // SyncNotifierObserver implementation. |
- virtual void OnNotificationStateChange( |
- bool notifications_enabled) OVERRIDE; |
- |
- virtual void OnIncomingNotification( |
- const syncable::ModelTypePayloadMap& type_payloads, |
- sync_notifier::IncomingNotificationSource source) OVERRIDE; |
- |
- virtual void StoreState(const std::string& cookie) OVERRIDE; |
- |
- void AddObserver(SyncManager::Observer* observer); |
- void RemoveObserver(SyncManager::Observer* observer); |
- |
- // Accessors for the private members. |
- syncable::Directory* directory() { return share_.directory.get(); } |
- SyncAPIServerConnectionManager* connection_manager() { |
- return connection_manager_.get(); |
- } |
- SyncScheduler* scheduler() const { return scheduler_.get(); } |
- UserShare* GetUserShare() { |
- DCHECK(initialized_); |
- return &share_; |
- } |
- |
- // Return the currently active (validated) username for use with syncable |
- // types. |
- const std::string& username_for_share() const { |
- return share_.name; |
- } |
- |
- Status GetStatus(); |
- |
- void RequestNudge(const tracked_objects::Location& nudge_location); |
- |
- void RequestNudgeForDataTypes( |
- const tracked_objects::Location& nudge_location, |
- ModelTypeSet type); |
- |
- TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type); |
- |
- void NotifyCryptographerState(Cryptographer* cryptographer); |
- |
- // See SyncManager::Shutdown* for information. |
- void StopSyncingForShutdown(const base::Closure& callback); |
- void ShutdownOnSyncThread(); |
- |
- // If this is a deletion for a password, sets the legacy |
- // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets |
- // |buffer|'s specifics field to contain the unencrypted data. |
- void SetExtraChangeRecordData(int64 id, |
- syncable::ModelType type, |
- ChangeReorderBuffer* buffer, |
- Cryptographer* cryptographer, |
- const syncable::EntryKernel& original, |
- bool existed_before, |
- bool exists_now); |
- |
- // Called only by our NetworkChangeNotifier. |
- virtual void OnIPAddressChanged() OVERRIDE; |
- |
- bool InitialSyncEndedForAllEnabledTypes() { |
- syncable::ModelTypeSet types; |
- ModelSafeRoutingInfo enabled_types; |
- registrar_->GetModelSafeRoutingInfo(&enabled_types); |
- for (ModelSafeRoutingInfo::const_iterator i = enabled_types.begin(); |
- i != enabled_types.end(); ++i) { |
- types.Put(i->first); |
- } |
- |
- return InitialSyncEndedForTypes(types, &share_); |
- } |
- |
- // SyncEngineEventListener implementation. |
- virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE; |
- |
- // ServerConnectionEventListener implementation. |
- virtual void OnServerConnectionEvent( |
- const ServerConnectionEvent& event) OVERRIDE; |
- |
- // JsBackend implementation. |
- virtual void SetJsEventHandler( |
- const WeakHandle<JsEventHandler>& event_handler) OVERRIDE; |
- virtual void ProcessJsMessage( |
- const std::string& name, const JsArgList& args, |
- const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE; |
- |
- private: |
- struct NotificationInfo { |
- int total_count; |
- std::string payload; |
- |
- NotificationInfo() : total_count(0) {} |
- |
- ~NotificationInfo() {} |
- |
- // Returned pointer owned by the caller. |
- DictionaryValue* ToValue() const { |
- DictionaryValue* value = new DictionaryValue(); |
- value->SetInteger("totalCount", total_count); |
- value->SetString("payload", payload); |
- return value; |
- } |
- }; |
- |
- typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap; |
- typedef JsArgList |
- (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&); |
- typedef base::Callback<JsArgList(const JsArgList&)> JsMessageHandler; |
- typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap; |
- |
- // Internal callback of UpdateCryptographerAndNigoriCallback. |
- void UpdateCryptographerAndNigoriCallback( |
- const std::string& chrome_version, |
- const base::Closure& done_callback, |
- const std::string& session_name); |
- |
- // Determine if the parents or predecessors differ between the old and new |
- // versions of an entry stored in |a| and |b|. Note that a node's index may |
- // change without its NEXT_ID changing if the node at NEXT_ID also moved (but |
- // the relative order is unchanged). To handle such cases, we rely on the |
- // caller to treat a position update on any sibling as updating the positions |
- // of all siblings. |
- static bool VisiblePositionsDiffer( |
- const syncable::EntryKernelMutation& mutation) { |
- const syncable::EntryKernel& a = mutation.original; |
- const syncable::EntryKernel& b = mutation.mutated; |
- // If the datatype isn't one where the browser model cares about position, |
- // don't bother notifying that data model of position-only changes. |
- if (!ShouldMaintainPosition( |
- syncable::GetModelTypeFromSpecifics(b.ref(SPECIFICS)))) |
- return false; |
- if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID)) |
- return true; |
- if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) |
- return true; |
- return false; |
- } |
- |
- // Determine if any of the fields made visible to clients of the Sync API |
- // differ between the versions of an entry stored in |a| and |b|. A return |
- // value of false means that it should be OK to ignore this change. |
- static bool VisiblePropertiesDiffer( |
- const syncable::EntryKernelMutation& mutation, |
- Cryptographer* cryptographer) { |
- const syncable::EntryKernel& a = mutation.original; |
- const syncable::EntryKernel& b = mutation.mutated; |
- const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); |
- const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); |
- DCHECK_EQ(syncable::GetModelTypeFromSpecifics(a_specifics), |
- syncable::GetModelTypeFromSpecifics(b_specifics)); |
- syncable::ModelType model_type = |
- syncable::GetModelTypeFromSpecifics(b_specifics); |
- // Suppress updates to items that aren't tracked by any browser model. |
- if (model_type < syncable::FIRST_REAL_MODEL_TYPE || |
- !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { |
- return false; |
- } |
- if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) |
- return true; |
- if (!AreSpecificsEqual(cryptographer, |
- a.ref(syncable::SPECIFICS), |
- b.ref(syncable::SPECIFICS))) { |
- return true; |
- } |
- // We only care if the name has changed if neither specifics is encrypted |
- // (encrypted nodes blow away the NON_UNIQUE_NAME). |
- if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && |
- a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) |
- return true; |
- if (VisiblePositionsDiffer(mutation)) |
- return true; |
- return false; |
- } |
- |
- bool ChangeBuffersAreEmpty() { |
- for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { |
- if (!change_buffers_[i].IsEmpty()) |
- return false; |
- } |
- return true; |
- } |
- |
- void ReEncryptEverything(WriteTransaction* trans); |
- |
- // Called for every notification. This updates the notification statistics |
- // to be displayed in about:sync. |
- void UpdateNotificationInfo( |
- const syncable::ModelTypePayloadMap& type_payloads); |
- |
- // Checks for server reachabilty and requests a nudge. |
- void OnIPAddressChangedImpl(); |
- |
- // Helper function used only by the constructor. |
- void BindJsMessageHandler( |
- const std::string& name, UnboundJsMessageHandler unbound_message_handler); |
- |
- // Returned pointer is owned by the caller. |
- static DictionaryValue* NotificationInfoToValue( |
- const NotificationInfoMap& notification_info); |
- |
- // JS message handlers. |
- JsArgList GetNotificationState(const JsArgList& args); |
- JsArgList GetNotificationInfo(const JsArgList& args); |
- JsArgList GetRootNodeDetails(const JsArgList& args); |
- JsArgList GetAllNodes(const JsArgList& args); |
- JsArgList GetNodeSummariesById(const JsArgList& args); |
- JsArgList GetNodeDetailsById(const JsArgList& args); |
- JsArgList GetChildNodeIds(const JsArgList& args); |
- JsArgList GetClientServerTraffic(const JsArgList& args); |
- |
- FilePath database_path_; |
- |
- const std::string name_; |
- |
- base::ThreadChecker thread_checker_; |
- |
- base::WeakPtrFactory<SyncInternal> weak_ptr_factory_; |
- |
- // Thread-safe handle used by |
- // HandleCalculateChangesChangeEventFromSyncApi(), which can be |
- // called from any thread. Valid only between between calls to |
- // Init() and Shutdown(). |
- // |
- // TODO(akalin): Ideally, we wouldn't need to store this; instead, |
- // we'd have another worker class which implements |
- // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a |
- // WeakHandle when we construct it. |
- WeakHandle<SyncInternal> weak_handle_this_; |
- |
- // |blocking_task_runner| is a TaskRunner to be used for tasks that |
- // may block on disk I/O. |
- scoped_refptr<base::TaskRunner> blocking_task_runner_; |
- |
- // We give a handle to share_ to clients of the API for use when constructing |
- // any transaction type. |
- UserShare share_; |
- |
- // This can be called from any thread, but only between calls to |
- // OpenDirectory() and ShutdownOnSyncThread(). |
- browser_sync::WeakHandle<SyncManager::ChangeObserver> change_observer_; |
- |
- ObserverList<SyncManager::Observer> observers_; |
- |
- // The ServerConnectionManager used to abstract communication between the |
- // client (the Syncer) and the sync server. |
- scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; |
- |
- // The scheduler that runs the Syncer. Needs to be explicitly |
- // Start()ed. |
- scoped_ptr<SyncScheduler> scheduler_; |
- |
- bool enable_sync_tabs_for_other_clients_; |
- |
- // The SyncNotifier which notifies us when updates need to be downloaded. |
- scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_; |
- |
- // A multi-purpose status watch object that aggregates stats from various |
- // sync components. |
- AllStatus allstatus_; |
- |
- // Each element of this array is a store of change records produced by |
- // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are |
- // segregated by model type, and are stored here to be processed and |
- // forwarded to the observer slightly later, at the TRANSACTION_ENDING |
- // step by HandleTransactionEndingChangeEvent. The list is cleared in the |
- // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. |
- ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT]; |
- |
- // The entity that provides us with information about which types to sync. |
- // The instance is shared between the SyncManager and the Syncer. |
- ModelSafeWorkerRegistrar* registrar_; |
- |
- SyncManager::ChangeDelegate* change_delegate_; |
- |
- // Set to true once Init has been called. |
- bool initialized_; |
- |
- // Controls the disabling of certain SyncManager features. |
- // Can be used to disable communication with the server and the use of an |
- // on-disk file for maintaining syncer state. |
- // TODO(117836): Clean up implementation of SyncManager unit tests. |
- TestingMode testing_mode_; |
- |
- bool observing_ip_address_changes_; |
- |
- // Map used to store the notification info to be displayed in |
- // about:sync page. |
- NotificationInfoMap notification_info_map_; |
- |
- // These are for interacting with chrome://sync-internals. |
- JsMessageHandlerMap js_message_handlers_; |
- WeakHandle<JsEventHandler> js_event_handler_; |
- JsSyncManagerObserver js_sync_manager_observer_; |
- JsMutationEventObserver js_mutation_event_observer_; |
- |
- // This is for keeping track of client events to send to the server. |
- DebugInfoEventListener debug_info_event_listener_; |
- |
- browser_sync::TrafficRecorder traffic_recorder_; |
- |
- Encryptor* encryptor_; |
- UnrecoverableErrorHandler* unrecoverable_error_handler_; |
- ReportUnrecoverableErrorFunction report_unrecoverable_error_function_; |
- |
- MessageLoop* const created_on_loop_; |
- |
- // The number of times we've automatically (i.e. not via SetPassphrase or |
- // conflict resolver) updated the nigori's encryption keys in this chrome |
- // instantiation. |
- int nigori_overwrite_count_; |
-}; |
- |
-// A class to calculate nudge delays for types. |
-class NudgeStrategy { |
- public: |
- static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type, |
- SyncManager::SyncInternal* core) { |
- NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type); |
- return GetNudgeDelayTimeDeltaFromType(delay_type, |
- model_type, |
- core); |
- } |
- |
- private: |
- // Possible types of nudge delay for datatypes. |
- // Note: These are just hints. If a sync happens then all dirty entries |
- // would be committed as part of the sync. |
- enum NudgeDelayStrategy { |
- // Sync right away. |
- IMMEDIATE, |
- |
- // Sync this change while syncing another change. |
- ACCOMPANY_ONLY, |
- |
- // The datatype does not use one of the predefined wait times but defines |
- // its own wait time logic for nudge. |
- CUSTOM, |
- }; |
- |
- static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) { |
- switch (type) { |
- case syncable::AUTOFILL: |
- return ACCOMPANY_ONLY; |
- case syncable::PREFERENCES: |
- case syncable::SESSIONS: |
- return CUSTOM; |
- default: |
- return IMMEDIATE; |
- } |
- } |
- |
- static TimeDelta GetNudgeDelayTimeDeltaFromType( |
- const NudgeDelayStrategy& delay_type, const ModelType& model_type, |
- const SyncManager::SyncInternal* core) { |
- CHECK(core); |
- TimeDelta delay = TimeDelta::FromMilliseconds( |
- SyncManager::kDefaultNudgeDelayMilliseconds); |
- switch (delay_type) { |
- case IMMEDIATE: |
- delay = TimeDelta::FromMilliseconds( |
- SyncManager::kDefaultNudgeDelayMilliseconds); |
- break; |
- case ACCOMPANY_ONLY: |
- delay = TimeDelta::FromSeconds( |
- browser_sync::kDefaultShortPollIntervalSeconds); |
- break; |
- case CUSTOM: |
- switch (model_type) { |
- case syncable::PREFERENCES: |
- delay = TimeDelta::FromMilliseconds( |
- SyncManager::kPreferencesNudgeDelayMilliseconds); |
- break; |
- case syncable::SESSIONS: |
- delay = core->scheduler()->sessions_commit_delay(); |
- break; |
- default: |
- NOTREACHED(); |
- } |
- break; |
- default: |
- NOTREACHED(); |
- } |
- return delay; |
- } |
-}; |
- |
-SyncManager::ChangeDelegate::~ChangeDelegate() {} |
- |
-SyncManager::ChangeObserver::~ChangeObserver() {} |
- |
-SyncManager::Observer::~Observer() {} |
- |
-SyncManager::SyncManager(const std::string& name) |
- : data_(new SyncInternal(name)) {} |
- |
-SyncManager::Status::Status() |
- : notifications_enabled(false), |
- notifications_received(0), |
- unsynced_count(0), |
- encryption_conflicts(0), |
- hierarchy_conflicts(0), |
- simple_conflicts(0), |
- server_conflicts(0), |
- committed_count(0), |
- syncing(false), |
- initial_sync_ended(false), |
- updates_available(0), |
- updates_received(0), |
- reflected_updates_received(0), |
- tombstone_updates_received(0), |
- num_local_overwrites_total(0), |
- num_server_overwrites_total(0), |
- nonempty_get_updates(0), |
- empty_get_updates(0), |
- sync_cycles_with_commits(0), |
- sync_cycles_without_commits(0), |
- useless_sync_cycles(0), |
- useful_sync_cycles(0), |
- cryptographer_ready(false), |
- crypto_has_pending_keys(false) { |
-} |
- |
-SyncManager::Status::~Status() { |
-} |
- |
-bool SyncManager::Init( |
- const FilePath& database_location, |
- const WeakHandle<JsEventHandler>& event_handler, |
- const std::string& sync_server_and_path, |
- int sync_server_port, |
- bool use_ssl, |
- const scoped_refptr<base::TaskRunner>& blocking_task_runner, |
- HttpPostProviderFactory* post_factory, |
- ModelSafeWorkerRegistrar* registrar, |
- browser_sync::ExtensionsActivityMonitor* extensions_activity_monitor, |
- ChangeDelegate* change_delegate, |
- const std::string& user_agent, |
- const SyncCredentials& credentials, |
- bool enable_sync_tabs_for_other_clients, |
- sync_notifier::SyncNotifier* sync_notifier, |
- const std::string& restored_key_for_bootstrapping, |
- TestingMode testing_mode, |
- Encryptor* encryptor, |
- UnrecoverableErrorHandler* unrecoverable_error_handler, |
- ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- DCHECK(post_factory); |
- DVLOG(1) << "SyncManager starting Init..."; |
- std::string server_string(sync_server_and_path); |
- return data_->Init(database_location, |
- event_handler, |
- server_string, |
- sync_server_port, |
- use_ssl, |
- blocking_task_runner, |
- post_factory, |
- registrar, |
- extensions_activity_monitor, |
- change_delegate, |
- user_agent, |
- credentials, |
- enable_sync_tabs_for_other_clients, |
- sync_notifier, |
- restored_key_for_bootstrapping, |
- testing_mode, |
- encryptor, |
- unrecoverable_error_handler, |
- report_unrecoverable_error_function); |
-} |
- |
-void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->UpdateCredentials(credentials); |
-} |
- |
-void SyncManager::UpdateEnabledTypes() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->UpdateEnabledTypes(); |
-} |
- |
-void SyncManager::MaybeSetSyncTabsInNigoriNode( |
- ModelTypeSet enabled_types) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->MaybeSetSyncTabsInNigoriNode(enabled_types); |
-} |
- |
-void SyncManager::ThrowUnrecoverableError() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- trans.GetWrappedTrans()->OnUnrecoverableError( |
- FROM_HERE, "Simulating unrecoverable error for testing purposes."); |
-} |
- |
-bool SyncManager::InitialSyncEndedForAllEnabledTypes() { |
- return data_->InitialSyncEndedForAllEnabledTypes(); |
-} |
- |
-void SyncManager::StartSyncingNormally() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->StartSyncingNormally(); |
-} |
- |
-void SyncManager::SetEncryptionPassphrase(const std::string& passphrase, |
- bool is_explicit) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->SetEncryptionPassphrase(passphrase, is_explicit); |
-} |
- |
-void SyncManager::SetDecryptionPassphrase(const std::string& passphrase) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->SetDecryptionPassphrase(passphrase); |
-} |
- |
-void SyncManager::EnableEncryptEverything() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- { |
- // Update the cryptographer to know we're now encrypting everything. |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- // Only set encrypt everything if we know we can encrypt. This allows the |
- // user to cancel encryption if they have forgotten their passphrase. |
- if (cryptographer->is_ready()) |
- cryptographer->set_encrypt_everything(); |
- } |
- |
- // Reads from cryptographer so will automatically encrypt all |
- // datatypes and update the nigori node as necessary. Will trigger |
- // OnPassphraseRequired if necessary. |
- data_->RefreshEncryption(); |
-} |
- |
-bool SyncManager::EncryptEverythingEnabledForTest() const { |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- return trans.GetCryptographer()->encrypt_everything(); |
-} |
- |
-bool SyncManager::IsUsingExplicitPassphrase() { |
- return data_ && data_->IsUsingExplicitPassphrase(); |
-} |
- |
-void SyncManager::RequestCleanupDisabledTypes() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (data_->scheduler()) |
- data_->scheduler()->ScheduleCleanupDisabledTypes(); |
-} |
- |
-void SyncManager::RequestClearServerData() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (data_->scheduler()) |
- data_->scheduler()->ScheduleClearUserData(); |
-} |
- |
-void SyncManager::RequestConfig( |
- ModelTypeSet types, ConfigureReason reason) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (!data_->scheduler()) { |
- LOG(INFO) |
- << "SyncManager::RequestConfig: bailing out because scheduler is " |
- << "null"; |
- return; |
- } |
- StartConfigurationMode(base::Closure()); |
- data_->scheduler()->ScheduleConfig(types, GetSourceFromReason(reason)); |
-} |
- |
-void SyncManager::StartConfigurationMode(const base::Closure& callback) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (!data_->scheduler()) { |
- LOG(INFO) |
- << "SyncManager::StartConfigurationMode: could not start " |
- << "configuration mode because because scheduler is null"; |
- return; |
- } |
- data_->scheduler()->Start( |
- browser_sync::SyncScheduler::CONFIGURATION_MODE, callback); |
-} |
- |
-bool SyncManager::SyncInternal::Init( |
- const FilePath& database_location, |
- const WeakHandle<JsEventHandler>& event_handler, |
- const std::string& sync_server_and_path, |
- int port, |
- bool use_ssl, |
- const scoped_refptr<base::TaskRunner>& blocking_task_runner, |
- HttpPostProviderFactory* post_factory, |
- ModelSafeWorkerRegistrar* model_safe_worker_registrar, |
- browser_sync::ExtensionsActivityMonitor* extensions_activity_monitor, |
- ChangeDelegate* change_delegate, |
- const std::string& user_agent, |
- const SyncCredentials& credentials, |
- bool enable_sync_tabs_for_other_clients, |
- sync_notifier::SyncNotifier* sync_notifier, |
- const std::string& restored_key_for_bootstrapping, |
- TestingMode testing_mode, |
- Encryptor* encryptor, |
- UnrecoverableErrorHandler* unrecoverable_error_handler, |
- ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { |
- CHECK(!initialized_); |
- |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- DVLOG(1) << "Starting SyncInternal initialization."; |
- |
- weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); |
- |
- blocking_task_runner_ = blocking_task_runner; |
- |
- registrar_ = model_safe_worker_registrar; |
- change_delegate_ = change_delegate; |
- testing_mode_ = testing_mode; |
- |
- enable_sync_tabs_for_other_clients_ = enable_sync_tabs_for_other_clients; |
- |
- sync_notifier_.reset(sync_notifier); |
- |
- AddObserver(&js_sync_manager_observer_); |
- SetJsEventHandler(event_handler); |
- |
- AddObserver(&debug_info_event_listener_); |
- |
- database_path_ = database_location.Append( |
- syncable::Directory::kSyncDatabaseFilename); |
- encryptor_ = encryptor; |
- unrecoverable_error_handler_ = unrecoverable_error_handler; |
- report_unrecoverable_error_function_ = report_unrecoverable_error_function; |
- share_.directory.reset( |
- new syncable::Directory(encryptor_, |
- unrecoverable_error_handler_, |
- report_unrecoverable_error_function_)); |
- |
- connection_manager_.reset(new SyncAPIServerConnectionManager( |
- sync_server_and_path, port, use_ssl, user_agent, post_factory)); |
- |
- net::NetworkChangeNotifier::AddIPAddressObserver(this); |
- observing_ip_address_changes_ = true; |
- |
- connection_manager()->AddListener(this); |
- |
- |
- // Test mode does not use a syncer context or syncer thread. |
- if (testing_mode_ == NON_TEST) { |
- // Build a SyncSessionContext and store the worker in it. |
- DVLOG(1) << "Sync is bringing up SyncSessionContext."; |
- std::vector<SyncEngineEventListener*> listeners; |
- listeners.push_back(&allstatus_); |
- listeners.push_back(this); |
- SyncSessionContext* context = new SyncSessionContext( |
- connection_manager_.get(), |
- directory(), |
- model_safe_worker_registrar, |
- extensions_activity_monitor, |
- listeners, |
- &debug_info_event_listener_, |
- &traffic_recorder_); |
- context->set_account_name(credentials.email); |
- // The SyncScheduler takes ownership of |context|. |
- scheduler_.reset(new SyncScheduler(name_, context, new Syncer())); |
- } |
- |
- bool signed_in = SignIn(credentials); |
- |
- if (signed_in) { |
- if (scheduler()) { |
- scheduler()->Start( |
- browser_sync::SyncScheduler::CONFIGURATION_MODE, base::Closure()); |
- } |
- |
- initialized_ = true; |
- |
- // Cryptographer should only be accessed while holding a |
- // transaction. Grabbing the user share for the transaction |
- // checks the initialization state, so this must come after |
- // |initialized_| is set to true. |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- trans.GetCryptographer()->Bootstrap(restored_key_for_bootstrapping); |
- trans.GetCryptographer()->AddObserver(this); |
- } |
- |
- // Notify that initialization is complete. Note: This should be the last to |
- // execute if |signed_in| is false. Reason being in that case we would |
- // post a task to shutdown sync. But if this function posts any other tasks |
- // on the UI thread and if shutdown wins then that tasks would execute on |
- // a freed pointer. This is because UI thread is not shut down. |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnInitializationComplete( |
- MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), |
- signed_in)); |
- |
- if (!signed_in && testing_mode_ == NON_TEST) |
- return false; |
- |
- sync_notifier_->AddObserver(this); |
- |
- return signed_in; |
-} |
- |
-void SyncManager::SyncInternal::UpdateCryptographerAndNigori( |
- const std::string& chrome_version, |
- const base::Closure& done_callback) { |
- DCHECK(initialized_); |
- browser_sync::GetSessionName( |
- blocking_task_runner_, |
- base::Bind( |
- &SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback, |
- weak_ptr_factory_.GetWeakPtr(), |
- chrome_version, |
- done_callback)); |
-} |
- |
-void SyncManager::SyncInternal::UpdateNigoriEncryptionState( |
- Cryptographer* cryptographer, |
- WriteNode* nigori_node) { |
- DCHECK(nigori_node); |
- sync_pb::NigoriSpecifics nigori = nigori_node->GetNigoriSpecifics(); |
- |
- if (cryptographer->is_ready() && |
- nigori_overwrite_count_ < kNigoriOverwriteLimit) { |
- // Does not modify the encrypted blob if the unencrypted data already |
- // matches what is about to be written. |
- sync_pb::EncryptedData original_keys = nigori.encrypted(); |
- if (!cryptographer->GetKeys(nigori.mutable_encrypted())) |
- NOTREACHED(); |
- |
- if (nigori.encrypted().SerializeAsString() != |
- original_keys.SerializeAsString()) { |
- // We've updated the nigori node's encryption keys. In order to prevent |
- // a possible looping of two clients constantly overwriting each other, |
- // we limit the absolute number of overwrites per client instantiation. |
- nigori_overwrite_count_++; |
- UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites", |
- nigori_overwrite_count_); |
- } |
- |
- // Note: we don't try to set using_explicit_passphrase here since if that |
- // is lost the user can always set it again. The main point is to preserve |
- // the encryption keys so all data remains decryptable. |
- } |
- cryptographer->UpdateNigoriFromEncryptedTypes(&nigori); |
- |
- // If nothing has changed, this is a no-op. |
- nigori_node->SetNigoriSpecifics(nigori); |
-} |
- |
-void SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback( |
- const std::string& chrome_version, |
- const base::Closure& done_callback, |
- const std::string& session_name) { |
- if (!directory()->initial_sync_ended_for_type(syncable::NIGORI)) { |
- done_callback.Run(); // Should only happen during first time sync. |
- return; |
- } |
- |
- bool success = false; |
- { |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- WriteNode node(&trans); |
- |
- if (node.InitByTagLookup(kNigoriTag) == sync_api::BaseNode::INIT_OK) { |
- sync_pb::NigoriSpecifics nigori(node.GetNigoriSpecifics()); |
- Cryptographer::UpdateResult result = cryptographer->Update(nigori); |
- if (result == Cryptographer::NEEDS_PASSPHRASE) { |
- sync_pb::EncryptedData pending_keys; |
- if (cryptographer->has_pending_keys()) |
- pending_keys = cryptographer->GetPendingKeys(); |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_DECRYPTION, |
- pending_keys)); |
- } |
- |
- |
- // Add or update device information. |
- bool contains_this_device = false; |
- for (int i = 0; i < nigori.device_information_size(); ++i) { |
- const sync_pb::DeviceInformation& device_information = |
- nigori.device_information(i); |
- if (device_information.cache_guid() == directory()->cache_guid()) { |
- // Update the version number in case it changed due to an update. |
- if (device_information.chrome_version() != chrome_version) { |
- sync_pb::DeviceInformation* mutable_device_information = |
- nigori.mutable_device_information(i); |
- mutable_device_information->set_chrome_version( |
- chrome_version); |
- } |
- contains_this_device = true; |
- } |
- } |
- |
- if (!contains_this_device) { |
- sync_pb::DeviceInformation* device_information = |
- nigori.add_device_information(); |
- device_information->set_cache_guid(directory()->cache_guid()); |
-#if defined(OS_CHROMEOS) |
- device_information->set_platform("ChromeOS"); |
-#elif defined(OS_LINUX) |
- device_information->set_platform("Linux"); |
-#elif defined(OS_MACOSX) |
- device_information->set_platform("Mac"); |
-#elif defined(OS_WIN) |
- device_information->set_platform("Windows"); |
-#endif |
- device_information->set_name(session_name); |
- device_information->set_chrome_version(chrome_version); |
- } |
- // Disabled to avoid nigori races. TODO(zea): re-enable. crbug.com/122837 |
- // node.SetNigoriSpecifics(nigori); |
- |
- // Make sure the nigori node has the up to date encryption info. |
- UpdateNigoriEncryptionState(cryptographer, &node); |
- |
- NotifyCryptographerState(cryptographer); |
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); |
- |
- success = cryptographer->is_ready(); |
- } else { |
- NOTREACHED(); |
- } |
- } |
- |
- if (success) |
- RefreshEncryption(); |
- done_callback.Run(); |
-} |
- |
-void SyncManager::SyncInternal::NotifyCryptographerState( |
- Cryptographer * cryptographer) { |
- // TODO(lipalani): Explore the possibility of hooking this up to |
- // SyncManager::Observer and making |AllStatus| a listener for that. |
- allstatus_.SetCryptographerReady(cryptographer->is_ready()); |
- allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); |
- debug_info_event_listener_.SetCryptographerReady(cryptographer->is_ready()); |
- debug_info_event_listener_.SetCrytographerHasPendingKeys( |
- cryptographer->has_pending_keys()); |
-} |
- |
-void SyncManager::SyncInternal::StartSyncingNormally() { |
- // Start the sync scheduler. |
- if (scheduler()) // NULL during certain unittests. |
- scheduler()->Start(SyncScheduler::NORMAL_MODE, base::Closure()); |
-} |
- |
-bool SyncManager::SyncInternal::OpenDirectory() { |
- DCHECK(!initialized_) << "Should only happen once"; |
- |
- // Set before Open(). |
- change_observer_ = |
- browser_sync::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()); |
- WeakHandle<syncable::TransactionObserver> transaction_observer( |
- browser_sync::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr())); |
- |
- syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED; |
- if (testing_mode_ == TEST_IN_MEMORY) { |
- open_result = directory()->OpenInMemoryForTest( |
- username_for_share(), this, transaction_observer); |
- } else { |
- open_result = directory()->Open( |
- database_path_, username_for_share(), this, transaction_observer); |
- } |
- if (open_result != syncable::OPENED) { |
- LOG(ERROR) << "Could not open share for:" << username_for_share(); |
- return false; |
- } |
- |
- connection_manager()->set_client_id(directory()->cache_guid()); |
- return true; |
-} |
- |
-bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- DCHECK(share_.name.empty()); |
- share_.name = credentials.email; |
- |
- DVLOG(1) << "Signing in user: " << username_for_share(); |
- if (!OpenDirectory()) |
- return false; |
- |
- // Retrieve and set the sync notifier state. This should be done |
- // only after OpenDirectory is called. |
- std::string unique_id = directory()->cache_guid(); |
- std::string state = directory()->GetNotificationState(); |
- DVLOG(1) << "Read notification unique ID: " << unique_id; |
- if (VLOG_IS_ON(1)) { |
- std::string encoded_state; |
- base::Base64Encode(state, &encoded_state); |
- DVLOG(1) << "Read notification state: " << encoded_state; |
- } |
- allstatus_.SetUniqueId(unique_id); |
- sync_notifier_->SetUniqueId(unique_id); |
- sync_notifier_->SetState(state); |
- |
- UpdateCredentials(credentials); |
- UpdateEnabledTypes(); |
- return true; |
-} |
- |
-void SyncManager::SyncInternal::UpdateCredentials( |
- const SyncCredentials& credentials) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- DCHECK_EQ(credentials.email, share_.name); |
- DCHECK(!credentials.email.empty()); |
- DCHECK(!credentials.sync_token.empty()); |
- |
- observing_ip_address_changes_ = true; |
- if (connection_manager()->set_auth_token(credentials.sync_token)) { |
- sync_notifier_->UpdateCredentials( |
- credentials.email, credentials.sync_token); |
- if (testing_mode_ == NON_TEST && initialized_) { |
- if (scheduler()) |
- scheduler()->OnCredentialsUpdated(); |
- } |
- } |
-} |
- |
-void SyncManager::SyncInternal::UpdateEnabledTypes() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- ModelSafeRoutingInfo routes; |
- registrar_->GetModelSafeRoutingInfo(&routes); |
- const ModelTypeSet enabled_types = GetRoutingInfoTypes(routes); |
- sync_notifier_->UpdateEnabledTypes(enabled_types); |
- if (enable_sync_tabs_for_other_clients_) |
- MaybeSetSyncTabsInNigoriNode(enabled_types); |
-} |
- |
-void SyncManager::SyncInternal::MaybeSetSyncTabsInNigoriNode( |
- const ModelTypeSet enabled_types) { |
- // The initialized_ check is to ensure that we don't CHECK in GetUserShare |
- // when this is called on start-up. It's ok to ignore that case, since |
- // presumably this would've run when the user originally enabled sessions. |
- if (initialized_ && enabled_types.Has(syncable::SESSIONS)) { |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- WriteNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- LOG(WARNING) << "Unable to set 'sync_tabs' bit because Nigori node not " |
- << "found."; |
- return; |
- } |
- |
- sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics()); |
- specifics.set_sync_tabs(true); |
- node.SetNigoriSpecifics(specifics); |
- } |
-} |
- |
-void SyncManager::SyncInternal::SetEncryptionPassphrase( |
- const std::string& passphrase, |
- bool is_explicit) { |
- // We do not accept empty passphrases. |
- if (passphrase.empty()) { |
- NOTREACHED() << "Cannot encrypt with an empty passphrase."; |
- return; |
- } |
- |
- // All accesses to the cryptographer are protected by a transaction. |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- KeyParams key_params = {"localhost", "dummy", passphrase}; |
- WriteNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. |
- NOTREACHED(); |
- return; |
- } |
- |
- bool nigori_has_explicit_passphrase = |
- node.GetNigoriSpecifics().using_explicit_passphrase(); |
- std::string bootstrap_token; |
- sync_pb::EncryptedData pending_keys; |
- if (cryptographer->has_pending_keys()) |
- pending_keys = cryptographer->GetPendingKeys(); |
- bool success = false; |
- |
- |
- // There are six cases to handle here: |
- // 1. The user has no pending keys and is setting their current GAIA password |
- // as the encryption passphrase. This happens either during first time sync |
- // with a clean profile, or after re-authenticating on a profile that was |
- // already signed in with the cryptographer ready. |
- // 2. The user has no pending keys, and is overwriting an (already provided) |
- // implicit passphrase with an explicit (custom) passphrase. |
- // 3. The user has pending keys for an explicit passphrase that is somehow set |
- // to their current GAIA passphrase. |
- // 4. The user has pending keys encrypted with their current GAIA passphrase |
- // and the caller passes in the current GAIA passphrase. |
- // 5. The user has pending keys encrypted with an older GAIA passphrase |
- // and the caller passes in the current GAIA passphrase. |
- // 6. The user has previously done encryption with an explicit passphrase. |
- // Furthermore, we enforce the fact that the bootstrap encryption token will |
- // always be derived from the newest GAIA password if the account is using |
- // an implicit passphrase (even if the data is encrypted with an old GAIA |
- // password). If the account is using an explicit (custom) passphrase, the |
- // bootstrap token will be derived from the most recently provided explicit |
- // passphrase (that was able to decrypt the data). |
- if (!nigori_has_explicit_passphrase) { |
- if (!cryptographer->has_pending_keys()) { |
- if (cryptographer->AddKey(key_params)) { |
- // Case 1 and 2. We set a new GAIA passphrase when there are no pending |
- // keys (1), or overwriting an implicit passphrase with a new explicit |
- // one (2) when there are no pending keys. |
- DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" ) |
- << " passphrase for encryption."; |
- cryptographer->GetBootstrapToken(&bootstrap_token); |
- success = true; |
- } else { |
- NOTREACHED() << "Failed to add key to cryptographer."; |
- success = false; |
- } |
- } else { // cryptographer->has_pending_keys() == true |
- if (is_explicit) { |
- // This can only happen if the nigori node is updated with a new |
- // implicit passphrase while a client is attempting to set a new custom |
- // passphrase (race condition). |
- DVLOG(1) << "Failing because an implicit passphrase is already set."; |
- success = false; |
- } else { // is_explicit == false |
- if (cryptographer->DecryptPendingKeys(key_params)) { |
- // Case 4. We successfully decrypted with the implicit GAIA passphrase |
- // passed in. |
- DVLOG(1) << "Implicit internal passphrase accepted for decryption."; |
- cryptographer->GetBootstrapToken(&bootstrap_token); |
- success = true; |
- } else { |
- // Case 5. Encryption was done with an old GAIA password, but we were |
- // provided with the current GAIA password. We need to generate a new |
- // bootstrap token to preserve it. We build a temporary cryptographer |
- // to allow us to extract these params without polluting our current |
- // cryptographer. |
- DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding " |
- << "anyways as default passphrase and persisting via " |
- << "bootstrap token."; |
- Cryptographer temp_cryptographer(encryptor_); |
- temp_cryptographer.AddKey(key_params); |
- temp_cryptographer.GetBootstrapToken(&bootstrap_token); |
- // We then set the new passphrase as the default passphrase of the |
- // real cryptographer, even though we have pending keys. This is safe, |
- // as although Cryptographer::is_initialized() will now be true, |
- // is_ready() will remain false due to having pending keys. |
- cryptographer->AddKey(key_params); |
- success = false; |
- } |
- } // is_explicit |
- } // cryptographer->has_pending_keys() |
- } else { // nigori_has_explicit_passphrase == true |
- // Case 6. We do not want to override a previously set explicit passphrase, |
- // so we return a failure. |
- DVLOG(1) << "Failing because an explicit passphrase is already set."; |
- success = false; |
- } |
- |
- DVLOG_IF(1, !success) |
- << "Failure in SetEncryptionPassphrase; notifying and returning."; |
- DVLOG_IF(1, success) |
- << "Successfully set encryption passphrase; updating nigori and " |
- "reencrypting."; |
- |
- FinishSetPassphrase( |
- success, bootstrap_token, is_explicit, &trans, &node); |
-} |
- |
-void SyncManager::SyncInternal::SetDecryptionPassphrase( |
- const std::string& passphrase) { |
- // We do not accept empty passphrases. |
- if (passphrase.empty()) { |
- NOTREACHED() << "Cannot decrypt with an empty passphrase."; |
- return; |
- } |
- |
- // All accesses to the cryptographer are protected by a transaction. |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- KeyParams key_params = {"localhost", "dummy", passphrase}; |
- WriteNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. |
- NOTREACHED(); |
- return; |
- } |
- |
- if (!cryptographer->has_pending_keys()) { |
- // Note that this *can* happen in a rare situation where data is |
- // re-encrypted on another client while a SetDecryptionPassphrase() call is |
- // in-flight on this client. It is rare enough that we choose to do nothing. |
- NOTREACHED() << "Attempt to set decryption passphrase failed because there " |
- << "were no pending keys."; |
- return; |
- } |
- |
- bool nigori_has_explicit_passphrase = |
- node.GetNigoriSpecifics().using_explicit_passphrase(); |
- std::string bootstrap_token; |
- sync_pb::EncryptedData pending_keys; |
- pending_keys = cryptographer->GetPendingKeys(); |
- bool success = false; |
- |
- // There are three cases to handle here: |
- // 7. We're using the current GAIA password to decrypt the pending keys. This |
- // happens when signing in to an account with a previously set implicit |
- // passphrase, where the data is already encrypted with the newest GAIA |
- // password. |
- // 8. The user is providing an old GAIA password to decrypt the pending keys. |
- // In this case, the user is using an implicit passphrase, but has changed |
- // their password since they last encrypted their data, and therefore |
- // their current GAIA password was unable to decrypt the data. This will |
- // happen when the user is setting up a new profile with a previously |
- // encrypted account (after changing passwords). |
- // 9. The user is providing a previously set explicit passphrase to decrypt |
- // the pending keys. |
- if (!nigori_has_explicit_passphrase) { |
- if (cryptographer->is_initialized()) { |
- // We only want to change the default encryption key to the pending |
- // one if the pending keybag already contains the current default. |
- // This covers the case where a different client re-encrypted |
- // everything with a newer gaia passphrase (and hence the keybag |
- // contains keys from all previously used gaia passphrases). |
- // Otherwise, we're in a situation where the pending keys are |
- // encrypted with an old gaia passphrase, while the default is the |
- // current gaia passphrase. In that case, we preserve the default. |
- Cryptographer temp_cryptographer(encryptor_); |
- temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys()); |
- if (temp_cryptographer.DecryptPendingKeys(key_params)) { |
- // Check to see if the pending bag of keys contains the current |
- // default key. |
- sync_pb::EncryptedData encrypted; |
- cryptographer->GetKeys(&encrypted); |
- if (temp_cryptographer.CanDecrypt(encrypted)) { |
- DVLOG(1) << "Implicit user provided passphrase accepted for " |
- << "decryption, overwriting default."; |
- // Case 7. The pending keybag contains the current default. Go ahead |
- // and update the cryptographer, letting the default change. |
- cryptographer->DecryptPendingKeys(key_params); |
- cryptographer->GetBootstrapToken(&bootstrap_token); |
- success = true; |
- } else { |
- // Case 8. The pending keybag does not contain the current default |
- // encryption key. We decrypt the pending keys here, and in |
- // FinishSetPassphrase, re-encrypt everything with the current GAIA |
- // passphrase instead of the passphrase just provided by the user. |
- DVLOG(1) << "Implicit user provided passphrase accepted for " |
- << "decryption, restoring implicit internal passphrase " |
- << "as default."; |
- std::string bootstrap_token_from_current_key; |
- cryptographer->GetBootstrapToken( |
- &bootstrap_token_from_current_key); |
- cryptographer->DecryptPendingKeys(key_params); |
- // Overwrite the default from the pending keys. |
- cryptographer->AddKeyFromBootstrapToken( |
- bootstrap_token_from_current_key); |
- success = true; |
- } |
- } else { // !temp_cryptographer.DecryptPendingKeys(..) |
- DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; |
- success = false; |
- } // temp_cryptographer.DecryptPendingKeys(...) |
- } else { // cryptographer->is_initialized() == false |
- if (cryptographer->DecryptPendingKeys(key_params)) { |
- // This can happpen in two cases: |
- // - First time sync on android, where we'll never have a |
- // !user_provided passphrase. |
- // - This is a restart for a client that lost their bootstrap token. |
- // In both cases, we should go ahead and initialize the cryptographer |
- // and persist the new bootstrap token. |
- // |
- // Note: at this point, we cannot distinguish between cases 7 and 8 |
- // above. This user provided passphrase could be the current or the |
- // old. But, as long as we persist the token, there's nothing more |
- // we can do. |
- cryptographer->GetBootstrapToken(&bootstrap_token); |
- DVLOG(1) << "Implicit user provided passphrase accepted, initializing" |
- << " cryptographer."; |
- success = true; |
- } else { |
- DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; |
- success = false; |
- } |
- } // cryptographer->is_initialized() |
- } else { // nigori_has_explicit_passphrase == true |
- // Case 9. Encryption was done with an explicit passphrase, and we decrypt |
- // with the passphrase provided by the user. |
- if (cryptographer->DecryptPendingKeys(key_params)) { |
- DVLOG(1) << "Explicit passphrase accepted for decryption."; |
- cryptographer->GetBootstrapToken(&bootstrap_token); |
- success = true; |
- } else { |
- DVLOG(1) << "Explicit passphrase failed to decrypt."; |
- success = false; |
- } |
- } // nigori_has_explicit_passphrase |
- |
- DVLOG_IF(1, !success) |
- << "Failure in SetDecryptionPassphrase; notifying and returning."; |
- DVLOG_IF(1, success) |
- << "Successfully set decryption passphrase; updating nigori and " |
- "reencrypting."; |
- |
- FinishSetPassphrase(success, |
- bootstrap_token, |
- nigori_has_explicit_passphrase, |
- &trans, |
- &node); |
-} |
- |
-void SyncManager::SyncInternal::FinishSetPassphrase( |
- bool success, |
- const std::string& bootstrap_token, |
- bool is_explicit, |
- WriteTransaction* trans, |
- WriteNode* nigori_node) { |
- Cryptographer* cryptographer = trans->GetCryptographer(); |
- NotifyCryptographerState(cryptographer); |
- |
- // It's possible we need to change the bootstrap token even if we failed to |
- // set the passphrase (for example if we need to preserve the new GAIA |
- // passphrase). |
- if (!bootstrap_token.empty()) { |
- DVLOG(1) << "Bootstrap token updated."; |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnBootstrapTokenUpdated(bootstrap_token)); |
- } |
- |
- if (!success) { |
- if (cryptographer->is_ready()) { |
- LOG(ERROR) << "Attempt to change passphrase failed while cryptographer " |
- << "was ready."; |
- } else if (cryptographer->has_pending_keys()) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_DECRYPTION, |
- cryptographer->GetPendingKeys())); |
- } else { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_ENCRYPTION, |
- sync_pb::EncryptedData())); |
- } |
- return; |
- } |
- |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseAccepted()); |
- DCHECK(cryptographer->is_ready()); |
- |
- // TODO(tim): Bug 58231. It would be nice if setting a passphrase didn't |
- // require messing with the Nigori node, because we can't set a passphrase |
- // until download conditions are met vs Cryptographer init. It seems like |
- // it's safe to defer this work. |
- sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics()); |
- // Does not modify specifics.encrypted() if the original decrypted data was |
- // the same. |
- if (!cryptographer->GetKeys(specifics.mutable_encrypted())) { |
- NOTREACHED(); |
- return; |
- } |
- specifics.set_using_explicit_passphrase(is_explicit); |
- nigori_node->SetNigoriSpecifics(specifics); |
- |
- // Does nothing if everything is already encrypted or the cryptographer has |
- // pending keys. |
- ReEncryptEverything(trans); |
-} |
- |
-bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { |
- ReadTransaction trans(FROM_HERE, &share_); |
- ReadNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. |
- NOTREACHED(); |
- return false; |
- } |
- |
- return node.GetNigoriSpecifics().using_explicit_passphrase(); |
-} |
- |
-void SyncManager::SyncInternal::RefreshEncryption() { |
- DCHECK(initialized_); |
- |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- WriteNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not " |
- << "found."; |
- return; |
- } |
- |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- |
- if (!cryptographer->is_ready()) { |
- DVLOG(1) << "Attempting to encrypt datatypes when cryptographer not " |
- << "initialized, prompting for passphrase."; |
- // TODO(zea): this isn't really decryption, but that's the only way we have |
- // to prompt the user for a passsphrase. See http://crbug.com/91379. |
- sync_pb::EncryptedData pending_keys; |
- if (cryptographer->has_pending_keys()) |
- pending_keys = cryptographer->GetPendingKeys(); |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_DECRYPTION, |
- pending_keys)); |
- return; |
- } |
- |
- UpdateNigoriEncryptionState(cryptographer, &node); |
- |
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); |
- |
- // We reencrypt everything regardless of whether the set of encrypted |
- // types changed to ensure that any stray unencrypted entries are overwritten. |
- ReEncryptEverything(&trans); |
-} |
- |
-void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { |
- Cryptographer* cryptographer = trans->GetCryptographer(); |
- if (!cryptographer || !cryptographer->is_ready()) |
- return; |
- syncable::ModelTypeSet encrypted_types = GetEncryptedTypes(trans); |
- ModelSafeRoutingInfo routes; |
- registrar_->GetModelSafeRoutingInfo(&routes); |
- std::string tag; |
- for (syncable::ModelTypeSet::Iterator iter = encrypted_types.First(); |
- iter.Good(); iter.Inc()) { |
- if (iter.Get() == syncable::PASSWORDS || |
- iter.Get() == syncable::NIGORI || |
- routes.count(iter.Get()) == 0) |
- continue; |
- ReadNode type_root(trans); |
- tag = syncable::ModelTypeToRootTag(iter.Get()); |
- if (type_root.InitByTagLookup(tag) != sync_api::BaseNode::INIT_OK) { |
- // This can happen when we enable a datatype for the first time on restart |
- // (for example when we upgrade) and therefore haven't done the initial |
- // download for that type at the time we RefreshEncryption. There's |
- // nothing we can do for now, so just move on to the next type. |
- continue; |
- } |
- |
- // Iterate through all children of this datatype. |
- std::queue<int64> to_visit; |
- int64 child_id = type_root.GetFirstChildId(); |
- to_visit.push(child_id); |
- while (!to_visit.empty()) { |
- child_id = to_visit.front(); |
- to_visit.pop(); |
- if (child_id == kInvalidId) |
- continue; |
- |
- WriteNode child(trans); |
- if (child.InitByIdLookup(child_id) != sync_api::BaseNode::INIT_OK) { |
- NOTREACHED(); |
- continue; |
- } |
- if (child.GetIsFolder()) { |
- to_visit.push(child.GetFirstChildId()); |
- } |
- if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) { |
- // Rewrite the specifics of the node with encrypted data if necessary |
- // (only rewrite the non-unique folders). |
- child.ResetFromSpecifics(); |
- } |
- to_visit.push(child.GetSuccessorId()); |
- } |
- } |
- |
- if (routes.count(syncable::PASSWORDS) > 0) { |
- // Passwords are encrypted with their own legacy scheme. |
- ReadNode passwords_root(trans); |
- std::string passwords_tag = |
- syncable::ModelTypeToRootTag(syncable::PASSWORDS); |
- // It's possible we'll have the password routing info and not the password |
- // root if we attempted to set a passphrase before passwords was enabled. |
- if (passwords_root.InitByTagLookup(passwords_tag) == |
- sync_api::BaseNode::INIT_OK) { |
- int64 child_id = passwords_root.GetFirstChildId(); |
- while (child_id != kInvalidId) { |
- WriteNode child(trans); |
- if (child.InitByIdLookup(child_id) != sync_api::BaseNode::INIT_OK) { |
- NOTREACHED(); |
- return; |
- } |
- child.SetPasswordSpecifics(child.GetPasswordSpecifics()); |
- child_id = child.GetSuccessorId(); |
- } |
- } |
- } |
- |
- // NOTE: We notify from within a transaction. |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnEncryptionComplete()); |
-} |
- |
-SyncManager::~SyncManager() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- delete data_; |
-} |
- |
-void SyncManager::AddObserver(Observer* observer) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->AddObserver(observer); |
-} |
- |
-void SyncManager::RemoveObserver(Observer* observer) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->RemoveObserver(observer); |
-} |
- |
-void SyncManager::StopSyncingForShutdown(const base::Closure& callback) { |
- data_->StopSyncingForShutdown(callback); |
-} |
- |
-void SyncManager::SyncInternal::StopSyncingForShutdown( |
- const base::Closure& callback) { |
- DVLOG(2) << "StopSyncingForShutdown"; |
- if (scheduler()) // May be null in tests. |
- scheduler()->RequestStop(callback); |
- else |
- created_on_loop_->PostTask(FROM_HERE, callback); |
- |
- if (connection_manager_.get()) |
- connection_manager_->TerminateAllIO(); |
-} |
- |
-void SyncManager::ShutdownOnSyncThread() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->ShutdownOnSyncThread(); |
-} |
- |
-void SyncManager::SyncInternal::ShutdownOnSyncThread() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- // Prevent any in-flight method calls from running. Also |
- // invalidates |weak_handle_this_| and |change_observer_|. |
- weak_ptr_factory_.InvalidateWeakPtrs(); |
- js_mutation_event_observer_.InvalidateWeakPtrs(); |
- |
- scheduler_.reset(); |
- |
- SetJsEventHandler(WeakHandle<JsEventHandler>()); |
- RemoveObserver(&js_sync_manager_observer_); |
- |
- RemoveObserver(&debug_info_event_listener_); |
- |
- if (sync_notifier_.get()) { |
- sync_notifier_->RemoveObserver(this); |
- } |
- sync_notifier_.reset(); |
- |
- if (connection_manager_.get()) { |
- connection_manager_->RemoveListener(this); |
- } |
- connection_manager_.reset(); |
- |
- net::NetworkChangeNotifier::RemoveIPAddressObserver(this); |
- observing_ip_address_changes_ = false; |
- |
- if (initialized_ && directory()) { |
- { |
- // Cryptographer should only be accessed while holding a |
- // transaction. |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- trans.GetCryptographer()->RemoveObserver(this); |
- } |
- directory()->SaveChanges(); |
- } |
- |
- share_.directory.reset(); |
- |
- change_delegate_ = NULL; |
- registrar_ = NULL; |
- |
- initialized_ = false; |
- |
- // We reset these here, since only now we know they will not be |
- // accessed from other threads (since we shut down everything). |
- change_observer_.Reset(); |
- weak_handle_this_.Reset(); |
-} |
- |
-void SyncManager::SyncInternal::OnIPAddressChanged() { |
- DVLOG(1) << "IP address change detected"; |
- if (!observing_ip_address_changes_) { |
- DVLOG(1) << "IP address change dropped."; |
- return; |
- } |
- |
- OnIPAddressChangedImpl(); |
-} |
- |
-void SyncManager::SyncInternal::OnIPAddressChangedImpl() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (scheduler()) |
- scheduler()->OnConnectionStatusChange(); |
-} |
- |
-void SyncManager::SyncInternal::OnServerConnectionEvent( |
- const ServerConnectionEvent& event) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (event.connection_code == |
- browser_sync::HttpResponse::SERVER_CONNECTION_OK) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnConnectionStatusChange(CONNECTION_OK)); |
- } |
- |
- if (event.connection_code == browser_sync::HttpResponse::SYNC_AUTH_ERROR) { |
- observing_ip_address_changes_ = false; |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); |
- } |
- |
- if (event.connection_code == |
- browser_sync::HttpResponse::SYNC_SERVER_ERROR) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); |
- } |
-} |
- |
-void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( |
- ModelTypeSet models_with_changes) { |
- // This notification happens immediately after the transaction mutex is |
- // released. This allows work to be performed without blocking other threads |
- // from acquiring a transaction. |
- if (!change_delegate_) |
- return; |
- |
- // Call commit. |
- for (ModelTypeSet::Iterator it = models_with_changes.First(); |
- it.Good(); it.Inc()) { |
- change_delegate_->OnChangesComplete(it.Get()); |
- change_observer_.Call( |
- FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, it.Get()); |
- } |
-} |
- |
-ModelTypeSet |
- SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) { |
- // This notification happens immediately before a syncable WriteTransaction |
- // falls out of scope. It happens while the channel mutex is still held, |
- // and while the transaction mutex is held, so it cannot be re-entrant. |
- if (!change_delegate_ || ChangeBuffersAreEmpty()) |
- return ModelTypeSet(); |
- |
- // This will continue the WriteTransaction using a read only wrapper. |
- // This is the last chance for read to occur in the WriteTransaction |
- // that's closing. This special ReadTransaction will not close the |
- // underlying transaction. |
- ReadTransaction read_trans(GetUserShare(), trans); |
- |
- ModelTypeSet models_with_changes; |
- for (int i = syncable::FIRST_REAL_MODEL_TYPE; |
- i < syncable::MODEL_TYPE_COUNT; ++i) { |
- const syncable::ModelType type = syncable::ModelTypeFromInt(i); |
- if (change_buffers_[type].IsEmpty()) |
- continue; |
- |
- ImmutableChangeRecordList ordered_changes; |
- // TODO(akalin): Propagate up the error further (see |
- // http://crbug.com/100907). |
- CHECK(change_buffers_[type].GetAllChangesInTreeOrder(&read_trans, |
- &ordered_changes)); |
- if (!ordered_changes.Get().empty()) { |
- change_delegate_-> |
- OnChangesApplied(type, &read_trans, ordered_changes); |
- change_observer_.Call(FROM_HERE, |
- &SyncManager::ChangeObserver::OnChangesApplied, |
- type, write_transaction_info.Get().id, ordered_changes); |
- models_with_changes.Put(type); |
- } |
- change_buffers_[i].Clear(); |
- } |
- return models_with_changes; |
-} |
- |
-void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) { |
- if (!scheduler()) { |
- return; |
- } |
- |
- // We have been notified about a user action changing a sync model. |
- LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << |
- "CALCULATE_CHANGES called with unapplied old changes."; |
- |
- // The mutated model type, or UNSPECIFIED if nothing was mutated. |
- syncable::ModelTypeSet mutated_model_types; |
- |
- const syncable::ImmutableEntryKernelMutationMap& mutations = |
- write_transaction_info.Get().mutations; |
- for (syncable::EntryKernelMutationMap::const_iterator it = |
- mutations.Get().begin(); it != mutations.Get().end(); ++it) { |
- if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { |
- continue; |
- } |
- |
- syncable::ModelType model_type = |
- syncable::GetModelTypeFromSpecifics( |
- it->second.mutated.ref(SPECIFICS)); |
- if (model_type < syncable::FIRST_REAL_MODEL_TYPE) { |
- NOTREACHED() << "Permanent or underspecified item changed via syncapi."; |
- continue; |
- } |
- |
- // Found real mutation. |
- if (model_type != syncable::UNSPECIFIED) { |
- mutated_model_types.Put(model_type); |
- } |
- } |
- |
- // Nudge if necessary. |
- if (!mutated_model_types.Empty()) { |
- if (weak_handle_this_.IsInitialized()) { |
- weak_handle_this_.Call(FROM_HERE, |
- &SyncInternal::RequestNudgeForDataTypes, |
- FROM_HERE, |
- mutated_model_types); |
- } else { |
- NOTREACHED(); |
- } |
- } |
-} |
- |
-void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, |
- syncable::ModelType type, ChangeReorderBuffer* buffer, |
- Cryptographer* cryptographer, const syncable::EntryKernel& original, |
- bool existed_before, bool exists_now) { |
- // If this is a deletion and the datatype was encrypted, we need to decrypt it |
- // and attach it to the buffer. |
- if (!exists_now && existed_before) { |
- sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); |
- if (type == syncable::PASSWORDS) { |
- // Passwords must use their own legacy ExtraPasswordChangeRecordData. |
- scoped_ptr<sync_pb::PasswordSpecificsData> data( |
- DecryptPasswordSpecifics(original_specifics, cryptographer)); |
- if (!data.get()) { |
- NOTREACHED(); |
- return; |
- } |
- buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); |
- } else if (original_specifics.has_encrypted()) { |
- // All other datatypes can just create a new unencrypted specifics and |
- // attach it. |
- const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); |
- if (!cryptographer->Decrypt(encrypted, &original_specifics)) { |
- NOTREACHED(); |
- return; |
- } |
- } |
- buffer->SetSpecificsForId(id, original_specifics); |
- } |
-} |
- |
-void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( |
- const ImmutableWriteTransactionInfo& write_transaction_info, |
- syncable::BaseTransaction* trans) { |
- // We only expect one notification per sync step, so change_buffers_ should |
- // contain no pending entries. |
- LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << |
- "CALCULATE_CHANGES called with unapplied old changes."; |
- |
- Cryptographer* crypto = directory()->GetCryptographer(trans); |
- const syncable::ImmutableEntryKernelMutationMap& mutations = |
- write_transaction_info.Get().mutations; |
- for (syncable::EntryKernelMutationMap::const_iterator it = |
- mutations.Get().begin(); it != mutations.Get().end(); ++it) { |
- bool existed_before = !it->second.original.ref(syncable::IS_DEL); |
- bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); |
- |
- // Omit items that aren't associated with a model. |
- syncable::ModelType type = |
- syncable::GetModelTypeFromSpecifics( |
- it->second.mutated.ref(SPECIFICS)); |
- if (type < syncable::FIRST_REAL_MODEL_TYPE) |
- continue; |
- |
- int64 handle = it->first; |
- if (exists_now && !existed_before) |
- change_buffers_[type].PushAddedItem(handle); |
- else if (!exists_now && existed_before) |
- change_buffers_[type].PushDeletedItem(handle); |
- else if (exists_now && existed_before && |
- VisiblePropertiesDiffer(it->second, crypto)) { |
- change_buffers_[type].PushUpdatedItem( |
- handle, VisiblePositionsDiffer(it->second)); |
- } |
- |
- SetExtraChangeRecordData(handle, type, &change_buffers_[type], crypto, |
- it->second.original, existed_before, exists_now); |
- } |
-} |
- |
-SyncManager::Status SyncManager::SyncInternal::GetStatus() { |
- return allstatus_.status(); |
-} |
- |
-void SyncManager::SyncInternal::RequestNudge( |
- const tracked_objects::Location& location) { |
- if (scheduler()) { |
- scheduler()->ScheduleNudge( |
- TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL, |
- ModelTypeSet(), location); |
- } |
-} |
- |
-TimeDelta SyncManager::SyncInternal::GetNudgeDelayTimeDelta( |
- const ModelType& model_type) { |
- return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this); |
-} |
- |
-void SyncManager::SyncInternal::RequestNudgeForDataTypes( |
- const tracked_objects::Location& nudge_location, |
- ModelTypeSet types) { |
- if (!scheduler()) { |
- NOTREACHED(); |
- return; |
- } |
- |
- debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get()); |
- |
- // TODO(lipalani) : Calculate the nudge delay based on all types. |
- base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta( |
- types.First().Get(), |
- this); |
- scheduler()->ScheduleNudge(nudge_delay, |
- browser_sync::NUDGE_SOURCE_LOCAL, |
- types, |
- nudge_location); |
-} |
- |
-void SyncManager::SyncInternal::OnSyncEngineEvent( |
- const SyncEngineEvent& event) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- // Only send an event if this is due to a cycle ending and this cycle |
- // concludes a canonical "sync" process; that is, based on what is known |
- // locally we are "all happy" and up-to-date. There may be new changes on |
- // the server, but we'll get them on a subsequent sync. |
- // |
- // Notifications are sent at the end of every sync cycle, regardless of |
- // whether we should sync again. |
- if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { |
- ModelSafeRoutingInfo enabled_types; |
- registrar_->GetModelSafeRoutingInfo(&enabled_types); |
- { |
- // Check to see if we need to notify the frontend that we have newly |
- // encrypted types or that we require a passphrase. |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- // If we've completed a sync cycle and the cryptographer isn't ready |
- // yet, prompt the user for a passphrase. |
- if (cryptographer->has_pending_keys()) { |
- DVLOG(1) << "OnPassPhraseRequired Sent"; |
- sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys(); |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_DECRYPTION, |
- pending_keys)); |
- } else if (!cryptographer->is_ready() && |
- event.snapshot->initial_sync_ended.Has(syncable::NIGORI)) { |
- DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not " |
- << "ready"; |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnPassphraseRequired(sync_api::REASON_ENCRYPTION, |
- sync_pb::EncryptedData())); |
- } |
- |
- NotifyCryptographerState(cryptographer); |
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); |
- } |
- |
- if (!initialized_) { |
- LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not " |
- << "initialized"; |
- return; |
- } |
- |
- if (!event.snapshot->has_more_to_sync) { |
- // To account for a nigori node arriving with stale/bad data, we ensure |
- // that the nigori node is up to date at the end of each cycle. |
- WriteTransaction trans(FROM_HERE, GetUserShare()); |
- WriteNode nigori_node(&trans); |
- if (nigori_node.InitByTagLookup(kNigoriTag) == |
- sync_api::BaseNode::INIT_OK) { |
- Cryptographer* cryptographer = trans.GetCryptographer(); |
- UpdateNigoriEncryptionState(cryptographer, &nigori_node); |
- } |
- |
- DVLOG(1) << "Sending OnSyncCycleCompleted"; |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnSyncCycleCompleted(event.snapshot)); |
- } |
- |
- // This is here for tests, which are still using p2p notifications. |
- // |
- // TODO(chron): Consider changing this back to track has_more_to_sync |
- // only notify peers if a successful commit has occurred. |
- bool is_notifiable_commit = |
- (event.snapshot->syncer_status.num_successful_commits > 0); |
- if (is_notifiable_commit) { |
- if (sync_notifier_.get()) { |
- const ModelTypeSet changed_types = |
- syncable::ModelTypePayloadMapToEnumSet( |
- event.snapshot->source.types); |
- sync_notifier_->SendNotification(changed_types); |
- } else { |
- DVLOG(1) << "Not sending notification: sync_notifier_ is NULL"; |
- } |
- } |
- } |
- |
- if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnStopSyncingPermanently()); |
- return; |
- } |
- |
- if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnClearServerDataSucceeded()); |
- return; |
- } |
- |
- if (event.what_happened == SyncEngineEvent::CLEAR_SERVER_DATA_FAILED) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnClearServerDataFailed()); |
- return; |
- } |
- |
- if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnUpdatedToken(event.updated_token)); |
- return; |
- } |
- |
- if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) { |
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_, |
- OnActionableError( |
- event.snapshot->errors.sync_protocol_error)); |
- return; |
- } |
- |
-} |
- |
-void SyncManager::SyncInternal::SetJsEventHandler( |
- const WeakHandle<JsEventHandler>& event_handler) { |
- js_event_handler_ = event_handler; |
- js_sync_manager_observer_.SetJsEventHandler(js_event_handler_); |
- js_mutation_event_observer_.SetJsEventHandler(js_event_handler_); |
-} |
- |
-void SyncManager::SyncInternal::ProcessJsMessage( |
- const std::string& name, const JsArgList& args, |
- const WeakHandle<JsReplyHandler>& reply_handler) { |
- if (!initialized_) { |
- NOTREACHED(); |
- return; |
- } |
- |
- if (!reply_handler.IsInitialized()) { |
- DVLOG(1) << "Uninitialized reply handler; dropping unknown message " |
- << name << " with args " << args.ToString(); |
- return; |
- } |
- |
- JsMessageHandler js_message_handler = js_message_handlers_[name]; |
- if (js_message_handler.is_null()) { |
- DVLOG(1) << "Dropping unknown message " << name |
- << " with args " << args.ToString(); |
- return; |
- } |
- |
- reply_handler.Call(FROM_HERE, |
- &JsReplyHandler::HandleJsReply, |
- name, js_message_handler.Run(args)); |
-} |
- |
-void SyncManager::SyncInternal::BindJsMessageHandler( |
- const std::string& name, |
- UnboundJsMessageHandler unbound_message_handler) { |
- js_message_handlers_[name] = |
- base::Bind(unbound_message_handler, base::Unretained(this)); |
-} |
- |
-DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue( |
- const NotificationInfoMap& notification_info) { |
- DictionaryValue* value = new DictionaryValue(); |
- |
- for (NotificationInfoMap::const_iterator it = notification_info.begin(); |
- it != notification_info.end(); ++it) { |
- const std::string& model_type_str = |
- syncable::ModelTypeToString(it->first); |
- value->Set(model_type_str, it->second.ToValue()); |
- } |
- |
- return value; |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetNotificationState( |
- const JsArgList& args) { |
- bool notifications_enabled = allstatus_.status().notifications_enabled; |
- ListValue return_args; |
- return_args.Append(Value::CreateBooleanValue(notifications_enabled)); |
- return JsArgList(&return_args); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetNotificationInfo( |
- const JsArgList& args) { |
- ListValue return_args; |
- return_args.Append(NotificationInfoToValue(notification_info_map_)); |
- return JsArgList(&return_args); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetRootNodeDetails( |
- const JsArgList& args) { |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- ReadNode root(&trans); |
- root.InitByRootLookup(); |
- ListValue return_args; |
- return_args.Append(root.GetDetailsAsValue()); |
- return JsArgList(&return_args); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetClientServerTraffic( |
- const JsArgList& args) { |
- ListValue return_args; |
- ListValue* value = traffic_recorder_.ToValue(); |
- if (value != NULL) |
- return_args.Append(value); |
- return JsArgList(&return_args); |
-} |
- |
-namespace { |
- |
-int64 GetId(const ListValue& ids, int i) { |
- std::string id_str; |
- if (!ids.GetString(i, &id_str)) { |
- return kInvalidId; |
- } |
- int64 id = kInvalidId; |
- if (!base::StringToInt64(id_str, &id)) { |
- return kInvalidId; |
- } |
- return id; |
-} |
- |
-JsArgList GetNodeInfoById(const JsArgList& args, |
- UserShare* user_share, |
- DictionaryValue* (BaseNode::*info_getter)() const) { |
- CHECK(info_getter); |
- ListValue return_args; |
- ListValue* node_summaries = new ListValue(); |
- return_args.Append(node_summaries); |
- ListValue* id_list = NULL; |
- ReadTransaction trans(FROM_HERE, user_share); |
- if (args.Get().GetList(0, &id_list)) { |
- CHECK(id_list); |
- for (size_t i = 0; i < id_list->GetSize(); ++i) { |
- int64 id = GetId(*id_list, i); |
- if (id == kInvalidId) { |
- continue; |
- } |
- ReadNode node(&trans); |
- if (node.InitByIdLookup(id) != sync_api::BaseNode::INIT_OK) { |
- continue; |
- } |
- node_summaries->Append((node.*info_getter)()); |
- } |
- } |
- return JsArgList(&return_args); |
-} |
- |
-} // namespace |
- |
-JsArgList SyncManager::SyncInternal::GetNodeSummariesById( |
- const JsArgList& args) { |
- return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetNodeDetailsById( |
- const JsArgList& args) { |
- return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetAllNodes( |
- const JsArgList& args) { |
- ListValue return_args; |
- ListValue* result = new ListValue(); |
- return_args.Append(result); |
- |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- std::vector<const syncable::EntryKernel*> entry_kernels; |
- trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(), |
- &entry_kernels); |
- |
- for (std::vector<const syncable::EntryKernel*>::const_iterator it = |
- entry_kernels.begin(); it != entry_kernels.end(); ++it) { |
- result->Append((*it)->ToValue()); |
- } |
- |
- return JsArgList(&return_args); |
-} |
- |
-JsArgList SyncManager::SyncInternal::GetChildNodeIds( |
- const JsArgList& args) { |
- ListValue return_args; |
- ListValue* child_ids = new ListValue(); |
- return_args.Append(child_ids); |
- int64 id = GetId(args.Get(), 0); |
- if (id != kInvalidId) { |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- syncable::Directory::ChildHandles child_handles; |
- trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(), |
- id, &child_handles); |
- for (syncable::Directory::ChildHandles::const_iterator it = |
- child_handles.begin(); it != child_handles.end(); ++it) { |
- child_ids->Append(Value::CreateStringValue( |
- base::Int64ToString(*it))); |
- } |
- } |
- return JsArgList(&return_args); |
-} |
- |
-void SyncManager::SyncInternal::OnEncryptedTypesChanged( |
- syncable::ModelTypeSet encrypted_types, |
- bool encrypt_everything) { |
- // NOTE: We're in a transaction. |
- FOR_EACH_OBSERVER( |
- SyncManager::Observer, observers_, |
- OnEncryptedTypesChanged(encrypted_types, encrypt_everything)); |
-} |
- |
-void SyncManager::SyncInternal::OnNotificationStateChange( |
- bool notifications_enabled) { |
- DVLOG(1) << "P2P: Notifications enabled = " |
- << (notifications_enabled ? "true" : "false"); |
- allstatus_.SetNotificationsEnabled(notifications_enabled); |
- if (scheduler()) { |
- scheduler()->set_notifications_enabled(notifications_enabled); |
- } |
- if (js_event_handler_.IsInitialized()) { |
- DictionaryValue details; |
- details.Set("enabled", Value::CreateBooleanValue(notifications_enabled)); |
- js_event_handler_.Call(FROM_HERE, |
- &JsEventHandler::HandleJsEvent, |
- "onNotificationStateChange", |
- JsEventDetails(&details)); |
- } |
-} |
- |
-void SyncManager::SyncInternal::UpdateNotificationInfo( |
- const syncable::ModelTypePayloadMap& type_payloads) { |
- for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin(); |
- it != type_payloads.end(); ++it) { |
- NotificationInfo* info = ¬ification_info_map_[it->first]; |
- info->total_count++; |
- info->payload = it->second; |
- } |
-} |
- |
-void SyncManager::SyncInternal::OnIncomingNotification( |
- const syncable::ModelTypePayloadMap& type_payloads, |
- sync_notifier::IncomingNotificationSource source) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (source == sync_notifier::LOCAL_NOTIFICATION) { |
- if (scheduler()) { |
- scheduler()->ScheduleNudgeWithPayloads( |
- TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec), |
- browser_sync::NUDGE_SOURCE_LOCAL_REFRESH, |
- type_payloads, FROM_HERE); |
- } |
- } else if (!type_payloads.empty()) { |
- if (scheduler()) { |
- scheduler()->ScheduleNudgeWithPayloads( |
- TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), |
- browser_sync::NUDGE_SOURCE_NOTIFICATION, |
- type_payloads, FROM_HERE); |
- } |
- allstatus_.IncrementNotificationsReceived(); |
- UpdateNotificationInfo(type_payloads); |
- debug_info_event_listener_.OnIncomingNotification(type_payloads); |
- } else { |
- LOG(WARNING) << "Sync received notification without any type information."; |
- } |
- |
- if (js_event_handler_.IsInitialized()) { |
- DictionaryValue details; |
- ListValue* changed_types = new ListValue(); |
- details.Set("changedTypes", changed_types); |
- for (syncable::ModelTypePayloadMap::const_iterator |
- it = type_payloads.begin(); |
- it != type_payloads.end(); ++it) { |
- const std::string& model_type_str = |
- syncable::ModelTypeToString(it->first); |
- changed_types->Append(Value::CreateStringValue(model_type_str)); |
- } |
- details.SetString("source", (source == sync_notifier::LOCAL_NOTIFICATION) ? |
- "LOCAL_NOTIFICATION" : "REMOTE_NOTIFICATION"); |
- js_event_handler_.Call(FROM_HERE, |
- &JsEventHandler::HandleJsEvent, |
- "onIncomingNotification", |
- JsEventDetails(&details)); |
- } |
-} |
- |
-void SyncManager::SyncInternal::StoreState( |
- const std::string& state) { |
- if (!directory()) { |
- LOG(ERROR) << "Could not write notification state"; |
- // TODO(akalin): Propagate result callback all the way to this |
- // function and call it with "false" to signal failure. |
- return; |
- } |
- if (VLOG_IS_ON(1)) { |
- std::string encoded_state; |
- base::Base64Encode(state, &encoded_state); |
- DVLOG(1) << "Writing notification state: " << encoded_state; |
- } |
- directory()->SetNotificationState(state); |
- directory()->SaveChanges(); |
-} |
- |
-void SyncManager::SyncInternal::AddObserver( |
- SyncManager::Observer* observer) { |
- observers_.AddObserver(observer); |
-} |
- |
-void SyncManager::SyncInternal::RemoveObserver( |
- SyncManager::Observer* observer) { |
- observers_.RemoveObserver(observer); |
-} |
- |
-SyncManager::Status SyncManager::GetDetailedStatus() const { |
- return data_->GetStatus(); |
-} |
- |
-void SyncManager::SaveChanges() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->SaveChanges(); |
-} |
- |
-void SyncManager::SyncInternal::SaveChanges() { |
- directory()->SaveChanges(); |
-} |
- |
-UserShare* SyncManager::GetUserShare() const { |
- return data_->GetUserShare(); |
-} |
- |
-void SyncManager::RefreshNigori(const std::string& chrome_version, |
- const base::Closure& done_callback) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->UpdateCryptographerAndNigori( |
- chrome_version, |
- done_callback); |
-} |
- |
-TimeDelta SyncManager::GetNudgeDelayTimeDelta( |
- const ModelType& model_type) { |
- return data_->GetNudgeDelayTimeDelta(model_type); |
-} |
- |
-syncable::ModelTypeSet SyncManager::GetEncryptedDataTypesForTest() const { |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- return GetEncryptedTypes(&trans); |
-} |
- |
-bool SyncManager::ReceivedExperimentalTypes(syncable::ModelTypeSet* to_add) |
- const { |
- ReadTransaction trans(FROM_HERE, GetUserShare()); |
- ReadNode node(&trans); |
- if (node.InitByTagLookup(kNigoriTag) != sync_api::BaseNode::INIT_OK) { |
- DVLOG(1) << "Couldn't find Nigori node."; |
- return false; |
- } |
- if (node.GetNigoriSpecifics().sync_tabs()) { |
- to_add->Put(syncable::SESSIONS); |
- return true; |
- } |
- return false; |
-} |
- |
-bool SyncManager::HasUnsyncedItems() const { |
- sync_api::ReadTransaction trans(FROM_HERE, GetUserShare()); |
- return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); |
-} |
- |
-void SyncManager::TriggerOnNotificationStateChangeForTest( |
- bool notifications_enabled) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- data_->OnNotificationStateChange(notifications_enabled); |
-} |
- |
-void SyncManager::TriggerOnIncomingNotificationForTest( |
- ModelTypeSet model_types) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- syncable::ModelTypePayloadMap model_types_with_payloads = |
- syncable::ModelTypePayloadMapFromEnumSet(model_types, |
- std::string()); |
- |
- data_->OnIncomingNotification(model_types_with_payloads, |
- sync_notifier::REMOTE_NOTIFICATION); |
-} |
- |
-const char* ConnectionStatusToString(ConnectionStatus status) { |
- switch (status) { |
- case CONNECTION_OK: |
- return "CONNECTION_OK"; |
- case CONNECTION_AUTH_ERROR: |
- return "CONNECTION_AUTH_ERROR"; |
- case CONNECTION_SERVER_ERROR: |
- return "CONNECTION_SERVER_ERROR"; |
- default: |
- NOTREACHED(); |
- return "INVALID_CONNECTION_STATUS"; |
- } |
-} |
- |
-// Helper function that converts a PassphraseRequiredReason value to a string. |
-const char* PassphraseRequiredReasonToString( |
- PassphraseRequiredReason reason) { |
- switch (reason) { |
- case REASON_PASSPHRASE_NOT_REQUIRED: |
- return "REASON_PASSPHRASE_NOT_REQUIRED"; |
- case REASON_ENCRYPTION: |
- return "REASON_ENCRYPTION"; |
- case REASON_DECRYPTION: |
- return "REASON_DECRYPTION"; |
- default: |
- NOTREACHED(); |
- return "INVALID_REASON"; |
- } |
-} |
- |
-// Helper function to determine if initial sync had ended for types. |
-bool InitialSyncEndedForTypes(syncable::ModelTypeSet types, |
- sync_api::UserShare* share) { |
- for (syncable::ModelTypeSet::Iterator i = types.First(); |
- i.Good(); i.Inc()) { |
- if (!share->directory->initial_sync_ended_for_type(i.Get())) |
- return false; |
- } |
- return true; |
-} |
- |
-syncable::ModelTypeSet GetTypesWithEmptyProgressMarkerToken( |
- syncable::ModelTypeSet types, |
- sync_api::UserShare* share) { |
- syncable::ModelTypeSet result; |
- for (syncable::ModelTypeSet::Iterator i = types.First(); |
- i.Good(); i.Inc()) { |
- sync_pb::DataTypeProgressMarker marker; |
- share->directory->GetDownloadProgress(i.Get(), &marker); |
- |
- if (marker.token().empty()) |
- result.Put(i.Get()); |
- |
- } |
- return result; |
-} |
- |
-} // namespace sync_api |