OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "components/sync_driver/generic_change_processor.h" | 5 #include "components/sync_driver/generic_change_processor.h" |
6 | 6 |
7 #include "base/location.h" | 7 #include "base/location.h" |
8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
10 #include "sync/api/sync_change.h" | 10 #include "sync/api/sync_change.h" |
(...skipping 18 matching lines...) Expand all Loading... |
29 syncer::WriteNode* write_node) { | 29 syncer::WriteNode* write_node) { |
30 if (syncer::GetModelTypeFromSpecifics(entity_specifics) == | 30 if (syncer::GetModelTypeFromSpecifics(entity_specifics) == |
31 syncer::PASSWORDS) { | 31 syncer::PASSWORDS) { |
32 write_node->SetPasswordSpecifics( | 32 write_node->SetPasswordSpecifics( |
33 entity_specifics.password().client_only_encrypted_data()); | 33 entity_specifics.password().client_only_encrypted_data()); |
34 } else { | 34 } else { |
35 write_node->SetEntitySpecifics(entity_specifics); | 35 write_node->SetEntitySpecifics(entity_specifics); |
36 } | 36 } |
37 } | 37 } |
38 | 38 |
39 // Helper function to convert AttachmentId to AttachmentMetadataRecord. | |
40 sync_pb::AttachmentMetadataRecord AttachmentIdToRecord( | |
41 const syncer::AttachmentId& attachment_id) { | |
42 sync_pb::AttachmentMetadataRecord record; | |
43 *record.mutable_id() = attachment_id.GetProto(); | |
44 return record; | |
45 } | |
46 | |
47 // Replace |write_nodes|'s attachment ids with |attachment_ids|. | |
48 void SetAttachmentMetadata(const syncer::AttachmentIdList& attachment_ids, | |
49 syncer::WriteNode* write_node) { | |
50 DCHECK(write_node); | |
51 sync_pb::AttachmentMetadata attachment_metadata; | |
52 std::transform( | |
53 attachment_ids.begin(), | |
54 attachment_ids.end(), | |
55 RepeatedFieldBackInserter(attachment_metadata.mutable_record()), | |
56 AttachmentIdToRecord); | |
57 write_node->SetAttachmentMetadata(attachment_metadata); | |
58 } | |
59 | |
60 syncer::SyncData BuildRemoteSyncData( | 39 syncer::SyncData BuildRemoteSyncData( |
61 int64 sync_id, | 40 int64 sync_id, |
62 const syncer::BaseNode& read_node, | 41 const syncer::BaseNode& read_node, |
63 const syncer::AttachmentServiceProxy& attachment_service_proxy) { | 42 const syncer::AttachmentServiceProxy& attachment_service_proxy) { |
64 const syncer::AttachmentIdList& attachment_ids = read_node.GetAttachmentIds(); | 43 const syncer::AttachmentIdList& attachment_ids = read_node.GetAttachmentIds(); |
65 // Use the specifics of non-password datatypes directly (encryption has | 44 // Use the specifics of non-password datatypes directly (encryption has |
66 // already been handled). | 45 // already been handled). |
67 if (read_node.GetModelType() != syncer::PASSWORDS) { | 46 if (read_node.GetModelType() != syncer::PASSWORDS) { |
68 return syncer::SyncData::CreateRemoteData(sync_id, | 47 return syncer::SyncData::CreateRemoteData(sync_id, |
69 read_node.GetEntitySpecifics(), | 48 read_node.GetEntitySpecifics(), |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 // Should have listed all the possible error cases above. | 312 // Should have listed all the possible error cases above. |
334 error.Reset(from_here, error_prefix + "unknown error", type); | 313 error.Reset(from_here, error_prefix + "unknown error", type); |
335 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 314 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
336 error.message()); | 315 error.message()); |
337 LOG(ERROR) << "Delete: Unknown error."; | 316 LOG(ERROR) << "Delete: Unknown error."; |
338 return error; | 317 return error; |
339 } | 318 } |
340 } | 319 } |
341 } | 320 } |
342 | 321 |
343 syncer::SyncError AttemptDelete(const syncer::SyncChange& change, | 322 syncer::SyncError AttemptDelete( |
344 syncer::ModelType type, | 323 const syncer::SyncChange& change, |
345 const std::string& type_str, | 324 syncer::ModelType type, |
346 syncer::WriteNode* node, | 325 const std::string& type_str, |
347 DataTypeErrorHandler* error_handler) { | 326 syncer::WriteNode* node, |
| 327 DataTypeErrorHandler* error_handler) { |
348 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE); | 328 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE); |
349 if (change.sync_data().IsLocal()) { | 329 if (change.sync_data().IsLocal()) { |
350 const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag(); | 330 const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag(); |
351 if (tag.empty()) { | 331 if (tag.empty()) { |
352 syncer::SyncError error( | 332 syncer::SyncError error( |
353 FROM_HERE, | 333 FROM_HERE, |
354 syncer::SyncError::DATATYPE_ERROR, | 334 syncer::SyncError::DATATYPE_ERROR, |
355 "Failed to delete " + type_str + " node. Local data, empty tag. " + | 335 "Failed to delete " + type_str + " node. Local data, empty tag. " + |
356 change.location().ToString(), | 336 change.location().ToString(), |
357 type); | 337 type); |
(...skipping 23 matching lines...) Expand all Loading... |
381 type, error_handler); | 361 type, error_handler); |
382 } | 362 } |
383 } | 363 } |
384 if (IsActOnceDataType(type)) | 364 if (IsActOnceDataType(type)) |
385 node->Drop(); | 365 node->Drop(); |
386 else | 366 else |
387 node->Tombstone(); | 367 node->Tombstone(); |
388 return syncer::SyncError(); | 368 return syncer::SyncError(); |
389 } | 369 } |
390 | 370 |
391 // A callback invoked on completion of AttachmentService::StoreAttachment. | |
392 void IgnoreStoreResult(const syncer::AttachmentService::StoreResult&) { | |
393 // TODO(maniscalco): Here is where we're going to update the in-directory | |
394 // entry to indicate that the attachments have been successfully stored on | |
395 // disk. Why do we care? Because we might crash after persisting the | |
396 // directory to disk, but before we have persisted its attachments, leaving us | |
397 // with danging attachment ids. Having a flag that indicates we've stored the | |
398 // entry will allow us to detect and filter entries with dangling attachment | |
399 // ids (bug 368353). | |
400 } | |
401 | |
402 void StoreAttachments(syncer::AttachmentService* attachment_service, | |
403 const syncer::AttachmentList& attachments) { | |
404 DCHECK(attachment_service); | |
405 syncer::AttachmentService::StoreCallback ignore_store_result = | |
406 base::Bind(&IgnoreStoreResult); | |
407 attachment_service->StoreAttachments(attachments, ignore_store_result); | |
408 } | |
409 | |
410 } // namespace | 371 } // namespace |
411 | 372 |
412 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( | 373 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( |
413 const tracked_objects::Location& from_here, | 374 const tracked_objects::Location& from_here, |
414 const syncer::SyncChangeList& list_of_changes) { | 375 const syncer::SyncChangeList& list_of_changes) { |
415 DCHECK(CalledOnValidThread()); | 376 DCHECK(CalledOnValidThread()); |
416 | |
417 // Keep track of brand new attachments so we can persist them on this device | |
418 // and upload them to the server. | |
419 syncer::AttachmentList new_attachments; | |
420 | |
421 syncer::WriteTransaction trans(from_here, share_handle()); | 377 syncer::WriteTransaction trans(from_here, share_handle()); |
422 | 378 |
423 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin(); | 379 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin(); |
424 iter != list_of_changes.end(); | 380 iter != list_of_changes.end(); |
425 ++iter) { | 381 ++iter) { |
426 const syncer::SyncChange& change = *iter; | 382 const syncer::SyncChange& change = *iter; |
427 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED); | 383 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED); |
428 syncer::ModelType type = change.sync_data().GetDataType(); | 384 syncer::ModelType type = change.sync_data().GetDataType(); |
429 std::string type_str = syncer::ModelTypeToString(type); | 385 std::string type_str = syncer::ModelTypeToString(type); |
430 syncer::WriteNode sync_node(&trans); | 386 syncer::WriteNode sync_node(&trans); |
431 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) { | 387 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) { |
432 syncer::SyncError error = | 388 syncer::SyncError error = |
433 AttemptDelete(change, type, type_str, &sync_node, error_handler()); | 389 AttemptDelete(change, type, type_str, &sync_node, error_handler()); |
434 if (error.IsSet()) { | 390 if (error.IsSet()) { |
435 NOTREACHED(); | 391 NOTREACHED(); |
436 return error; | 392 return error; |
437 } | 393 } |
| 394 attachment_service_->OnSyncDataDelete(change.sync_data()); |
438 if (merge_result_.get()) { | 395 if (merge_result_.get()) { |
439 merge_result_->set_num_items_deleted( | 396 merge_result_->set_num_items_deleted( |
440 merge_result_->num_items_deleted() + 1); | 397 merge_result_->num_items_deleted() + 1); |
441 } | 398 } |
442 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) { | 399 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) { |
443 syncer::SyncError error = HandleActionAdd( | 400 syncer::SyncError error = |
444 change, type_str, type, trans, &sync_node, &new_attachments); | 401 HandleActionAdd(change, type_str, type, trans, &sync_node); |
445 if (error.IsSet()) { | 402 if (error.IsSet()) { |
446 return error; | 403 return error; |
447 } | 404 } |
448 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) { | 405 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) { |
449 syncer::SyncError error = HandleActionUpdate( | 406 syncer::SyncError error = |
450 change, type_str, type, trans, &sync_node, &new_attachments); | 407 HandleActionUpdate(change, type_str, type, trans, &sync_node); |
451 if (error.IsSet()) { | 408 if (error.IsSet()) { |
452 return error; | 409 return error; |
453 } | 410 } |
454 } else { | 411 } else { |
455 syncer::SyncError error( | 412 syncer::SyncError error( |
456 FROM_HERE, | 413 FROM_HERE, |
457 syncer::SyncError::DATATYPE_ERROR, | 414 syncer::SyncError::DATATYPE_ERROR, |
458 "Received unset SyncChange in the change processor, " + | 415 "Received unset SyncChange in the change processor, " + |
459 change.location().ToString(), | 416 change.location().ToString(), |
460 type); | 417 type); |
461 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 418 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
462 error.message()); | 419 error.message()); |
463 NOTREACHED(); | 420 NOTREACHED(); |
464 LOG(ERROR) << "Unset sync change."; | 421 LOG(ERROR) << "Unset sync change."; |
465 return error; | 422 return error; |
466 } | 423 } |
467 } | 424 } |
468 | |
469 if (!new_attachments.empty()) { | |
470 StoreAttachments(attachment_service_.get(), new_attachments); | |
471 } | |
472 | |
473 return syncer::SyncError(); | 425 return syncer::SyncError(); |
474 } | 426 } |
475 | 427 |
476 // WARNING: this code is sensitive to compiler optimizations. Be careful | 428 // WARNING: this code is sensitive to compiler optimizations. Be careful |
477 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else | 429 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else |
478 // the compiler attempts to merge it with other calls, losing useful information | 430 // the compiler attempts to merge it with other calls, losing useful information |
479 // in breakpad uploads. | 431 // in breakpad uploads. |
480 syncer::SyncError GenericChangeProcessor::HandleActionAdd( | 432 syncer::SyncError GenericChangeProcessor::HandleActionAdd( |
481 const syncer::SyncChange& change, | 433 const syncer::SyncChange& change, |
482 const std::string& type_str, | 434 const std::string& type_str, |
483 const syncer::ModelType& type, | 435 const syncer::ModelType& type, |
484 const syncer::WriteTransaction& trans, | 436 const syncer::WriteTransaction& trans, |
485 syncer::WriteNode* sync_node, | 437 syncer::WriteNode* sync_node) { |
486 syncer::AttachmentList* new_attachments) { | |
487 // TODO(sync): Handle other types of creation (custom parents, folders, | 438 // TODO(sync): Handle other types of creation (custom parents, folders, |
488 // etc.). | 439 // etc.). |
489 syncer::ReadNode root_node(&trans); | 440 syncer::ReadNode root_node(&trans); |
490 const syncer::SyncDataLocal sync_data_local(change.sync_data()); | |
491 if (root_node.InitByTagLookup(syncer::ModelTypeToRootTag( | 441 if (root_node.InitByTagLookup(syncer::ModelTypeToRootTag( |
492 sync_data_local.GetDataType())) != syncer::BaseNode::INIT_OK) { | 442 change.sync_data().GetDataType())) != syncer::BaseNode::INIT_OK) { |
493 syncer::SyncError error(FROM_HERE, | 443 syncer::SyncError error(FROM_HERE, |
494 syncer::SyncError::DATATYPE_ERROR, | 444 syncer::SyncError::DATATYPE_ERROR, |
495 "Failed to look up root node for type " + type_str, | 445 "Failed to look up root node for type " + type_str, |
496 type); | 446 type); |
497 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 447 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
498 error.message()); | 448 error.message()); |
499 NOTREACHED(); | 449 NOTREACHED(); |
500 LOG(ERROR) << "Create: no root node."; | 450 LOG(ERROR) << "Create: no root node."; |
501 return error; | 451 return error; |
502 } | 452 } |
503 syncer::WriteNode::InitUniqueByCreationResult result = | 453 syncer::WriteNode::InitUniqueByCreationResult result = |
504 sync_node->InitUniqueByCreation( | 454 sync_node->InitUniqueByCreation( |
505 sync_data_local.GetDataType(), root_node, sync_data_local.GetTag()); | 455 change.sync_data().GetDataType(), |
| 456 root_node, |
| 457 syncer::SyncDataLocal(change.sync_data()).GetTag()); |
506 if (result != syncer::WriteNode::INIT_SUCCESS) { | 458 if (result != syncer::WriteNode::INIT_SUCCESS) { |
507 std::string error_prefix = "Failed to create " + type_str + " node: " + | 459 std::string error_prefix = "Failed to create " + type_str + " node: " + |
508 change.location().ToString() + ", "; | 460 change.location().ToString() + ", "; |
509 switch (result) { | 461 switch (result) { |
510 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: { | 462 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: { |
511 syncer::SyncError error; | 463 syncer::SyncError error; |
512 error.Reset(FROM_HERE, error_prefix + "empty tag", type); | 464 error.Reset(FROM_HERE, error_prefix + "empty tag", type); |
513 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 465 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
514 error.message()); | 466 error.message()); |
515 LOG(ERROR) << "Create: Empty tag."; | 467 LOG(ERROR) << "Create: Empty tag."; |
(...skipping 28 matching lines...) Expand all Loading... |
544 syncer::SyncError error; | 496 syncer::SyncError error; |
545 error.Reset(FROM_HERE, error_prefix + "unknown error", type); | 497 error.Reset(FROM_HERE, error_prefix + "unknown error", type); |
546 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 498 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
547 error.message()); | 499 error.message()); |
548 LOG(ERROR) << "Create: Unknown error."; | 500 LOG(ERROR) << "Create: Unknown error."; |
549 return error; | 501 return error; |
550 } | 502 } |
551 } | 503 } |
552 } | 504 } |
553 sync_node->SetTitle(change.sync_data().GetTitle()); | 505 sync_node->SetTitle(change.sync_data().GetTitle()); |
554 SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node); | 506 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node); |
555 | 507 attachment_service_->OnSyncDataAdd(change.sync_data()); |
556 syncer::AttachmentIdList attachment_ids = sync_data_local.GetAttachmentIds(); | |
557 SetAttachmentMetadata(attachment_ids, sync_node); | |
558 | |
559 // Return any newly added attachments. | |
560 const syncer::AttachmentList& local_attachments_for_upload = | |
561 sync_data_local.GetLocalAttachmentsForUpload(); | |
562 new_attachments->insert(new_attachments->end(), | |
563 local_attachments_for_upload.begin(), | |
564 local_attachments_for_upload.end()); | |
565 | |
566 if (merge_result_.get()) { | 508 if (merge_result_.get()) { |
567 merge_result_->set_num_items_added(merge_result_->num_items_added() + 1); | 509 merge_result_->set_num_items_added(merge_result_->num_items_added() + 1); |
568 } | 510 } |
569 return syncer::SyncError(); | 511 return syncer::SyncError(); |
570 } | 512 } |
571 // WARNING: this code is sensitive to compiler optimizations. Be careful | 513 // WARNING: this code is sensitive to compiler optimizations. Be careful |
572 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else | 514 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else |
573 // the compiler attempts to merge it with other calls, losing useful information | 515 // the compiler attempts to merge it with other calls, losing useful information |
574 // in breakpad uploads. | 516 // in breakpad uploads. |
575 syncer::SyncError GenericChangeProcessor::HandleActionUpdate( | 517 syncer::SyncError GenericChangeProcessor::HandleActionUpdate( |
576 const syncer::SyncChange& change, | 518 const syncer::SyncChange& change, |
577 const std::string& type_str, | 519 const std::string& type_str, |
578 const syncer::ModelType& type, | 520 const syncer::ModelType& type, |
579 const syncer::WriteTransaction& trans, | 521 const syncer::WriteTransaction& trans, |
580 syncer::WriteNode* sync_node, | 522 syncer::WriteNode* sync_node) { |
581 syncer::AttachmentList* new_attachments) { | |
582 // TODO(zea): consider having this logic for all possible changes? | 523 // TODO(zea): consider having this logic for all possible changes? |
583 | |
584 const syncer::SyncDataLocal sync_data_local(change.sync_data()); | |
585 syncer::BaseNode::InitByLookupResult result = | 524 syncer::BaseNode::InitByLookupResult result = |
586 sync_node->InitByClientTagLookup(sync_data_local.GetDataType(), | 525 sync_node->InitByClientTagLookup( |
587 sync_data_local.GetTag()); | 526 change.sync_data().GetDataType(), |
| 527 syncer::SyncDataLocal(change.sync_data()).GetTag()); |
588 if (result != syncer::BaseNode::INIT_OK) { | 528 if (result != syncer::BaseNode::INIT_OK) { |
589 std::string error_prefix = "Failed to load " + type_str + " node. " + | 529 std::string error_prefix = "Failed to load " + type_str + " node. " + |
590 change.location().ToString() + ", "; | 530 change.location().ToString() + ", "; |
591 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) { | 531 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) { |
592 syncer::SyncError error; | 532 syncer::SyncError error; |
593 error.Reset(FROM_HERE, error_prefix + "empty tag", type); | 533 error.Reset(FROM_HERE, error_prefix + "empty tag", type); |
594 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 534 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
595 error.message()); | 535 error.message()); |
596 LOG(ERROR) << "Update: Empty tag."; | 536 LOG(ERROR) << "Update: Empty tag."; |
597 return error; | 537 return error; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
659 type); | 599 type); |
660 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, | 600 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, |
661 error.message()); | 601 error.message()); |
662 LOG(ERROR) << "Update: encr case 4."; | 602 LOG(ERROR) << "Update: encr case 4."; |
663 return error; | 603 return error; |
664 } | 604 } |
665 } | 605 } |
666 } | 606 } |
667 | 607 |
668 sync_node->SetTitle(change.sync_data().GetTitle()); | 608 sync_node->SetTitle(change.sync_data().GetTitle()); |
669 SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node); | 609 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node); |
670 SetAttachmentMetadata(sync_data_local.GetAttachmentIds(), sync_node); | 610 attachment_service_->OnSyncDataUpdate(sync_node->GetAttachmentIds(), |
671 | 611 change.sync_data()); |
672 // Return any newly added attachments. | |
673 const syncer::AttachmentList& local_attachments_for_upload = | |
674 sync_data_local.GetLocalAttachmentsForUpload(); | |
675 new_attachments->insert(new_attachments->end(), | |
676 local_attachments_for_upload.begin(), | |
677 local_attachments_for_upload.end()); | |
678 | |
679 if (merge_result_.get()) { | 612 if (merge_result_.get()) { |
680 merge_result_->set_num_items_modified(merge_result_->num_items_modified() + | 613 merge_result_->set_num_items_modified(merge_result_->num_items_modified() + |
681 1); | 614 1); |
682 } | 615 } |
683 // TODO(sync): Support updating other parts of the sync node (title, | 616 // TODO(sync): Support updating other parts of the sync node (title, |
684 // successor, parent, etc.). | 617 // successor, parent, etc.). |
685 return syncer::SyncError(); | 618 return syncer::SyncError(); |
686 } | 619 } |
687 | 620 |
688 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes( | 621 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes( |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 void GenericChangeProcessor::StartImpl() { | 655 void GenericChangeProcessor::StartImpl() { |
723 DCHECK(CalledOnValidThread()); | 656 DCHECK(CalledOnValidThread()); |
724 } | 657 } |
725 | 658 |
726 syncer::UserShare* GenericChangeProcessor::share_handle() const { | 659 syncer::UserShare* GenericChangeProcessor::share_handle() const { |
727 DCHECK(CalledOnValidThread()); | 660 DCHECK(CalledOnValidThread()); |
728 return share_handle_; | 661 return share_handle_; |
729 } | 662 } |
730 | 663 |
731 } // namespace browser_sync | 664 } // namespace browser_sync |
OLD | NEW |