| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "sync/internal_api/public/write_node.h" | 5 #include "sync/internal_api/public/write_node.h" |
| 6 | 6 |
| 7 #include "base/utf_string_conversions.h" | 7 #include "base/utf_string_conversions.h" |
| 8 #include "base/values.h" | 8 #include "base/values.h" |
| 9 #include "sync/internal_api/public/base_transaction.h" | 9 #include "sync/internal_api/public/base_transaction.h" |
| 10 #include "sync/internal_api/public/write_transaction.h" | 10 #include "sync/internal_api/public/write_transaction.h" |
| 11 #include "sync/internal_api/syncapi_internal.h" | 11 #include "sync/internal_api/syncapi_internal.h" |
| 12 #include "sync/protocol/app_specifics.pb.h" | 12 #include "sync/protocol/app_specifics.pb.h" |
| 13 #include "sync/protocol/autofill_specifics.pb.h" | 13 #include "sync/protocol/autofill_specifics.pb.h" |
| 14 #include "sync/protocol/bookmark_specifics.pb.h" | 14 #include "sync/protocol/bookmark_specifics.pb.h" |
| 15 #include "sync/protocol/extension_specifics.pb.h" | 15 #include "sync/protocol/extension_specifics.pb.h" |
| 16 #include "sync/protocol/password_specifics.pb.h" | 16 #include "sync/protocol/password_specifics.pb.h" |
| 17 #include "sync/protocol/session_specifics.pb.h" | 17 #include "sync/protocol/session_specifics.pb.h" |
| 18 #include "sync/protocol/theme_specifics.pb.h" | 18 #include "sync/protocol/theme_specifics.pb.h" |
| 19 #include "sync/protocol/typed_url_specifics.pb.h" | 19 #include "sync/protocol/typed_url_specifics.pb.h" |
| 20 #include "sync/syncable/mutable_entry.h" | 20 #include "sync/syncable/mutable_entry.h" |
| 21 #include "sync/syncable/nigori_util.h" | 21 #include "sync/syncable/nigori_util.h" |
| 22 #include "sync/syncable/syncable_util.h" |
| 22 #include "sync/util/cryptographer.h" | 23 #include "sync/util/cryptographer.h" |
| 23 | 24 |
| 24 using std::string; | 25 using std::string; |
| 25 using std::vector; | 26 using std::vector; |
| 26 | 27 |
| 27 namespace syncer { | 28 namespace syncer { |
| 28 | 29 |
| 29 using syncable::kEncryptedString; | 30 using syncable::kEncryptedString; |
| 30 using syncable::SPECIFICS; | 31 using syncable::SPECIFICS; |
| 31 | 32 |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 SetEntitySpecifics(entity_specifics); | 198 SetEntitySpecifics(entity_specifics); |
| 198 } | 199 } |
| 199 | 200 |
| 200 void WriteNode::SetEntitySpecifics( | 201 void WriteNode::SetEntitySpecifics( |
| 201 const sync_pb::EntitySpecifics& new_value) { | 202 const sync_pb::EntitySpecifics& new_value) { |
| 202 ModelType new_specifics_type = | 203 ModelType new_specifics_type = |
| 203 GetModelTypeFromSpecifics(new_value); | 204 GetModelTypeFromSpecifics(new_value); |
| 204 DCHECK_NE(new_specifics_type, UNSPECIFIED); | 205 DCHECK_NE(new_specifics_type, UNSPECIFIED); |
| 205 DVLOG(1) << "Writing entity specifics of type " | 206 DVLOG(1) << "Writing entity specifics of type " |
| 206 << ModelTypeToString(new_specifics_type); | 207 << ModelTypeToString(new_specifics_type); |
| 207 // GetModelType() can be unspecified if this is the first time this | 208 DCHECK_EQ(new_specifics_type, GetModelType()); |
| 208 // node is being initialized (see PutModelType()). Otherwise, it | |
| 209 // should match |new_specifics_type|. | |
| 210 if (GetModelType() != UNSPECIFIED) { | |
| 211 DCHECK_EQ(new_specifics_type, GetModelType()); | |
| 212 } | |
| 213 | 209 |
| 214 // Preserve unknown fields. | 210 // Preserve unknown fields. |
| 215 const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS); | 211 const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS); |
| 216 sync_pb::EntitySpecifics new_specifics; | 212 sync_pb::EntitySpecifics new_specifics; |
| 217 new_specifics.CopyFrom(new_value); | 213 new_specifics.CopyFrom(new_value); |
| 218 new_specifics.mutable_unknown_fields()->MergeFrom( | 214 new_specifics.mutable_unknown_fields()->MergeFrom( |
| 219 old_specifics.unknown_fields()); | 215 old_specifics.unknown_fields()); |
| 220 | 216 |
| 221 // Will update the entry if encryption was necessary. | 217 // Will update the entry if encryption was necessary. |
| 222 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(), | 218 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(), |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 // Find a node by client tag, and bind this WriteNode to it. | 281 // Find a node by client tag, and bind this WriteNode to it. |
| 286 // Return true if the write node was found, and was not deleted. | 282 // Return true if the write node was found, and was not deleted. |
| 287 // Undeleting a deleted node is possible by ClientTag. | 283 // Undeleting a deleted node is possible by ClientTag. |
| 288 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup( | 284 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup( |
| 289 ModelType model_type, | 285 ModelType model_type, |
| 290 const std::string& tag) { | 286 const std::string& tag) { |
| 291 DCHECK(!entry_) << "Init called twice"; | 287 DCHECK(!entry_) << "Init called twice"; |
| 292 if (tag.empty()) | 288 if (tag.empty()) |
| 293 return INIT_FAILED_PRECONDITION; | 289 return INIT_FAILED_PRECONDITION; |
| 294 | 290 |
| 295 const std::string hash = GenerateSyncableHash(model_type, tag); | 291 const std::string hash = syncable::GenerateSyncableHash(model_type, tag); |
| 296 | 292 |
| 297 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), | 293 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), |
| 298 syncable::GET_BY_CLIENT_TAG, hash); | 294 syncable::GET_BY_CLIENT_TAG, hash); |
| 299 if (!entry_->good()) | 295 if (!entry_->good()) |
| 300 return INIT_FAILED_ENTRY_NOT_GOOD; | 296 return INIT_FAILED_ENTRY_NOT_GOOD; |
| 301 if (entry_->Get(syncable::IS_DEL)) | 297 if (entry_->Get(syncable::IS_DEL)) |
| 302 return INIT_FAILED_ENTRY_IS_DEL; | 298 return INIT_FAILED_ENTRY_IS_DEL; |
| 303 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY; | 299 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY; |
| 304 } | 300 } |
| 305 | 301 |
| 306 BaseNode::InitByLookupResult WriteNode::InitByTagLookup( | 302 BaseNode::InitByLookupResult WriteNode::InitByTagLookup( |
| 307 const std::string& tag) { | 303 const std::string& tag) { |
| 308 DCHECK(!entry_) << "Init called twice"; | 304 DCHECK(!entry_) << "Init called twice"; |
| 309 if (tag.empty()) | 305 if (tag.empty()) |
| 310 return INIT_FAILED_PRECONDITION; | 306 return INIT_FAILED_PRECONDITION; |
| 311 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), | 307 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), |
| 312 syncable::GET_BY_SERVER_TAG, tag); | 308 syncable::GET_BY_SERVER_TAG, tag); |
| 313 if (!entry_->good()) | 309 if (!entry_->good()) |
| 314 return INIT_FAILED_ENTRY_NOT_GOOD; | 310 return INIT_FAILED_ENTRY_NOT_GOOD; |
| 315 if (entry_->Get(syncable::IS_DEL)) | 311 if (entry_->Get(syncable::IS_DEL)) |
| 316 return INIT_FAILED_ENTRY_IS_DEL; | 312 return INIT_FAILED_ENTRY_IS_DEL; |
| 317 ModelType model_type = GetModelType(); | 313 ModelType model_type = GetModelType(); |
| 318 DCHECK_EQ(model_type, NIGORI); | 314 DCHECK_EQ(model_type, NIGORI); |
| 319 return INIT_OK; | 315 return INIT_OK; |
| 320 } | 316 } |
| 321 | 317 |
| 322 void WriteNode::PutModelType(ModelType model_type) { | |
| 323 // Set an empty specifics of the appropriate datatype. The presence | |
| 324 // of the specific field will identify the model type. | |
| 325 DCHECK(GetModelType() == model_type || | |
| 326 GetModelType() == UNSPECIFIED); // Immutable once set. | |
| 327 | |
| 328 sync_pb::EntitySpecifics specifics; | |
| 329 AddDefaultFieldValue(model_type, &specifics); | |
| 330 SetEntitySpecifics(specifics); | |
| 331 } | |
| 332 | |
| 333 // Create a new node with default properties, and bind this WriteNode to it. | 318 // Create a new node with default properties, and bind this WriteNode to it. |
| 334 // Return true on success. | 319 // Return true on success. |
| 335 bool WriteNode::InitByCreation(ModelType model_type, | 320 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent, |
| 336 const BaseNode& parent, | 321 const BaseNode* predecessor) { |
| 337 const BaseNode* predecessor) { | |
| 338 DCHECK(!entry_) << "Init called twice"; | 322 DCHECK(!entry_) << "Init called twice"; |
| 339 // |predecessor| must be a child of |parent| or NULL. | 323 // |predecessor| must be a child of |parent| or NULL. |
| 340 if (predecessor && predecessor->GetParentId() != parent.GetId()) { | 324 if (predecessor && predecessor->GetParentId() != parent.GetId()) { |
| 341 DCHECK(false); | 325 DCHECK(false); |
| 342 return false; | 326 return false; |
| 343 } | 327 } |
| 344 | 328 |
| 345 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); | 329 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); |
| 346 | 330 |
| 347 // Start out with a dummy name. We expect | 331 // Start out with a dummy name. We expect |
| 348 // the caller to set a meaningful name after creation. | 332 // the caller to set a meaningful name after creation. |
| 349 string dummy(kDefaultNameForNewNodes); | 333 string dummy(kDefaultNameForNewNodes); |
| 350 | 334 |
| 351 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), | 335 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), |
| 352 syncable::CREATE, parent_id, dummy); | 336 syncable::CREATE_BOOKMARK, parent_id, |
| 337 dummy); |
| 353 | 338 |
| 354 if (!entry_->good()) | 339 if (!entry_->good()) |
| 355 return false; | 340 return false; |
| 356 | 341 |
| 357 // Entries are untitled folders by default. | 342 // Entries are untitled folders by default. |
| 358 entry_->Put(syncable::IS_DIR, true); | 343 entry_->Put(syncable::IS_DIR, true); |
| 359 | 344 |
| 360 PutModelType(model_type); | |
| 361 | |
| 362 // Now set the predecessor, which sets IS_UNSYNCED as necessary. | 345 // Now set the predecessor, which sets IS_UNSYNCED as necessary. |
| 363 return PutPredecessor(predecessor); | 346 return PutPredecessor(predecessor); |
| 364 } | 347 } |
| 365 | 348 |
| 366 // Create a new node with default properties and a client defined unique tag, | 349 // Create a new node with default properties and a client defined unique tag, |
| 367 // and bind this WriteNode to it. | 350 // and bind this WriteNode to it. |
| 368 // Return true on success. If the tag exists in the database, then | 351 // Return true on success. If the tag exists in the database, then |
| 369 // we will attempt to undelete the node. | 352 // we will attempt to undelete the node. |
| 370 // TODO(chron): Code datatype into hash tag. | 353 // TODO(chron): Code datatype into hash tag. |
| 371 // TODO(chron): Is model type ever lost? | 354 // TODO(chron): Is model type ever lost? |
| 372 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation( | 355 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation( |
| 373 ModelType model_type, | 356 ModelType model_type, |
| 374 const BaseNode& parent, | 357 const BaseNode& parent, |
| 375 const std::string& tag) { | 358 const std::string& tag) { |
| 376 // This DCHECK will only fail if init is called twice. | 359 // This DCHECK will only fail if init is called twice. |
| 377 DCHECK(!entry_); | 360 DCHECK(!entry_); |
| 378 if (tag.empty()) { | 361 if (tag.empty()) { |
| 379 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag."; | 362 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag."; |
| 380 return INIT_FAILED_EMPTY_TAG; | 363 return INIT_FAILED_EMPTY_TAG; |
| 381 } | 364 } |
| 382 | 365 |
| 383 const std::string hash = GenerateSyncableHash(model_type, tag); | 366 const std::string hash = syncable::GenerateSyncableHash(model_type, tag); |
| 384 | 367 |
| 385 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); | 368 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID); |
| 386 | 369 |
| 387 // Start out with a dummy name. We expect | 370 // Start out with a dummy name. We expect |
| 388 // the caller to set a meaningful name after creation. | 371 // the caller to set a meaningful name after creation. |
| 389 string dummy(kDefaultNameForNewNodes); | 372 string dummy(kDefaultNameForNewNodes); |
| 390 | 373 |
| 391 // Check if we have this locally and need to undelete it. | 374 // Check if we have this locally and need to undelete it. |
| 392 scoped_ptr<syncable::MutableEntry> existing_entry( | 375 scoped_ptr<syncable::MutableEntry> existing_entry( |
| 393 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), | 376 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), |
| (...skipping 25 matching lines...) Expand all Loading... |
| 419 // tags and updates. | 402 // tags and updates. |
| 420 | 403 |
| 421 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy); | 404 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy); |
| 422 existing_entry->Put(syncable::PARENT_ID, parent_id); | 405 existing_entry->Put(syncable::PARENT_ID, parent_id); |
| 423 entry_ = existing_entry.release(); | 406 entry_ = existing_entry.release(); |
| 424 } else { | 407 } else { |
| 425 return INIT_FAILED_ENTRY_ALREADY_EXISTS; | 408 return INIT_FAILED_ENTRY_ALREADY_EXISTS; |
| 426 } | 409 } |
| 427 } else { | 410 } else { |
| 428 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), | 411 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(), |
| 429 syncable::CREATE, parent_id, dummy); | 412 syncable::CREATE_UNIQUE, |
| 413 model_type, parent_id, dummy); |
| 430 if (!entry_->good()) | 414 if (!entry_->good()) |
| 431 return INIT_FAILED_COULD_NOT_CREATE_ENTRY; | 415 return INIT_FAILED_COULD_NOT_CREATE_ENTRY; |
| 432 | 416 |
| 433 // Only set IS_DIR for new entries. Don't bitflip undeleted ones. | 417 // Only set IS_DIR for new entries. Don't bitflip undeleted ones. |
| 434 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash); | 418 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash); |
| 435 } | 419 } |
| 436 | 420 |
| 437 // We don't support directory and tag combinations. | 421 // We don't support directory and tag combinations. |
| 438 entry_->Put(syncable::IS_DIR, false); | 422 entry_->Put(syncable::IS_DIR, false); |
| 439 | 423 |
| 440 // Will clear specifics data. | |
| 441 PutModelType(model_type); | |
| 442 | |
| 443 // Now set the predecessor, which sets IS_UNSYNCED as necessary. | 424 // Now set the predecessor, which sets IS_UNSYNCED as necessary. |
| 444 bool success = PutPredecessor(NULL); | 425 bool success = PutPredecessor(NULL); |
| 445 if (!success) | 426 if (!success) |
| 446 return INIT_FAILED_SET_PREDECESSOR; | 427 return INIT_FAILED_SET_PREDECESSOR; |
| 447 | 428 |
| 448 return INIT_SUCCESS; | 429 return INIT_SUCCESS; |
| 449 } | 430 } |
| 450 | 431 |
| 451 bool WriteNode::SetPosition(const BaseNode& new_parent, | 432 bool WriteNode::SetPosition(const BaseNode& new_parent, |
| 452 const BaseNode* predecessor) { | 433 const BaseNode* predecessor) { |
| 453 // |predecessor| must be a child of |new_parent| or NULL. | 434 // |predecessor| must be a child of |new_parent| or NULL. |
| 454 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) { | 435 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) { |
| 455 DCHECK(false); | 436 DCHECK(false); |
| 456 return false; | 437 return false; |
| 457 } | 438 } |
| 458 | 439 |
| 459 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID); | 440 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID); |
| 460 | 441 |
| 461 // Filter out redundant changes if both the parent and the predecessor match. | 442 // Filter out redundant changes if both the parent and the predecessor match. |
| 462 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) { | 443 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) { |
| 463 const syncable::Id& old = entry_->Get(syncable::PREV_ID); | 444 const syncable::Id& old = entry_->GetPredecessorId(); |
| 464 if ((!predecessor && old.IsRoot()) || | 445 if ((!predecessor && old.IsRoot()) || |
| 465 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) { | 446 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) { |
| 466 return true; | 447 return true; |
| 467 } | 448 } |
| 468 } | 449 } |
| 469 | 450 |
| 470 // Atomically change the parent. This will fail if it would | 451 // Atomically change the parent. This will fail if it would |
| 471 // introduce a cycle in the hierarchy. | 452 // introduce a cycle in the hierarchy. |
| 472 if (!entry_->Put(syncable::PARENT_ID, new_parent_id)) | 453 if (!entry_->Put(syncable::PARENT_ID, new_parent_id)) |
| 473 return false; | 454 return false; |
| 474 | 455 |
| 475 // Now set the predecessor, which sets IS_UNSYNCED as necessary. | 456 // Now set the predecessor, which sets IS_UNSYNCED as necessary. |
| 476 return PutPredecessor(predecessor); | 457 return PutPredecessor(predecessor); |
| 458 return true; |
| 477 } | 459 } |
| 478 | 460 |
| 479 const syncable::Entry* WriteNode::GetEntry() const { | 461 const syncable::Entry* WriteNode::GetEntry() const { |
| 480 return entry_; | 462 return entry_; |
| 481 } | 463 } |
| 482 | 464 |
| 483 const BaseTransaction* WriteNode::GetTransaction() const { | 465 const BaseTransaction* WriteNode::GetTransaction() const { |
| 484 return transaction_; | 466 return transaction_; |
| 485 } | 467 } |
| 486 | 468 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 505 MarkForSyncing(); | 487 MarkForSyncing(); |
| 506 | 488 |
| 507 return true; | 489 return true; |
| 508 } | 490 } |
| 509 | 491 |
| 510 void WriteNode::MarkForSyncing() { | 492 void WriteNode::MarkForSyncing() { |
| 511 syncable::MarkForSyncing(entry_); | 493 syncable::MarkForSyncing(entry_); |
| 512 } | 494 } |
| 513 | 495 |
| 514 } // namespace syncer | 496 } // namespace syncer |
| OLD | NEW |