OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sync/engine/syncapi.h" | 5 #include "chrome/browser/sync/engine/syncapi.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <bitset> | 8 #include <bitset> |
9 #include <iomanip> | 9 #include <iomanip> |
10 #include <list> | 10 #include <list> |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 } | 195 } |
196 } | 196 } |
197 | 197 |
198 UserShare::UserShare() {} | 198 UserShare::UserShare() {} |
199 | 199 |
200 UserShare::~UserShare() {} | 200 UserShare::~UserShare() {} |
201 | 201 |
202 //////////////////////////////////// | 202 //////////////////////////////////// |
203 // BaseNode member definitions. | 203 // BaseNode member definitions. |
204 | 204 |
205 BaseNode::BaseNode() {} | 205 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {} |
206 | 206 |
207 BaseNode::~BaseNode() {} | 207 BaseNode::~BaseNode() {} |
208 | 208 |
209 std::string BaseNode::GenerateSyncableHash( | 209 std::string BaseNode::GenerateSyncableHash( |
210 syncable::ModelType model_type, const std::string& client_tag) { | 210 syncable::ModelType model_type, const std::string& client_tag) { |
211 // blank PB with just the extension in it has termination symbol, | 211 // blank PB with just the extension in it has termination symbol, |
212 // handy for delimiter | 212 // handy for delimiter |
213 sync_pb::EntitySpecifics serialized_type; | 213 sync_pb::EntitySpecifics serialized_type; |
214 syncable::AddDefaultExtensionValue(model_type, &serialized_type); | 214 syncable::AddDefaultExtensionValue(model_type, &serialized_type); |
215 std::string hash_input; | 215 std::string hash_input; |
(...skipping 22 matching lines...) Expand all Loading... | |
238 } | 238 } |
239 | 239 |
240 bool BaseNode::DecryptIfNecessary() { | 240 bool BaseNode::DecryptIfNecessary() { |
241 if (GetIsFolder()) return true; // Ignore the top-level datatype folder. | 241 if (GetIsFolder()) return true; // Ignore the top-level datatype folder. |
242 const sync_pb::EntitySpecifics& specifics = | 242 const sync_pb::EntitySpecifics& specifics = |
243 GetEntry()->Get(syncable::SPECIFICS); | 243 GetEntry()->Get(syncable::SPECIFICS); |
244 if (specifics.HasExtension(sync_pb::password)) { | 244 if (specifics.HasExtension(sync_pb::password)) { |
245 // Passwords have their own legacy encryption structure. | 245 // Passwords have their own legacy encryption structure. |
246 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( | 246 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( |
247 specifics, GetTransaction()->GetCryptographer())); | 247 specifics, GetTransaction()->GetCryptographer())); |
248 if (!data.get()) | 248 if (!data.get()) { |
249 LOG(ERROR) << "Failed to decrypt password specifics."; | |
249 return false; | 250 return false; |
251 } | |
250 password_data_.swap(data); | 252 password_data_.swap(data); |
251 return true; | 253 return true; |
252 } | 254 } |
253 | 255 |
254 // We assume any node with the encrypted field set has encrypted data. | 256 // We assume any node with the encrypted field set has encrypted data. |
255 if (!specifics.has_encrypted()) | 257 if (!specifics.has_encrypted()) |
256 return true; | 258 return true; |
257 | 259 |
258 const sync_pb::EncryptedData& encrypted = | 260 const sync_pb::EncryptedData& encrypted = |
259 specifics.encrypted(); | 261 specifics.encrypted(); |
260 std::string plaintext_data = GetTransaction()->GetCryptographer()-> | 262 std::string plaintext_data = GetTransaction()->GetCryptographer()-> |
261 DecryptToString(encrypted); | 263 DecryptToString(encrypted); |
262 if (plaintext_data.length() == 0) | 264 if (plaintext_data.length() == 0 || |
263 return false; | 265 !unencrypted_data_.ParseFromString(plaintext_data)) { |
264 if (!unencrypted_data_.ParseFromString(plaintext_data)) { | |
265 LOG(ERROR) << "Failed to decrypt encrypted node of type " << | 266 LOG(ERROR) << "Failed to decrypt encrypted node of type " << |
266 syncable::ModelTypeToString(GetModelType()) << "."; | 267 syncable::ModelTypeToString(GetModelType()) << "."; |
267 return false; | 268 return false; |
268 } | 269 } |
269 return true; | 270 return true; |
270 } | 271 } |
271 | 272 |
272 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics( | 273 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics( |
273 const syncable::Entry* entry) const { | 274 const syncable::Entry* entry) const { |
274 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS); | 275 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS); |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
397 return GetEntitySpecifics().GetExtension(sync_pb::bookmark); | 398 return GetEntitySpecifics().GetExtension(sync_pb::bookmark); |
398 } | 399 } |
399 | 400 |
400 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { | 401 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { |
401 DCHECK_EQ(syncable::NIGORI, GetModelType()); | 402 DCHECK_EQ(syncable::NIGORI, GetModelType()); |
402 return GetEntitySpecifics().GetExtension(sync_pb::nigori); | 403 return GetEntitySpecifics().GetExtension(sync_pb::nigori); |
403 } | 404 } |
404 | 405 |
405 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { | 406 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { |
406 DCHECK_EQ(syncable::PASSWORDS, GetModelType()); | 407 DCHECK_EQ(syncable::PASSWORDS, GetModelType()); |
407 DCHECK(password_data_.get()); | |
408 return *password_data_; | 408 return *password_data_; |
409 } | 409 } |
410 | 410 |
411 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { | 411 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { |
412 DCHECK_EQ(syncable::THEMES, GetModelType()); | 412 DCHECK_EQ(syncable::THEMES, GetModelType()); |
413 return GetEntitySpecifics().GetExtension(sync_pb::theme); | 413 return GetEntitySpecifics().GetExtension(sync_pb::theme); |
414 } | 414 } |
415 | 415 |
416 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { | 416 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { |
417 DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); | 417 DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
564 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext( | 564 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext( |
565 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer)); | 565 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer)); |
566 if (old_plaintext.get() && | 566 if (old_plaintext.get() && |
567 old_plaintext->SerializeAsString() == data.SerializeAsString() && | 567 old_plaintext->SerializeAsString() == data.SerializeAsString() && |
568 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) { | 568 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) { |
569 return; | 569 return; |
570 } | 570 } |
571 | 571 |
572 sync_pb::PasswordSpecifics new_value; | 572 sync_pb::PasswordSpecifics new_value; |
573 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) { | 573 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) { |
574 NOTREACHED(); | 574 NOTREACHED() << "Failed to encrypt password, possibly due to sync node " |
575 << "corruption"; | |
576 return; | |
575 } | 577 } |
576 | 578 |
577 sync_pb::EntitySpecifics entity_specifics; | 579 sync_pb::EntitySpecifics entity_specifics; |
578 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value); | 580 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value); |
579 SetEntitySpecifics(entity_specifics); | 581 SetEntitySpecifics(entity_specifics); |
580 } | 582 } |
581 | 583 |
582 void WriteNode::SetThemeSpecifics( | 584 void WriteNode::SetThemeSpecifics( |
583 const sync_pb::ThemeSpecifics& new_value) { | 585 const sync_pb::ThemeSpecifics& new_value) { |
584 sync_pb::EntitySpecifics entity_specifics; | 586 sync_pb::EntitySpecifics entity_specifics; |
(...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1196 | 1198 |
1197 // Called when the user disables or enables a sync type. | 1199 // Called when the user disables or enables a sync type. |
1198 void UpdateEnabledTypes(); | 1200 void UpdateEnabledTypes(); |
1199 | 1201 |
1200 // Tell the sync engine to start the syncing process. | 1202 // Tell the sync engine to start the syncing process. |
1201 void StartSyncingNormally(); | 1203 void StartSyncingNormally(); |
1202 | 1204 |
1203 // Whether or not the Nigori node is encrypted using an explicit passphrase. | 1205 // Whether or not the Nigori node is encrypted using an explicit passphrase. |
1204 bool IsUsingExplicitPassphrase(); | 1206 bool IsUsingExplicitPassphrase(); |
1205 | 1207 |
1208 // Update the Cryptographer from the current nigori node. | |
1209 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so | |
1210 // should only be called after syncapi is fully initialized. | |
1211 // Returns true if cryptographer is ready, false otherwise. | |
1212 bool UpdateCryptographerFromNigori(); | |
1213 | |
1206 // Set the datatypes we want to encrypt and encrypt any nodes as necessary. | 1214 // Set the datatypes we want to encrypt and encrypt any nodes as necessary. |
1207 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); | 1215 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); |
1208 | 1216 |
1209 // Try to set the current passphrase to |passphrase|, and record whether | 1217 // Try to set the current passphrase to |passphrase|, and record whether |
1210 // it is an explicit passphrase or implicitly using gaia in the Nigori | 1218 // it is an explicit passphrase or implicitly using gaia in the Nigori |
1211 // node. | 1219 // node. |
1212 void SetPassphrase(const std::string& passphrase, bool is_explicit); | 1220 void SetPassphrase(const std::string& passphrase, bool is_explicit); |
1213 | 1221 |
1214 // Call periodically from a database-safe thread to persist recent changes | 1222 // Call periodically from a database-safe thread to persist recent changes |
1215 // to the syncapi model. | 1223 // to the syncapi model. |
(...skipping 572 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1788 } | 1796 } |
1789 | 1797 |
1790 // Do this once the directory is opened. | 1798 // Do this once the directory is opened. |
1791 BootstrapEncryption(restored_key_for_bootstrapping); | 1799 BootstrapEncryption(restored_key_for_bootstrapping); |
1792 MarkAndNotifyInitializationComplete(); | 1800 MarkAndNotifyInitializationComplete(); |
1793 return signed_in; | 1801 return signed_in; |
1794 } | 1802 } |
1795 | 1803 |
1796 void SyncManager::SyncInternal::BootstrapEncryption( | 1804 void SyncManager::SyncInternal::BootstrapEncryption( |
1797 const std::string& restored_key_for_bootstrapping) { | 1805 const std::string& restored_key_for_bootstrapping) { |
1806 // Cryptographer should only be accessed while holding a transaction. | |
1807 ReadTransaction trans(GetUserShare()); | |
1808 Cryptographer* cryptographer = trans.GetCryptographer(); | |
1809 | |
1810 // Set the bootstrap token before bailing out if nigori node is not there. | |
1811 // This could happen if server asked us to migrate nigri. | |
1812 cryptographer->Bootstrap(restored_key_for_bootstrapping); | |
1813 } | |
1814 | |
1815 bool SyncManager::SyncInternal::UpdateCryptographerFromNigori() { | |
1798 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); | 1816 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); |
1799 if (!lookup.good()) { | 1817 if (!lookup.good()) { |
1800 VLOG(0) << "BootstrapEncryption: lookup not good so bailing out"; | 1818 NOTREACHED() << "BootstrapEncryption: lookup not good so bailing out"; |
1819 return false; | |
1820 } | |
1821 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI)) | |
1822 return false; // Should only happen during first time sync. | |
1823 | |
1824 ReadTransaction trans(GetUserShare()); | |
1825 Cryptographer* cryptographer = trans.GetCryptographer(); | |
1826 | |
1827 ReadNode node(&trans); | |
1828 if (!node.InitByTagLookup(kNigoriTag)) { | |
1801 NOTREACHED(); | 1829 NOTREACHED(); |
1802 return; | 1830 return false; |
1831 } | |
1832 Cryptographer::UpdateResult result = | |
1833 cryptographer->Update(node.GetNigoriSpecifics()); | |
1834 if (result == Cryptographer::NEEDS_PASSPHRASE) { | |
1835 ObserverList<SyncManager::Observer> temp_obs_list; | |
1836 CopyObservers(&temp_obs_list); | |
1837 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, | |
1838 OnPassphraseRequired(sync_api::REASON_DECRYPTION)); | |
1803 } | 1839 } |
1804 | 1840 |
1805 sync_pb::NigoriSpecifics nigori; | 1841 return cryptographer->is_ready(); |
1806 syncable::ModelTypeSet encrypted_types; | |
1807 { | |
1808 // Cryptographer should only be accessed while holding a transaction. | |
1809 ReadTransaction trans(GetUserShare()); | |
1810 Cryptographer* cryptographer = trans.GetCryptographer(); | |
1811 | |
1812 // Set the bootstrap token before bailing out if nigori node is not there. | |
1813 // This could happen if server asked us to migrate nigri. | |
1814 cryptographer->Bootstrap(restored_key_for_bootstrapping); | |
1815 | |
1816 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI)) | |
1817 return; | |
1818 | |
1819 ReadNode node(&trans); | |
1820 if (!node.InitByTagLookup(kNigoriTag)) { | |
1821 NOTREACHED(); | |
1822 return; | |
1823 } | |
1824 | |
1825 nigori.CopyFrom(node.GetNigoriSpecifics()); | |
1826 Cryptographer::UpdateResult result = cryptographer->Update(nigori); | |
1827 if (result == Cryptographer::NEEDS_PASSPHRASE) { | |
1828 ObserverList<SyncManager::Observer> temp_obs_list; | |
1829 CopyObservers(&temp_obs_list); | |
1830 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list, | |
1831 OnPassphraseRequired(sync_api::REASON_DECRYPTION)); | |
1832 } | |
1833 | |
1834 // Refresh list of encrypted datatypes. | |
1835 encrypted_types = GetEncryptedTypes(&trans); | |
1836 } | |
1837 | |
1838 | |
1839 | |
1840 // Ensure any datatypes that need encryption are encrypted. | |
1841 EncryptDataTypes(encrypted_types); | |
1842 } | 1842 } |
1843 | 1843 |
1844 void SyncManager::SyncInternal::StartSyncingNormally() { | 1844 void SyncManager::SyncInternal::StartSyncingNormally() { |
1845 // Start the syncer thread. This won't actually | 1845 // Start the syncer thread. This won't actually |
1846 // result in any syncing until at least the | 1846 // result in any syncing until at least the |
1847 // DirectoryManager broadcasts the OPENED event, | 1847 // DirectoryManager broadcasts the OPENED event, |
1848 // and a valid server connection is detected. | 1848 // and a valid server connection is detected. |
1849 if (syncer_thread()) // NULL during certain unittests. | 1849 if (syncer_thread()) // NULL during certain unittests. |
1850 syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); | 1850 syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); |
1851 } | 1851 } |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2078 } | 2078 } |
2079 | 2079 |
2080 void SyncManager::SyncInternal::EncryptDataTypes( | 2080 void SyncManager::SyncInternal::EncryptDataTypes( |
2081 const syncable::ModelTypeSet& encrypted_types) { | 2081 const syncable::ModelTypeSet& encrypted_types) { |
2082 VLOG(1) << "Attempting to encrypt datatypes " | 2082 VLOG(1) << "Attempting to encrypt datatypes " |
2083 << syncable::ModelTypeSetToString(encrypted_types); | 2083 << syncable::ModelTypeSetToString(encrypted_types); |
2084 | 2084 |
2085 WriteTransaction trans(GetUserShare()); | 2085 WriteTransaction trans(GetUserShare()); |
2086 WriteNode node(&trans); | 2086 WriteNode node(&trans); |
2087 if (!node.InitByTagLookup(kNigoriTag)) { | 2087 if (!node.InitByTagLookup(kNigoriTag)) { |
2088 LOG(ERROR) << "Unable to set encrypted datatypes because Nigori node not " | 2088 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not " |
2089 << "found."; | 2089 << "found."; |
2090 NOTREACHED(); | |
2091 return; | 2090 return; |
2092 } | 2091 } |
2093 | 2092 |
2094 Cryptographer* cryptographer = trans.GetCryptographer(); | 2093 Cryptographer* cryptographer = trans.GetCryptographer(); |
2095 | 2094 |
2095 if (!cryptographer->is_ready()) { | |
2096 NOTREACHED() << "Attempting to encrypt datatypes when cryptographer not " | |
2097 << "ready."; | |
2098 return; | |
2099 } | |
2100 | |
2096 // Update the Nigori node set of encrypted datatypes so other machines notice. | 2101 // Update the Nigori node set of encrypted datatypes so other machines notice. |
2097 // Note, we merge the current encrypted types with those requested. Once a | 2102 // Note, we merge the current encrypted types with those requested. Once a |
2098 // datatypes is marked as needing encryption, it is never unmarked. | 2103 // datatypes is marked as needing encryption, it is never unmarked. |
2099 sync_pb::NigoriSpecifics nigori; | 2104 sync_pb::NigoriSpecifics nigori; |
2100 nigori.CopyFrom(node.GetNigoriSpecifics()); | 2105 nigori.CopyFrom(node.GetNigoriSpecifics()); |
2101 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans); | 2106 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans); |
2102 syncable::ModelTypeSet newly_encrypted_types; | 2107 syncable::ModelTypeSet newly_encrypted_types; |
2103 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(), | 2108 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(), |
2104 encrypted_types.begin(), encrypted_types.end(), | 2109 encrypted_types.begin(), encrypted_types.end(), |
2105 std::inserter(newly_encrypted_types, | 2110 std::inserter(newly_encrypted_types, |
(...skipping 877 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2983 } | 2988 } |
2984 BaseTransaction::~BaseTransaction() { | 2989 BaseTransaction::~BaseTransaction() { |
2985 delete lookup_; | 2990 delete lookup_; |
2986 } | 2991 } |
2987 | 2992 |
2988 UserShare* SyncManager::GetUserShare() const { | 2993 UserShare* SyncManager::GetUserShare() const { |
2989 DCHECK(data_->initialized()) << "GetUserShare requires initialization!"; | 2994 DCHECK(data_->initialized()) << "GetUserShare requires initialization!"; |
2990 return data_->GetUserShare(); | 2995 return data_->GetUserShare(); |
2991 } | 2996 } |
2992 | 2997 |
2998 void SyncManager::ReloadNigori() { | |
2999 DCHECK(data_->initialized()); | |
3000 if (data_->UpdateCryptographerFromNigori()) | |
tim (not reviewing)
2011/06/14 01:32:58
is it possible for is_ready() to change from true
Nicolas Zea
2011/06/14 16:52:38
You're right, once we release the transaction it's
| |
3001 data_->EncryptDataTypes(syncable::ModelTypeSet()); | |
3002 } | |
3003 | |
2993 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const { | 3004 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const { |
2994 sync_api::ReadTransaction trans(GetUserShare()); | 3005 sync_api::ReadTransaction trans(GetUserShare()); |
2995 return GetEncryptedTypes(&trans); | 3006 return GetEncryptedTypes(&trans); |
2996 } | 3007 } |
2997 | 3008 |
2998 | |
2999 bool SyncManager::HasUnsyncedItems() const { | 3009 bool SyncManager::HasUnsyncedItems() const { |
3000 sync_api::ReadTransaction trans(GetUserShare()); | 3010 sync_api::ReadTransaction trans(GetUserShare()); |
3001 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); | 3011 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); |
3002 } | 3012 } |
3003 | 3013 |
3004 void SyncManager::LogUnsyncedItems(int level) const { | 3014 void SyncManager::LogUnsyncedItems(int level) const { |
3005 std::vector<int64> unsynced_handles; | 3015 std::vector<int64> unsynced_handles; |
3006 sync_api::ReadTransaction trans(GetUserShare()); | 3016 sync_api::ReadTransaction trans(GetUserShare()); |
3007 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles( | 3017 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles( |
3008 trans.GetWrappedTrans(), &unsynced_handles); | 3018 trans.GetWrappedTrans(), &unsynced_handles); |
(...skipping 18 matching lines...) Expand all Loading... | |
3027 void SyncManager::TriggerOnIncomingNotificationForTest( | 3037 void SyncManager::TriggerOnIncomingNotificationForTest( |
3028 const syncable::ModelTypeBitSet& model_types) { | 3038 const syncable::ModelTypeBitSet& model_types) { |
3029 syncable::ModelTypePayloadMap model_types_with_payloads = | 3039 syncable::ModelTypePayloadMap model_types_with_payloads = |
3030 syncable::ModelTypePayloadMapFromBitSet(model_types, | 3040 syncable::ModelTypePayloadMapFromBitSet(model_types, |
3031 std::string()); | 3041 std::string()); |
3032 | 3042 |
3033 data_->OnIncomingNotification(model_types_with_payloads); | 3043 data_->OnIncomingNotification(model_types_with_payloads); |
3034 } | 3044 } |
3035 | 3045 |
3036 } // namespace sync_api | 3046 } // namespace sync_api |
OLD | NEW |