| 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_sessions/sessions_sync_manager.h" | 5 #include "components/sync_sessions/sessions_sync_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/metrics/field_trial.h" | 10 #include "base/metrics/field_trial.h" |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 | 36 |
| 37 // The maximum number of navigations in each direction we care to sync. | 37 // The maximum number of navigations in each direction we care to sync. |
| 38 const int kMaxSyncNavigationCount = 6; | 38 const int kMaxSyncNavigationCount = 6; |
| 39 | 39 |
| 40 // The URL at which the set of synced tabs is displayed. We treat it differently | 40 // The URL at which the set of synced tabs is displayed. We treat it differently |
| 41 // from all other URL's as accessing it triggers a sync refresh of Sessions. | 41 // from all other URL's as accessing it triggers a sync refresh of Sessions. |
| 42 const char kNTPOpenTabSyncURL[] = "chrome://newtab/#open_tabs"; | 42 const char kNTPOpenTabSyncURL[] = "chrome://newtab/#open_tabs"; |
| 43 | 43 |
| 44 // Default number of days without activity after which a session is considered | 44 // Default number of days without activity after which a session is considered |
| 45 // stale and becomes a candidate for garbage collection. | 45 // stale and becomes a candidate for garbage collection. |
| 46 const size_t kDefaultStaleSessionThresholdDays = 14; // 2 weeks. | 46 const int kDefaultStaleSessionThresholdDays = 14; // 2 weeks. |
| 47 | 47 |
| 48 // Comparator function for use with std::sort that will sort tabs by | 48 // Comparator function for use with std::sort that will sort tabs by |
| 49 // descending timestamp (i.e., most recent first). | 49 // descending timestamp (i.e., most recent first). |
| 50 bool TabsRecencyComparator(const sessions::SessionTab* t1, | 50 bool TabsRecencyComparator(const sessions::SessionTab* t1, |
| 51 const sessions::SessionTab* t2) { | 51 const sessions::SessionTab* t2) { |
| 52 return t1->timestamp > t2->timestamp; | 52 return t1->timestamp > t2->timestamp; |
| 53 } | 53 } |
| 54 | 54 |
| 55 // Comparator function for use with std::sort that will sort sessions by | 55 // Comparator function for use with std::sort that will sort sessions by |
| 56 // descending modified_time (i.e., most recent first). | 56 // descending modified_time (i.e., most recent first). |
| (...skipping 451 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 // to avoid any possible ping-pong delete/reassociate sequence, but | 508 // to avoid any possible ping-pong delete/reassociate sequence, but |
| 509 // remember that this happened as our TabNodePool is inconsistent. | 509 // remember that this happened as our TabNodePool is inconsistent. |
| 510 local_tab_pool_out_of_sync_ = true; | 510 local_tab_pool_out_of_sync_ = true; |
| 511 LOG(WARNING) << "Local session data deleted. Ignoring until next " | 511 LOG(WARNING) << "Local session data deleted. Ignoring until next " |
| 512 << "local navigation event."; | 512 << "local navigation event."; |
| 513 } else if (session.has_header()) { | 513 } else if (session.has_header()) { |
| 514 // Disassociate only when header node is deleted. For tab node | 514 // Disassociate only when header node is deleted. For tab node |
| 515 // deletions, the header node will be updated and foreign tab will | 515 // deletions, the header node will be updated and foreign tab will |
| 516 // get deleted. | 516 // get deleted. |
| 517 DisassociateForeignSession(session.session_tag()); | 517 DisassociateForeignSession(session.session_tag()); |
| 518 } else if (session.has_tab()) { |
| 519 // The challenge here is that we don't know if this tab deletion is |
| 520 // being processed before or after the parent was updated to no longer |
| 521 // references the tab. Or, even more extreme, the parent has been |
| 522 // deleted as well. Tell the tracker to do what it can. The header's |
| 523 // update will mostly get us into the correct state, the only thing |
| 524 // this deletion needs to accomplish is make sure we never tell sync |
| 525 // to delete this tab later during garbage collection. |
| 526 session_tracker_.DeleteForeignTab(session.session_tag(), |
| 527 session.tab_node_id()); |
| 518 } | 528 } |
| 519 break; | 529 break; |
| 520 case syncer::SyncChange::ACTION_ADD: | 530 case syncer::SyncChange::ACTION_ADD: |
| 521 case syncer::SyncChange::ACTION_UPDATE: | 531 case syncer::SyncChange::ACTION_UPDATE: |
| 522 if (current_machine_tag() == session.session_tag()) { | 532 if (current_machine_tag() == session.session_tag()) { |
| 523 // We should only ever receive a change to our own machine's session | 533 // We should only ever receive a change to our own machine's session |
| 524 // info if encryption was turned on. In that case, the data is still | 534 // info if encryption was turned on. In that case, the data is still |
| 525 // the same, so we can ignore. | 535 // the same, so we can ignore. |
| 526 // TODO(skym): Is it really safe to return here? Why not continue? | |
| 527 // Couldn't there be multiple SessionSpecifics in the SyncChangeList | |
| 528 // that contain different session tags? | |
| 529 LOG(WARNING) << "Dropping modification to local session."; | 536 LOG(WARNING) << "Dropping modification to local session."; |
| 530 return syncer::SyncError(); | 537 return syncer::SyncError(); |
| 531 } | 538 } |
| 532 UpdateTrackerWithForeignSession( | 539 UpdateTrackerWithForeignSession( |
| 533 session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime()); | 540 session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime()); |
| 534 break; | 541 break; |
| 535 default: | 542 default: |
| 536 NOTREACHED() << "Processing sync changes failed, unknown change type."; | 543 NOTREACHED() << "Processing sync changes failed, unknown change type."; |
| 537 } | 544 } |
| 538 } | 545 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 551 return syncer::SyncChange( | 558 return syncer::SyncChange( |
| 552 FROM_HERE, SyncChange::ACTION_DELETE, | 559 FROM_HERE, SyncChange::ACTION_DELETE, |
| 553 SyncData::CreateLocalDelete( | 560 SyncData::CreateLocalDelete( |
| 554 TabNodePool::TabIdToTag(current_machine_tag(), tab.tab_node_id()), | 561 TabNodePool::TabIdToTag(current_machine_tag(), tab.tab_node_id()), |
| 555 syncer::SESSIONS)); | 562 syncer::SESSIONS)); |
| 556 } | 563 } |
| 557 } | 564 } |
| 558 | 565 |
| 559 bool SessionsSyncManager::GetAllForeignSessions( | 566 bool SessionsSyncManager::GetAllForeignSessions( |
| 560 std::vector<const sync_driver::SyncedSession*>* sessions) { | 567 std::vector<const sync_driver::SyncedSession*>* sessions) { |
| 561 if (!session_tracker_.LookupAllForeignSessions(sessions)) | 568 if (!session_tracker_.LookupAllForeignSessions( |
| 569 sessions, SyncedSessionTracker::PRESENTABLE)) |
| 562 return false; | 570 return false; |
| 563 std::sort(sessions->begin(), sessions->end(), SessionsRecencyComparator); | 571 std::sort(sessions->begin(), sessions->end(), SessionsRecencyComparator); |
| 564 return true; | 572 return true; |
| 565 } | 573 } |
| 566 | 574 |
| 567 bool SessionsSyncManager::InitFromSyncModel( | 575 bool SessionsSyncManager::InitFromSyncModel( |
| 568 const syncer::SyncDataList& sync_data, | 576 const syncer::SyncDataList& sync_data, |
| 569 syncer::SyncDataList* restored_tabs, | 577 syncer::SyncDataList* restored_tabs, |
| 570 syncer::SyncChangeList* new_changes) { | 578 syncer::SyncChangeList* new_changes) { |
| 571 bool found_current_header = false; | 579 bool found_current_header = false; |
| 572 for (syncer::SyncDataList::const_iterator it = sync_data.begin(); | 580 for (syncer::SyncDataList::const_iterator it = sync_data.begin(); |
| 573 it != sync_data.end(); ++it) { | 581 it != sync_data.end(); ++it) { |
| 574 // TODO(skym): Why don't we ever look at data.change_type()? Why is this | |
| 575 // code path so much different from ProcessSyncChanges? | |
| 576 const syncer::SyncData& data = *it; | 582 const syncer::SyncData& data = *it; |
| 577 DCHECK(data.GetSpecifics().has_session()); | 583 DCHECK(data.GetSpecifics().has_session()); |
| 578 const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session(); | 584 const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session(); |
| 579 if (specifics.session_tag().empty() || | 585 if (specifics.session_tag().empty() || |
| 580 (specifics.has_tab() && | 586 (specifics.has_tab() && |
| 581 (!specifics.has_tab_node_id() || !specifics.tab().has_tab_id()))) { | 587 (!specifics.has_tab_node_id() || !specifics.tab().has_tab_id()))) { |
| 582 syncer::SyncChange tombstone(TombstoneTab(specifics)); | 588 syncer::SyncChange tombstone(TombstoneTab(specifics)); |
| 583 if (tombstone.IsValid()) | 589 if (tombstone.IsValid()) |
| 584 new_changes->push_back(tombstone); | 590 new_changes->push_back(tombstone); |
| 585 } else if (specifics.session_tag() != current_machine_tag()) { | 591 } else if (specifics.session_tag() != current_machine_tag()) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 601 new_changes->push_back(tombstone); | 607 new_changes->push_back(tombstone); |
| 602 } else { | 608 } else { |
| 603 // This is a valid old tab node, add it to the pool so it can be | 609 // This is a valid old tab node, add it to the pool so it can be |
| 604 // reused for reassociation. | 610 // reused for reassociation. |
| 605 local_tab_pool_.AddTabNode(specifics.tab_node_id()); | 611 local_tab_pool_.AddTabNode(specifics.tab_node_id()); |
| 606 restored_tabs->push_back(*it); | 612 restored_tabs->push_back(*it); |
| 607 } | 613 } |
| 608 } | 614 } |
| 609 } | 615 } |
| 610 } | 616 } |
| 617 |
| 618 // Cleanup all foreign sessions, since orphaned tabs may have been added after |
| 619 // the header. |
| 620 std::vector<const sync_driver::SyncedSession*> sessions; |
| 621 session_tracker_.LookupAllForeignSessions(&sessions, |
| 622 SyncedSessionTracker::RAW); |
| 623 for (const auto* session : sessions) { |
| 624 session_tracker_.CleanupSession(session->session_tag); |
| 625 } |
| 626 |
| 611 return found_current_header; | 627 return found_current_header; |
| 612 } | 628 } |
| 613 | 629 |
| 614 void SessionsSyncManager::UpdateTrackerWithForeignSession( | 630 void SessionsSyncManager::UpdateTrackerWithForeignSession( |
| 615 const sync_pb::SessionSpecifics& specifics, | 631 const sync_pb::SessionSpecifics& specifics, |
| 616 const base::Time& modification_time) { | 632 const base::Time& modification_time) { |
| 617 std::string foreign_session_tag = specifics.session_tag(); | 633 std::string foreign_session_tag = specifics.session_tag(); |
| 618 DCHECK_NE(foreign_session_tag, current_machine_tag()); | 634 DCHECK_NE(foreign_session_tag, current_machine_tag()); |
| 619 | 635 |
| 620 sync_driver::SyncedSession* foreign_session = | 636 sync_driver::SyncedSession* foreign_session = |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 656 // Delete any closed windows and unused tabs as necessary. | 672 // Delete any closed windows and unused tabs as necessary. |
| 657 session_tracker_.CleanupSession(foreign_session_tag); | 673 session_tracker_.CleanupSession(foreign_session_tag); |
| 658 } else if (specifics.has_tab()) { | 674 } else if (specifics.has_tab()) { |
| 659 const sync_pb::SessionTab& tab_s = specifics.tab(); | 675 const sync_pb::SessionTab& tab_s = specifics.tab(); |
| 660 SessionID::id_type tab_id = tab_s.tab_id(); | 676 SessionID::id_type tab_id = tab_s.tab_id(); |
| 661 | 677 |
| 662 const sessions::SessionTab* existing_tab; | 678 const sessions::SessionTab* existing_tab; |
| 663 if (session_tracker_.LookupSessionTab(foreign_session_tag, tab_id, | 679 if (session_tracker_.LookupSessionTab(foreign_session_tag, tab_id, |
| 664 &existing_tab) && | 680 &existing_tab) && |
| 665 existing_tab->timestamp > modification_time) { | 681 existing_tab->timestamp > modification_time) { |
| 682 // Force the tracker to remember this tab node id, even if it isn't |
| 683 // currently being used. |
| 684 session_tracker_.GetTab(foreign_session_tag, tab_id, |
| 685 specifics.tab_node_id()); |
| 666 DVLOG(1) << "Ignoring " << foreign_session_tag << "'s session tab " | 686 DVLOG(1) << "Ignoring " << foreign_session_tag << "'s session tab " |
| 667 << tab_id << " with earlier modification time"; | 687 << tab_id << " with earlier modification time"; |
| 668 return; | 688 return; |
| 669 } | 689 } |
| 670 | 690 |
| 671 sessions::SessionTab* tab = session_tracker_.GetTab( | 691 sessions::SessionTab* tab = session_tracker_.GetTab( |
| 672 foreign_session_tag, tab_id, specifics.tab_node_id()); | 692 foreign_session_tag, tab_id, specifics.tab_node_id()); |
| 673 | 693 |
| 674 // Update SessionTab based on protobuf. | 694 // Update SessionTab based on protobuf. |
| 675 tab->SetFromSyncData(tab_s, modification_time); | 695 tab->SetFromSyncData(tab_s, modification_time); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 733 case sync_pb::SyncEnums_DeviceType_TYPE_TABLET: | 753 case sync_pb::SyncEnums_DeviceType_TYPE_TABLET: |
| 734 session_header->device_type = sync_driver::SyncedSession::TYPE_TABLET; | 754 session_header->device_type = sync_driver::SyncedSession::TYPE_TABLET; |
| 735 break; | 755 break; |
| 736 case sync_pb::SyncEnums_DeviceType_TYPE_OTHER: | 756 case sync_pb::SyncEnums_DeviceType_TYPE_OTHER: |
| 737 // Intentionally fall-through | 757 // Intentionally fall-through |
| 738 default: | 758 default: |
| 739 session_header->device_type = sync_driver::SyncedSession::TYPE_OTHER; | 759 session_header->device_type = sync_driver::SyncedSession::TYPE_OTHER; |
| 740 break; | 760 break; |
| 741 } | 761 } |
| 742 } | 762 } |
| 743 session_header->modified_time = mtime; | 763 session_header->modified_time = |
| 764 std::max(mtime, session_header->modified_time); |
| 744 } | 765 } |
| 745 | 766 |
| 746 // static | 767 // static |
| 747 void SessionsSyncManager::BuildSyncedSessionFromSpecifics( | 768 void SessionsSyncManager::BuildSyncedSessionFromSpecifics( |
| 748 const std::string& session_tag, | 769 const std::string& session_tag, |
| 749 const sync_pb::SessionWindow& specifics, | 770 const sync_pb::SessionWindow& specifics, |
| 750 base::Time mtime, | 771 base::Time mtime, |
| 751 sessions::SessionWindow* session_window) { | 772 sessions::SessionWindow* session_window) { |
| 752 if (specifics.has_window_id()) | 773 if (specifics.has_window_id()) |
| 753 session_window->window_id.set_id(specifics.window_id()); | 774 session_window->window_id.set_id(specifics.window_id()); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 804 const std::string& tag, | 825 const std::string& tag, |
| 805 syncer::SyncChangeList* change_output) { | 826 syncer::SyncChangeList* change_output) { |
| 806 if (tag == current_machine_tag()) { | 827 if (tag == current_machine_tag()) { |
| 807 LOG(ERROR) << "Attempting to delete local session. This is not currently " | 828 LOG(ERROR) << "Attempting to delete local session. This is not currently " |
| 808 << "supported."; | 829 << "supported."; |
| 809 return; | 830 return; |
| 810 } | 831 } |
| 811 | 832 |
| 812 std::set<int> tab_node_ids_to_delete; | 833 std::set<int> tab_node_ids_to_delete; |
| 813 session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete); | 834 session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete); |
| 814 if (!DisassociateForeignSession(tag)) { | 835 if (DisassociateForeignSession(tag)) { |
| 815 // We don't have any data for this session, our work here is done! | 836 // Only tell sync to delete the header if there was one. |
| 816 return; | 837 change_output->push_back( |
| 838 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, |
| 839 SyncData::CreateLocalDelete(tag, syncer::SESSIONS))); |
| 817 } | 840 } |
| 818 | |
| 819 // Prepare deletes for the meta-node as well as individual tab nodes. | |
| 820 change_output->push_back( | |
| 821 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, | |
| 822 SyncData::CreateLocalDelete(tag, syncer::SESSIONS))); | |
| 823 | |
| 824 for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin(); | 841 for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin(); |
| 825 it != tab_node_ids_to_delete.end(); ++it) { | 842 it != tab_node_ids_to_delete.end(); ++it) { |
| 826 change_output->push_back(syncer::SyncChange( | 843 change_output->push_back(syncer::SyncChange( |
| 827 FROM_HERE, SyncChange::ACTION_DELETE, | 844 FROM_HERE, SyncChange::ACTION_DELETE, |
| 828 SyncData::CreateLocalDelete(TabNodePool::TabIdToTag(tag, *it), | 845 SyncData::CreateLocalDelete(TabNodePool::TabIdToTag(tag, *it), |
| 829 syncer::SESSIONS))); | 846 syncer::SESSIONS))); |
| 830 } | 847 } |
| 831 if (!sessions_updated_callback_.is_null()) | 848 if (!sessions_updated_callback_.is_null()) |
| 832 sessions_updated_callback_.Run(); | 849 sessions_updated_callback_.Run(); |
| 833 } | 850 } |
| 834 | 851 |
| 835 bool SessionsSyncManager::DisassociateForeignSession( | 852 bool SessionsSyncManager::DisassociateForeignSession( |
| 836 const std::string& foreign_session_tag) { | 853 const std::string& foreign_session_tag) { |
| 837 if (foreign_session_tag == current_machine_tag()) { | 854 DCHECK_NE(foreign_session_tag, current_machine_tag()); |
| 838 DVLOG(1) << "Local session deleted! Doing nothing until a navigation is " | |
| 839 << "triggered."; | |
| 840 return false; | |
| 841 } | |
| 842 DVLOG(1) << "Disassociating session " << foreign_session_tag; | 855 DVLOG(1) << "Disassociating session " << foreign_session_tag; |
| 843 return session_tracker_.DeleteSession(foreign_session_tag); | 856 return session_tracker_.DeleteSession(foreign_session_tag); |
| 844 } | 857 } |
| 845 | 858 |
| 846 bool SessionsSyncManager::GetForeignSession( | 859 bool SessionsSyncManager::GetForeignSession( |
| 847 const std::string& tag, | 860 const std::string& tag, |
| 848 std::vector<const sessions::SessionWindow*>* windows) { | 861 std::vector<const sessions::SessionWindow*>* windows) { |
| 849 return session_tracker_.LookupSessionWindows(tag, windows); | 862 return session_tracker_.LookupSessionWindows(tag, windows); |
| 850 } | 863 } |
| 851 | 864 |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1023 return &favicon_cache_; | 1036 return &favicon_cache_; |
| 1024 } | 1037 } |
| 1025 | 1038 |
| 1026 SyncedWindowDelegatesGetter* | 1039 SyncedWindowDelegatesGetter* |
| 1027 SessionsSyncManager::synced_window_delegates_getter() const { | 1040 SessionsSyncManager::synced_window_delegates_getter() const { |
| 1028 return sessions_client_->GetSyncedWindowDelegatesGetter(); | 1041 return sessions_client_->GetSyncedWindowDelegatesGetter(); |
| 1029 } | 1042 } |
| 1030 | 1043 |
| 1031 void SessionsSyncManager::DoGarbageCollection() { | 1044 void SessionsSyncManager::DoGarbageCollection() { |
| 1032 std::vector<const sync_driver::SyncedSession*> sessions; | 1045 std::vector<const sync_driver::SyncedSession*> sessions; |
| 1033 if (!GetAllForeignSessions(&sessions)) | 1046 if (!session_tracker_.LookupAllForeignSessions(&sessions, |
| 1047 SyncedSessionTracker::RAW)) |
| 1034 return; // No foreign sessions. | 1048 return; // No foreign sessions. |
| 1035 | 1049 |
| 1036 // Iterate through all the sessions and delete any with age older than | 1050 // Iterate through all the sessions and delete any with age older than |
| 1037 // |stale_session_threshold_days_|. | 1051 // |stale_session_threshold_days_|. |
| 1038 syncer::SyncChangeList changes; | 1052 syncer::SyncChangeList changes; |
| 1039 for (std::vector<const sync_driver::SyncedSession*>::const_iterator iter = | 1053 for (const auto* session : sessions) { |
| 1040 sessions.begin(); | |
| 1041 iter != sessions.end(); ++iter) { | |
| 1042 const sync_driver::SyncedSession* session = *iter; | |
| 1043 int session_age_in_days = | 1054 int session_age_in_days = |
| 1044 (base::Time::Now() - session->modified_time).InDays(); | 1055 (base::Time::Now() - session->modified_time).InDays(); |
| 1045 std::string session_tag = session->session_tag; | 1056 if (session_age_in_days > stale_session_threshold_days_) { |
| 1046 if (session_age_in_days > 0 && // If false, local clock is not trustworty. | 1057 std::string session_tag = session->session_tag; |
| 1047 static_cast<size_t>(session_age_in_days) > | |
| 1048 stale_session_threshold_days_) { | |
| 1049 DVLOG(1) << "Found stale session " << session_tag << " with age " | 1058 DVLOG(1) << "Found stale session " << session_tag << " with age " |
| 1050 << session_age_in_days << ", deleting."; | 1059 << session_age_in_days << ", deleting."; |
| 1051 DeleteForeignSessionInternal(session_tag, &changes); | 1060 DeleteForeignSessionInternal(session_tag, &changes); |
| 1052 } | 1061 } |
| 1053 } | 1062 } |
| 1054 | 1063 |
| 1055 if (!changes.empty()) | 1064 if (!changes.empty()) |
| 1056 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | 1065 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
| 1057 } | 1066 } |
| 1058 | 1067 |
| 1059 }; // namespace browser_sync | 1068 }; // namespace browser_sync |
| OLD | NEW |