summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-27 17:56:42 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-27 17:56:42 +0000
commitf545687686a2bcb95fb83a11440f1c80c5a704eb (patch)
treea0b54b7d5588ba2c1585e4ff87b8cfacce4290f0
parentacae39d810773951c6a8bfed2d464248d554015a (diff)
downloadchromium_src-f545687686a2bcb95fb83a11440f1c80c5a704eb.zip
chromium_src-f545687686a2bcb95fb83a11440f1c80c5a704eb.tar.gz
chromium_src-f545687686a2bcb95fb83a11440f1c80c5a704eb.tar.bz2
sync: beginnings of a sync/api implementation of sessions datatype
Based off of https://codereview.chromium.org/23018004/ BUG=80194, 98892 R=shashishekhar@chromium.org, zea@chromium.org Review URL: https://codereview.chromium.org/18600013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@225731 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/sync/glue/session_sync_test_helper.cc135
-rw-r--r--chrome/browser/sync/glue/session_sync_test_helper.h56
-rw-r--r--chrome/browser/sync/profile_sync_service_session_unittest.cc244
-rw-r--r--chrome/browser/sync/sessions2/sessions_sync_manager.cc430
-rw-r--r--chrome/browser/sync/sessions2/sessions_sync_manager.h237
-rw-r--r--chrome/browser/sync/sessions2/sessions_sync_manager_unittest.cc369
-rw-r--r--chrome/browser/sync/sessions2/tab_node_pool2.cc6
-rw-r--r--chrome/browser/sync/sessions2/tab_node_pool2.h4
-rw-r--r--chrome/browser/sync/sessions2/tab_node_pool2_unittest.cc112
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi3
11 files changed, 1367 insertions, 231 deletions
diff --git a/chrome/browser/sync/glue/session_sync_test_helper.cc b/chrome/browser/sync/glue/session_sync_test_helper.cc
new file mode 100644
index 0000000..0fd71f6
--- /dev/null
+++ b/chrome/browser/sync/glue/session_sync_test_helper.cc
@@ -0,0 +1,135 @@
+// Copyright 2013 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/glue/session_sync_test_helper.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/sync/glue/synced_session.h"
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/protocol/sync_enums.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace browser_sync {
+
+static const char* kClientName = "name";
+static const char* kAppId = "app_id";
+static const char* kVirtualUrl = "http://foo/1";
+static const char* kReferrer = "referrer";
+static const char* kTitle = "title";
+
+// static
+void SessionSyncTestHelper::BuildSessionSpecifics(
+ const std::string& tag,
+ sync_pb::SessionSpecifics* meta) {
+ meta->set_session_tag(tag);
+ sync_pb::SessionHeader* header = meta->mutable_header();
+ header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
+ header->set_client_name(kClientName);
+}
+
+// static
+void SessionSyncTestHelper::AddWindowSpecifics(
+ int window_id,
+ const std::vector<int>& tab_list,
+ sync_pb::SessionSpecifics* meta) {
+ sync_pb::SessionHeader* header = meta->mutable_header();
+ sync_pb::SessionWindow* window = header->add_window();
+ window->set_window_id(window_id);
+ window->set_selected_tab_index(0);
+ window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
+ for (std::vector<int>::const_iterator iter = tab_list.begin();
+ iter != tab_list.end(); ++iter) {
+ window->add_tab(*iter);
+ }
+}
+
+// static
+void SessionSyncTestHelper::VerifySyncedSession(
+ const std::string& tag,
+ const std::vector<std::vector<SessionID::id_type> >& windows,
+ const SyncedSession& session) {
+ ASSERT_EQ(tag, session.session_tag);
+ ASSERT_EQ(SyncedSession::TYPE_LINUX, session.device_type);
+ ASSERT_EQ(kClientName, session.session_name);
+ ASSERT_EQ(windows.size(), session.windows.size());
+
+ // We assume the window id's are in increasing order.
+ int i = 0;
+ for (std::vector<std::vector<int> >::const_iterator win_iter =
+ windows.begin();
+ win_iter != windows.end(); ++win_iter, ++i) {
+ SessionWindow* win_ptr;
+ SyncedSession::SyncedWindowMap::const_iterator map_iter =
+ session.windows.find(i);
+ if (map_iter != session.windows.end())
+ win_ptr = map_iter->second;
+ else
+ FAIL();
+ ASSERT_EQ(win_iter->size(), win_ptr->tabs.size());
+ ASSERT_EQ(0, win_ptr->selected_tab_index);
+ ASSERT_EQ(1, win_ptr->type);
+ int j = 0;
+ for (std::vector<int>::const_iterator tab_iter = (*win_iter).begin();
+ tab_iter != (*win_iter).end(); ++tab_iter, ++j) {
+ SessionTab* tab = win_ptr->tabs[j];
+ ASSERT_EQ(*tab_iter, tab->tab_id.id());
+ ASSERT_EQ(1U, tab->navigations.size());
+ ASSERT_EQ(1, tab->tab_visual_index);
+ ASSERT_EQ(0, tab->current_navigation_index);
+ ASSERT_TRUE(tab->pinned);
+ ASSERT_EQ(kAppId, tab->extension_app_id);
+ ASSERT_EQ(1U, tab->navigations.size());
+ ASSERT_EQ(tab->navigations[0].virtual_url(), GURL(kVirtualUrl));
+ ASSERT_EQ(tab->navigations[0].referrer().url, GURL(kReferrer));
+ ASSERT_EQ(tab->navigations[0].title(), string16(ASCIIToUTF16(kTitle)));
+ ASSERT_EQ(tab->navigations[0].transition_type(),
+ content::PAGE_TRANSITION_TYPED);
+ }
+ }
+}
+
+void SessionSyncTestHelper::BuildTabSpecifics(
+ const std::string& tag,
+ int window_id,
+ int tab_id,
+ sync_pb::SessionSpecifics* tab_base) {
+ tab_base->set_session_tag(tag);
+ tab_base->set_tab_node_id(++max_tab_node_id_);
+ sync_pb::SessionTab* tab = tab_base->mutable_tab();
+ tab->set_tab_id(tab_id);
+ tab->set_tab_visual_index(1);
+ tab->set_current_navigation_index(0);
+ tab->set_pinned(true);
+ tab->set_extension_app_id(kAppId);
+ sync_pb::TabNavigation* navigation = tab->add_navigation();
+ navigation->set_virtual_url(kVirtualUrl);
+ navigation->set_referrer(kReferrer);
+ navigation->set_title(kTitle);
+ navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
+}
+
+void SessionSyncTestHelper::Reset() {
+ max_tab_node_id_ = 0;
+}
+
+sync_pb::SessionSpecifics SessionSyncTestHelper::BuildForeignSession(
+ const std::string& tag,
+ const std::vector<SessionID::id_type>& tab_list,
+ std::vector<sync_pb::SessionSpecifics>* tabs) {
+ sync_pb::SessionSpecifics meta;
+ BuildSessionSpecifics(tag, &meta);
+ AddWindowSpecifics(0, tab_list, &meta);
+ std::vector<sync_pb::SessionSpecifics> tabs1;
+ tabs1.resize(tab_list.size());
+ for (size_t i = 0; i < tab_list.size(); ++i) {
+ BuildTabSpecifics(tag, 0, tab_list[i], &tabs1[i]);
+ }
+
+ if (tabs)
+ tabs->swap(tabs1);
+ return meta;
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/session_sync_test_helper.h b/chrome/browser/sync/glue/session_sync_test_helper.h
new file mode 100644
index 0000000..ea576e5
--- /dev/null
+++ b/chrome/browser/sync/glue/session_sync_test_helper.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_SESSION_SYNC_TEST_HELPER_H_
+#define CHROME_BROWSER_SYNC_GLUE_SESSION_SYNC_TEST_HELPER_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/sessions/session_id.h"
+
+namespace sync_pb {
+class SessionSpecifics;
+}
+
+namespace browser_sync {
+
+struct SyncedSession;
+
+class SessionSyncTestHelper {
+ public:
+ SessionSyncTestHelper() : max_tab_node_id_(0) {}
+
+ static void BuildSessionSpecifics(const std::string& tag,
+ sync_pb::SessionSpecifics* meta);
+
+ static void AddWindowSpecifics(int window_id,
+ const std::vector<int>& tab_list,
+ sync_pb::SessionSpecifics* meta);
+
+ static void VerifySyncedSession(
+ const std::string& tag,
+ const std::vector<std::vector<SessionID::id_type> >& windows,
+ const SyncedSession& session);
+
+ void BuildTabSpecifics(const std::string& tag,
+ int window_id,
+ int tab_id,
+ sync_pb::SessionSpecifics* tab_base);
+
+ sync_pb::SessionSpecifics BuildForeignSession(
+ const std::string& tag,
+ const std::vector<SessionID::id_type>& tab_list,
+ std::vector<sync_pb::SessionSpecifics>* tabs);
+
+ void Reset();
+
+ private:
+ int max_tab_node_id_;
+ DISALLOW_COPY_AND_ASSIGN(SessionSyncTestHelper);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_SESSION_SYNC_TEST_HELPER_H_
diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc
index 979ed404..e9857cc 100644
--- a/chrome/browser/sync/profile_sync_service_session_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc
@@ -15,7 +15,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/invalidation/invalidation_service_factory.h"
@@ -28,6 +27,7 @@
#include "chrome/browser/sync/glue/session_change_processor.h"
#include "chrome/browser/sync/glue/session_data_type_controller.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
+#include "chrome/browser/sync/glue/session_sync_test_helper.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/glue/synced_device_tracker.h"
#include "chrome/browser/sync/glue/synced_tab_delegate.h"
@@ -103,73 +103,6 @@ class FakeProfileSyncService : public TestProfileSyncService {
}
};
-void BuildSessionSpecifics(const std::string& tag,
- sync_pb::SessionSpecifics* meta) {
- meta->set_session_tag(tag);
- sync_pb::SessionHeader* header = meta->mutable_header();
- header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
- header->set_client_name("name");
-}
-
-void AddWindowSpecifics(int window_id,
- const std::vector<int>& tab_list,
- sync_pb::SessionSpecifics* meta) {
- sync_pb::SessionHeader* header = meta->mutable_header();
- sync_pb::SessionWindow* window = header->add_window();
- window->set_window_id(window_id);
- window->set_selected_tab_index(0);
- window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
- for (std::vector<int>::const_iterator iter = tab_list.begin();
- iter != tab_list.end(); ++iter) {
- window->add_tab(*iter);
- }
-}
-
-// Verifies number of windows, number of tabs, and basic fields.
-void VerifySyncedSession(
- const std::string& tag,
- const std::vector<std::vector<SessionID::id_type> >& windows,
- const SyncedSession& session) {
- ASSERT_EQ(tag, session.session_tag);
- ASSERT_EQ(SyncedSession::TYPE_LINUX, session.device_type);
- ASSERT_EQ("name", session.session_name);
- ASSERT_EQ(windows.size(), session.windows.size());
-
- // We assume the window id's are in increasing order.
- int i = 0;
- for (std::vector<std::vector<int> >::const_iterator win_iter =
- windows.begin();
- win_iter != windows.end(); ++win_iter, ++i) {
- SessionWindow* win_ptr;
- SyncedSession::SyncedWindowMap::const_iterator map_iter =
- session.windows.find(i);
- if (map_iter != session.windows.end())
- win_ptr = map_iter->second;
- else
- FAIL();
- ASSERT_EQ(win_iter->size(), win_ptr->tabs.size());
- ASSERT_EQ(0, win_ptr->selected_tab_index);
- ASSERT_EQ(1, win_ptr->type);
- int j = 0;
- for (std::vector<int>::const_iterator tab_iter = (*win_iter).begin();
- tab_iter != (*win_iter).end(); ++tab_iter, ++j) {
- SessionTab* tab = win_ptr->tabs[j];
- ASSERT_EQ(*tab_iter, tab->tab_id.id());
- ASSERT_EQ(1U, tab->navigations.size());
- ASSERT_EQ(1, tab->tab_visual_index);
- ASSERT_EQ(0, tab->current_navigation_index);
- ASSERT_TRUE(tab->pinned);
- ASSERT_EQ("app_id", tab->extension_app_id);
- ASSERT_EQ(1U, tab->navigations.size());
- ASSERT_EQ(tab->navigations[0].virtual_url(), GURL("http://foo/1"));
- ASSERT_EQ(tab->navigations[0].referrer().url, GURL("referrer"));
- ASSERT_EQ(tab->navigations[0].title(), string16(ASCIIToUTF16("title")));
- ASSERT_EQ(tab->navigations[0].transition_type(),
- content::PAGE_TRANSITION_TYPED);
- }
- }
-}
-
bool CompareMemoryToString(
const std::string& str,
const scoped_refptr<base::RefCountedMemory>& mem) {
@@ -191,27 +124,9 @@ class ProfileSyncServiceSessionTest
ProfileSyncServiceSessionTest()
: window_bounds_(0, 1, 2, 3),
notified_of_update_(false),
- notified_of_refresh_(false),
- max_tab_node_id_(0) {}
+ notified_of_refresh_(false) {}
ProfileSyncService* sync_service() { return sync_service_.get(); }
- void BuildTabSpecifics(const std::string& tag, int window_id, int tab_id,
- sync_pb::SessionSpecifics* tab_base) {
- tab_base->set_session_tag(tag);
- tab_base->set_tab_node_id(++max_tab_node_id_);
- sync_pb::SessionTab* tab = tab_base->mutable_tab();
- tab->set_tab_id(tab_id);
- tab->set_tab_visual_index(1);
- tab->set_current_navigation_index(0);
- tab->set_pinned(true);
- tab->set_extension_app_id("app_id");
- sync_pb::TabNavigation* navigation = tab->add_navigation();
- navigation->set_virtual_url("http://foo/1");
- navigation->set_referrer("referrer");
- navigation->set_title("title");
- navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
- }
-
protected:
virtual TestingProfile* CreateProfile() OVERRIDE {
TestingProfile::Builder builder;
@@ -252,7 +167,6 @@ class ProfileSyncServiceSessionTest
}
virtual void TearDown() {
- max_tab_node_id_ = 0;
sync_service_->Shutdown();
sync_service_.reset();
@@ -326,7 +240,7 @@ class ProfileSyncServiceSessionTest
bool notified_of_refresh_;
content::NotificationRegistrar registrar_;
net::TestURLFetcherFactory fetcher_factory_;
- int max_tab_node_id_;
+ SessionSyncTestHelper helper_;
};
class CreateRootHelper {
@@ -455,17 +369,11 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
// Fill an instance of session specifics with a foreign session's data.
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Update associator with the session's meta node containing one window.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -481,7 +389,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
ASSERT_EQ(1U, foreign_sessions.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
}
// Write a foreign session with one window to a node. Sync, then add a window.
@@ -493,17 +401,11 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
// Build a foreign session with one window and four tabs.
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1(nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Update associator with the session's meta node containing one window.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
// Add tabs for first window.
@@ -517,17 +419,17 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
// Add a second window.
SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
std::vector<SessionID::id_type> tab_list2(
tab_nums2, tab_nums2 + arraysize(tab_nums2));
- AddWindowSpecifics(1, tab_list2, &meta);
+ helper_.AddWindowSpecifics(1, tab_list2, &meta);
std::vector<sync_pb::SessionSpecifics> tabs2;
tabs2.resize(tab_list2.size());
for (size_t i = 0; i < tab_list2.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
+ helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
}
// Update associator with the session's meta node containing two windows.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -542,17 +444,17 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
ASSERT_EQ(1U, foreign_sessions.size());
session_reference.push_back(tab_list2);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
// Add a third window.
SessionID::id_type tab_nums3[] = {8, 16, 19, 21};
std::vector<SessionID::id_type> tab_list3(
tab_nums3, tab_nums3 + arraysize(tab_nums3));
- AddWindowSpecifics(2, tab_list3, &meta);
+ helper_.AddWindowSpecifics(2, tab_list3, &meta);
std::vector<sync_pb::SessionSpecifics> tabs3;
tabs3.resize(tab_list3.size());
for (size_t i = 0; i < tab_list3.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list3[i], &tabs3[i]);
+ helper_.BuildTabSpecifics(tag, 0, tab_list3[i], &tabs3[i]);
}
// Update associator with the session's meta node containing three windows.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -567,12 +469,12 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
ASSERT_EQ(1U, foreign_sessions.size());
session_reference.push_back(tab_list3);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
// Close third window (by clearing and then not adding it back).
meta.mutable_header()->clear_window();
- AddWindowSpecifics(0, tab_list1, &meta);
- AddWindowSpecifics(1, tab_list2, &meta);
+ helper_.AddWindowSpecifics(0, tab_list1, &meta);
+ helper_.AddWindowSpecifics(1, tab_list2, &meta);
// Update associator with just the meta node, now containing only two windows.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -581,11 +483,11 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
ASSERT_EQ(1U, foreign_sessions.size());
session_reference.pop_back(); // Pop off the data for the third window.
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
// Close second window (by clearing and then not adding it back).
meta.mutable_header()->clear_window();
- AddWindowSpecifics(0, tab_list1, &meta);
+ helper_.AddWindowSpecifics(0, tab_list1, &meta);
// Update associator with just the meta node, now containing only one windows.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -594,7 +496,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
ASSERT_EQ(1U, foreign_sessions.size());
session_reference.pop_back(); // Pop off the data for the second window.
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
}
// Write a foreign session to a node, with the tabs arriving first, and then
@@ -606,17 +508,11 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
// Fill an instance of session specifics with a foreign session's data.
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Add tabs for first window.
for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
@@ -632,7 +528,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
ASSERT_EQ(1U, foreign_sessions.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
}
// Write a foreign session to a node with some tabs that never arrive.
@@ -643,27 +539,21 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) {
// Fill an instance of session specifics with a foreign session's data.
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size()); // First window has all the tabs
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Add a second window, but this time only create two tab nodes, despite the
// window expecting four tabs.
SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
std::vector<SessionID::id_type> tab_list2(
tab_nums2, tab_nums2 + arraysize(tab_nums2));
- AddWindowSpecifics(1, tab_list2, &meta);
+ helper_.AddWindowSpecifics(1, tab_list2, &meta);
std::vector<sync_pb::SessionSpecifics> tabs2;
tabs2.resize(2);
for (size_t i = 0; i < 2; ++i) {
- BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
+ helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
}
// Update associator with the session's meta node containing two windows.
@@ -689,7 +579,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) {
// Close the second window.
meta.mutable_header()->clear_window();
- AddWindowSpecifics(0, tab_list1, &meta);
+ helper_.AddWindowSpecifics(0, tab_list1, &meta);
// Update associator with the session's meta node containing one window.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -701,7 +591,7 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) {
ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
}
// Test the DataTypeController on update.
@@ -848,17 +738,11 @@ TEST_F(ProfileSyncServiceSessionTest, DeleteForeignSession) {
ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
// Fill an instance of session specifics with a foreign session's data.
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Update associator with the session's meta node containing one window.
model_associator_->AssociateForeignSpecifics(meta, base::Time());
@@ -873,7 +757,7 @@ TEST_F(ProfileSyncServiceSessionTest, DeleteForeignSession) {
ASSERT_EQ(1U, foreign_sessions.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
// Now delete the foreign session.
model_associator_->DeleteForeignSession(tag);
@@ -889,28 +773,22 @@ TEST_F(ProfileSyncServiceSessionTest, DeleteStaleSessions) {
// Fill two instances of session specifics with a foreign session's data.
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
std::string tag2 = "tag2";
sync_pb::SessionSpecifics meta2;
- BuildSessionSpecifics(tag2, &meta2);
+ helper_.BuildSessionSpecifics(tag2, &meta2);
SessionID::id_type tab_nums2[] = {8, 15, 18, 20};
std::vector<SessionID::id_type> tab_list2(
tab_nums2, tab_nums2 + arraysize(tab_nums2));
- AddWindowSpecifics(0, tab_list2, &meta2);
+ helper_.AddWindowSpecifics(0, tab_list2, &meta2);
std::vector<sync_pb::SessionSpecifics> tabs2;
tabs2.resize(tab_list2.size());
for (size_t i = 0; i < tab_list2.size(); ++i) {
- BuildTabSpecifics(tag2, 0, tab_list2[i], &tabs2[i]);
+ helper_.BuildTabSpecifics(tag2, 0, tab_list2[i], &tabs2[i]);
}
// Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
@@ -940,7 +818,7 @@ TEST_F(ProfileSyncServiceSessionTest, DeleteStaleSessions) {
ASSERT_EQ(1U, foreign_sessions.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list2);
- VerifySyncedSession(tag2, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag2, session_reference, *(foreign_sessions[0]));
}
// Write a stale foreign session to a node. Then update one of its tabs so
@@ -951,17 +829,11 @@ TEST_F(ProfileSyncServiceSessionTest, StaleSessionRefresh) {
ASSERT_TRUE(create_root.success());
std::string tag = "tag1";
- sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
- SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
- std::vector<SessionID::id_type> tab_list1(
- tab_nums1, tab_nums1 + arraysize(tab_nums1));
- AddWindowSpecifics(0, tab_list1, &meta);
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
std::vector<sync_pb::SessionSpecifics> tabs1;
- tabs1.resize(tab_list1.size());
- for (size_t i = 0; i < tab_list1.size(); ++i) {
- BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
- }
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper_.BuildForeignSession(
+ tag, tab_list1, &tabs1));
// Associate.
base::Time stale_time = base::Time::Now() - base::TimeDelta::FromDays(21);
@@ -985,7 +857,7 @@ TEST_F(ProfileSyncServiceSessionTest, StaleSessionRefresh) {
ASSERT_EQ(1U, foreign_sessions.size());
std::vector<std::vector<SessionID::id_type> > session_reference;
session_reference.push_back(tab_list1);
- VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+ helper_.VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
}
// Crashes sometimes on Windows, particularly XP.
@@ -1217,12 +1089,12 @@ TEST_F(ProfileSyncServiceSessionTest, Favicons) {
// Build a foreign session with one window and one tab.
std::string tag = "tag1";
sync_pb::SessionSpecifics meta;
- BuildSessionSpecifics(tag, &meta);
+ helper_.BuildSessionSpecifics(tag, &meta);
std::vector<SessionID::id_type> tab_list;
tab_list.push_back(5);
- AddWindowSpecifics(0, tab_list, &meta);
+ helper_.AddWindowSpecifics(0, tab_list, &meta);
sync_pb::SessionSpecifics tab;
- BuildTabSpecifics(tag, 0, tab_list[0], &tab);
+ helper_.BuildTabSpecifics(tag, 0, tab_list[0], &tab);
std::string url = tab.tab().navigation(0).virtual_url();
scoped_refptr<base::RefCountedMemory> favicon;
diff --git a/chrome/browser/sync/sessions2/sessions_sync_manager.cc b/chrome/browser/sync/sessions2/sessions_sync_manager.cc
new file mode 100644
index 0000000..2a49054
--- /dev/null
+++ b/chrome/browser/sync/sessions2/sessions_sync_manager.cc
@@ -0,0 +1,430 @@
+// Copyright 2013 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/sessions2/sessions_sync_manager.h"
+
+#include "chrome/browser/sync/glue/synced_tab_delegate.h"
+#include "sync/api/sync_error.h"
+#include "sync/api/sync_error_factory.h"
+#include "sync/api/sync_merge_result.h"
+#include "sync/api/time.h"
+
+using syncer::SyncChange;
+using syncer::SyncData;
+
+namespace browser_sync {
+
+// Maximum number of favicons to sync.
+// TODO(zea): pull this from the server.
+static const int kMaxSyncFavicons = 200;
+
+static const int kInvalidTabNodeId = -1;
+
+SessionsSyncManager::SessionsSyncManager(
+ Profile* profile,
+ scoped_ptr<SyncPrefs> sync_prefs,
+ SyncInternalApiDelegate* delegate)
+ : favicon_cache_(profile, kMaxSyncFavicons),
+ delegate_(delegate),
+ local_session_header_node_id_(kInvalidTabNodeId) {
+ sync_prefs_ = sync_prefs.Pass();
+}
+
+SessionsSyncManager::~SessionsSyncManager() {
+}
+
+// Returns the GUID-based string that should be used for
+// |SessionsSyncManager::current_machine_tag_|.
+static std::string BuildMachineTag(const std::string& cache_guid) {
+ std::string machine_tag = "session_sync";
+ machine_tag.append(cache_guid);
+ return machine_tag;
+}
+
+syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
+ syncer::ModelType type,
+ const syncer::SyncDataList& initial_sync_data,
+ scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
+ scoped_ptr<syncer::SyncErrorFactory> error_handler) {
+ syncer::SyncMergeResult merge_result(type);
+ DCHECK(session_tracker_.Empty());
+ DCHECK_EQ(0U, local_tab_pool_.Capacity());
+
+ error_handler_ = error_handler.Pass();
+ sync_processor_ = sync_processor.Pass();
+
+ local_session_header_node_id_ = kInvalidTabNodeId;
+ scoped_ptr<DeviceInfo> local_device_info(delegate_->GetLocalDeviceInfo());
+ syncer::SyncChangeList new_changes;
+
+ // Make sure we have a machine tag. We do this now (versus earlier) as it's
+ // a conveniently safe time to assert sync is ready and the cache_guid is
+ // initialized.
+ if (current_machine_tag_.empty())
+ InitializeCurrentMachineTag();
+ if (local_device_info) {
+ current_session_name_ = local_device_info->client_name();
+ } else {
+ merge_result.set_error(error_handler_->CreateAndUploadError(
+ FROM_HERE,
+ "Failed to get device info for machine tag."));
+ return merge_result;
+ }
+ session_tracker_.SetLocalSessionTag(current_machine_tag_);
+
+ // First, we iterate over sync data to update our session_tracker_.
+ if (!InitFromSyncModel(initial_sync_data, &new_changes)) {
+ // The sync db didn't have a header node for us. Create one.
+ sync_pb::EntitySpecifics specifics;
+ sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session();
+ base_specifics->set_session_tag(current_machine_tag());
+ sync_pb::SessionHeader* header_s = base_specifics->mutable_header();
+ header_s->set_client_name(current_session_name_);
+ header_s->set_device_type(DeviceInfo::GetLocalDeviceType());
+ syncer::SyncData data = syncer::SyncData::CreateLocalData(
+ current_machine_tag(), current_session_name_, specifics);
+ new_changes.push_back(syncer::SyncChange(
+ FROM_HERE, syncer::SyncChange::ACTION_ADD, data));
+ }
+
+#if defined(OS_ANDROID)
+ std::string sync_machine_tag(BuildMachineTag(delegate_->GetCacheGuid()));
+ if (current_machine_tag_.compare(sync_machine_tag) != 0)
+ DeleteForeignSession(sync_machine_tag, &new_changes);
+#endif
+
+ // Check if anything has changed on the local client side.
+ AssociateWindows(RELOAD_TABS, &new_changes);
+
+ merge_result.set_error(
+ sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
+ return merge_result;
+}
+
+void SessionsSyncManager::AssociateWindows(
+ ReloadTabsOption option,
+ syncer::SyncChangeList* change_output) {
+ NOTIMPLEMENTED() << "TODO(tim)";
+}
+
+void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab,
+ syncer::SyncChangeList* change_output) {
+ NOTIMPLEMENTED() << "TODO(tim)";
+}
+
+void SessionsSyncManager::OnLocalTabModified(
+ const SyncedTabDelegate& modified_tab, syncer::SyncError* error) {
+ NOTIMPLEMENTED() << "TODO(tim): SessionModelAssociator::Observe equivalent.";
+}
+
+void SessionsSyncManager::OnBrowserOpened() {
+ NOTIMPLEMENTED() << "TODO(tim): SessionModelAssociator::Observe equivalent.";
+}
+
+bool SessionsSyncManager::ShouldSyncTab(const SyncedTabDelegate& tab) const {
+ NOTIMPLEMENTED() << "TODO(tim)";
+ return true;
+}
+
+void SessionsSyncManager::ForwardRelevantFaviconUpdatesToFaviconCache(
+ const std::set<GURL>& updated_favicon_page_urls) {
+ NOTIMPLEMENTED() <<
+ "TODO(tim): SessionModelAssociator::FaviconsUpdated equivalent.";
+}
+
+void SessionsSyncManager::StopSyncing(syncer::ModelType type) {
+ NOTIMPLEMENTED();
+}
+
+syncer::SyncDataList SessionsSyncManager::GetAllSyncData(
+ syncer::ModelType type) const {
+ NOTIMPLEMENTED();
+ return syncer::SyncDataList();
+}
+
+syncer::SyncError SessionsSyncManager::ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) {
+ NOTIMPLEMENTED();
+ return syncer::SyncError();
+}
+
+syncer::SyncChange SessionsSyncManager::TombstoneTab(
+ const sync_pb::SessionSpecifics& tab) {
+ if (!tab.has_tab_node_id()) {
+ LOG(WARNING) << "Old sessions node without tab node id; can't tombstone.";
+ return syncer::SyncChange();
+ } else {
+ return syncer::SyncChange(
+ FROM_HERE,
+ SyncChange::ACTION_DELETE,
+ SyncData::CreateLocalDelete(
+ TabNodePool2::TabIdToTag(current_machine_tag(),
+ tab.tab_node_id()),
+ syncer::SESSIONS));
+ }
+}
+
+bool SessionsSyncManager::GetAllForeignSessions(
+ std::vector<const SyncedSession*>* sessions) {
+ return session_tracker_.LookupAllForeignSessions(sessions);
+}
+
+bool SessionsSyncManager::InitFromSyncModel(
+ const syncer::SyncDataList& sync_data,
+ syncer::SyncChangeList* new_changes) {
+ bool found_current_header = false;
+ for (syncer::SyncDataList::const_iterator it = sync_data.begin();
+ it != sync_data.end();
+ ++it) {
+ const syncer::SyncData& data = *it;
+ DCHECK(data.GetSpecifics().has_session());
+ const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session();
+ if (specifics.session_tag().empty() ||
+ (specifics.has_tab() && (!specifics.has_tab_node_id() ||
+ !specifics.tab().has_tab_id()))) {
+ syncer::SyncChange tombstone(TombstoneTab(specifics));
+ if (tombstone.IsValid())
+ new_changes->push_back(tombstone);
+ } else if (specifics.session_tag() != current_machine_tag()) {
+ UpdateTrackerWithForeignSession(specifics, data.GetRemoteModifiedTime());
+ } else {
+ // This is previously stored local session information.
+ if (specifics.has_header() && !found_current_header) {
+ // This is our previous header node, reuse it.
+ found_current_header = true;
+ if (specifics.header().has_client_name())
+ current_session_name_ = specifics.header().client_name();
+ } else {
+ if (specifics.has_header() || !specifics.has_tab()) {
+ LOG(WARNING) << "Found more than one session header node with local "
+ << "tag.";
+ syncer::SyncChange tombstone(TombstoneTab(specifics));
+ if (tombstone.IsValid())
+ new_changes->push_back(tombstone);
+ } else {
+ // This is a valid old tab node, add it to the pool so it can be
+ // reused for reassociation.
+ local_tab_pool_.AddTabNode(specifics.tab_node_id());
+ }
+ }
+ }
+ }
+ return found_current_header;
+}
+
+void SessionsSyncManager::UpdateTrackerWithForeignSession(
+ const sync_pb::SessionSpecifics& specifics,
+ const base::Time& modification_time) {
+ std::string foreign_session_tag = specifics.session_tag();
+ DCHECK_NE(foreign_session_tag, current_machine_tag());
+
+ SyncedSession* foreign_session =
+ session_tracker_.GetSession(foreign_session_tag);
+ if (specifics.has_header()) {
+ // Read in the header data for this foreign session.
+ // Header data contains window information and ordered tab id's for each
+ // window.
+
+ // Load (or create) the SyncedSession object for this client.
+ const sync_pb::SessionHeader& header = specifics.header();
+ PopulateSessionHeaderFromSpecifics(header,
+ modification_time,
+ foreign_session);
+
+ // Reset the tab/window tracking for this session (must do this before
+ // we start calling PutWindowInSession and PutTabInWindow so that all
+ // unused tabs/windows get cleared by the CleanupSession(...) call).
+ session_tracker_.ResetSessionTracking(foreign_session_tag);
+
+ // Process all the windows and their tab information.
+ int num_windows = header.window_size();
+ DVLOG(1) << "Associating " << foreign_session_tag << " with "
+ << num_windows << " windows.";
+
+ for (int i = 0; i < num_windows; ++i) {
+ const sync_pb::SessionWindow& window_s = header.window(i);
+ SessionID::id_type window_id = window_s.window_id();
+ session_tracker_.PutWindowInSession(foreign_session_tag,
+ window_id);
+ BuildSyncedSessionFromSpecifics(foreign_session_tag,
+ window_s,
+ modification_time,
+ foreign_session->windows[window_id]);
+ }
+ // Delete any closed windows and unused tabs as necessary.
+ session_tracker_.CleanupSession(foreign_session_tag);
+ } else if (specifics.has_tab()) {
+ const sync_pb::SessionTab& tab_s = specifics.tab();
+ SessionID::id_type tab_id = tab_s.tab_id();
+ SessionTab* tab =
+ session_tracker_.GetTab(foreign_session_tag,
+ tab_id,
+ specifics.tab_node_id());
+
+ // Update SessionTab based on protobuf.
+ tab->SetFromSyncData(tab_s, modification_time);
+
+ // If a favicon or favicon urls are present, load the URLs and visit
+ // times into the in-memory favicon cache.
+ RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time);
+
+ // Update the last modified time.
+ if (foreign_session->modified_time < modification_time)
+ foreign_session->modified_time = modification_time;
+ } else {
+ LOG(WARNING) << "Ignoring foreign session node with missing header/tab "
+ << "fields and tag " << foreign_session_tag << ".";
+ }
+}
+
+void SessionsSyncManager::InitializeCurrentMachineTag() {
+ DCHECK(current_machine_tag_.empty());
+ std::string persisted_guid;
+ persisted_guid = sync_prefs_->GetSyncSessionsGUID();
+ if (!persisted_guid.empty()) {
+ current_machine_tag_ = persisted_guid;
+ DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid;
+ } else {
+ current_machine_tag_ = BuildMachineTag(delegate_->GetCacheGuid());
+ DVLOG(1) << "Creating session sync guid: " << current_machine_tag_;
+ sync_prefs_->SetSyncSessionsGUID(current_machine_tag_);
+ }
+
+ local_tab_pool_.SetMachineTag(current_machine_tag_);
+}
+
+// static
+void SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
+ const sync_pb::SessionHeader& header_specifics,
+ base::Time mtime,
+ SyncedSession* session_header) const {
+ if (header_specifics.has_client_name())
+ session_header->session_name = header_specifics.client_name();
+ if (header_specifics.has_device_type()) {
+ switch (header_specifics.device_type()) {
+ case sync_pb::SyncEnums_DeviceType_TYPE_WIN:
+ session_header->device_type = SyncedSession::TYPE_WIN;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_MAC:
+ session_header->device_type = SyncedSession::TYPE_MACOSX;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_LINUX:
+ session_header->device_type = SyncedSession::TYPE_LINUX;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_CROS:
+ session_header->device_type = SyncedSession::TYPE_CHROMEOS;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_PHONE:
+ session_header->device_type = SyncedSession::TYPE_PHONE;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_TABLET:
+ session_header->device_type = SyncedSession::TYPE_TABLET;
+ break;
+ case sync_pb::SyncEnums_DeviceType_TYPE_OTHER:
+ // Intentionally fall-through
+ default:
+ session_header->device_type = SyncedSession::TYPE_OTHER;
+ break;
+ }
+ }
+ session_header->modified_time = mtime;
+}
+
+// static
+void SessionsSyncManager::BuildSyncedSessionFromSpecifics(
+ const std::string& session_tag,
+ const sync_pb::SessionWindow& specifics,
+ base::Time mtime,
+ SessionWindow* session_window) {
+ if (specifics.has_window_id())
+ session_window->window_id.set_id(specifics.window_id());
+ if (specifics.has_selected_tab_index())
+ session_window->selected_tab_index = specifics.selected_tab_index();
+ if (specifics.has_browser_type()) {
+ if (specifics.browser_type() ==
+ sync_pb::SessionWindow_BrowserType_TYPE_TABBED) {
+ session_window->type = 1;
+ } else {
+ session_window->type = 2;
+ }
+ }
+ session_window->timestamp = mtime;
+ session_window->tabs.resize(specifics.tab_size(), NULL);
+ for (int i = 0; i < specifics.tab_size(); i++) {
+ SessionID::id_type tab_id = specifics.tab(i);
+ session_tracker_.PutTabInWindow(session_tag,
+ session_window->window_id.id(),
+ tab_id,
+ i);
+ }
+}
+
+void SessionsSyncManager::RefreshFaviconVisitTimesFromForeignTab(
+ const sync_pb::SessionTab& tab, const base::Time& modification_time) {
+ // First go through and iterate over all the navigations, checking if any
+ // have valid favicon urls.
+ for (int i = 0; i < tab.navigation_size(); ++i) {
+ if (!tab.navigation(i).favicon_url().empty()) {
+ const std::string& page_url = tab.navigation(i).virtual_url();
+ const std::string& favicon_url = tab.navigation(i).favicon_url();
+ favicon_cache_.OnReceivedSyncFavicon(GURL(page_url),
+ GURL(favicon_url),
+ std::string(),
+ syncer::TimeToProtoTime(
+ modification_time));
+ }
+ }
+}
+
+bool SessionsSyncManager::GetSyncedFaviconForPageURL(
+ const std::string& page_url,
+ scoped_refptr<base::RefCountedMemory>* favicon_png) const {
+ return favicon_cache_.GetSyncedFaviconForPageURL(GURL(page_url), favicon_png);
+}
+
+void SessionsSyncManager::DeleteForeignSession(
+ const std::string& tag, syncer::SyncChangeList* change_output) {
+ if (tag == current_machine_tag()) {
+ LOG(ERROR) << "Attempting to delete local session. This is not currently "
+ << "supported.";
+ return;
+ }
+
+ std::set<int> tab_node_ids_to_delete;
+ session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete);
+ if (!DisassociateForeignSession(tag)) {
+ // We don't have any data for this session, our work here is done!
+ return;
+ }
+
+ // Prepare deletes for the meta-node as well as individual tab nodes.
+ change_output->push_back(syncer::SyncChange(
+ FROM_HERE,
+ SyncChange::ACTION_DELETE,
+ SyncData::CreateLocalDelete(tag, syncer::SESSIONS)));
+
+ for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin();
+ it != tab_node_ids_to_delete.end();
+ ++it) {
+ change_output->push_back(syncer::SyncChange(
+ FROM_HERE,
+ SyncChange::ACTION_DELETE,
+ SyncData::CreateLocalDelete(TabNodePool2::TabIdToTag(tag, *it),
+ syncer::SESSIONS)));
+ }
+}
+
+bool SessionsSyncManager::DisassociateForeignSession(
+ const std::string& foreign_session_tag) {
+ if (foreign_session_tag == current_machine_tag()) {
+ DVLOG(1) << "Local session deleted! Doing nothing until a navigation is "
+ << "triggered.";
+ return false;
+ }
+ DVLOG(1) << "Disassociating session " << foreign_session_tag;
+ return session_tracker_.DeleteSession(foreign_session_tag);
+}
+
+}; // namespace browser_sync
diff --git a/chrome/browser/sync/sessions2/sessions_sync_manager.h b/chrome/browser/sync/sessions2/sessions_sync_manager.h
new file mode 100644
index 0000000..174ede6
--- /dev/null
+++ b/chrome/browser/sync/sessions2/sessions_sync_manager.h
@@ -0,0 +1,237 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_SYNC_SESSIONS2_SESSIONS_SYNC_MANAGER_H_
+#define CHROME_BROWSER_SYNC_SESSIONS2_SESSIONS_SYNC_MANAGER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/time/time.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/browser/sync/glue/favicon_cache.h"
+#include "chrome/browser/sync/glue/synced_session.h"
+#include "chrome/browser/sync/glue/synced_session_tracker.h"
+#include "chrome/browser/sync/sessions2/tab_node_pool2.h"
+#include "chrome/browser/sync/sync_prefs.h"
+#include "sync/api/syncable_service.h"
+
+class Profile;
+
+namespace syncer {
+class SyncErrorFactory;
+}
+
+namespace sync_pb {
+class SessionHeader;
+class SessionSpecifics;
+class SessionTab;
+class SessionWindow;
+class TabNavigation;
+} // namespace sync_pb
+
+namespace browser_sync {
+
+class DataTypeErrorHandler;
+class SyncedTabDelegate;
+
+// Contains all logic for associating the Chrome sessions model and
+// the sync sessions model.
+class SessionsSyncManager : public syncer::SyncableService {
+ public:
+ // Isolates SessionsSyncManager from having to depend on sync internals.
+ class SyncInternalApiDelegate {
+ public:
+ // Returns sync's representation of the local device info.
+ // Return value is an empty scoped_ptr if the device info is unavailable.
+ virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const = 0;
+
+ // Used for creation of the machine tag for this local session.
+ virtual std::string GetCacheGuid() const = 0;
+ };
+
+ SessionsSyncManager(Profile* profile,
+ scoped_ptr<SyncPrefs> sync_prefs,
+ SyncInternalApiDelegate* delegate);
+ virtual ~SessionsSyncManager();
+
+ // A local navigation event took place that affects the synced session
+ // for this instance of Chrome.
+ void OnLocalTabModified(const SyncedTabDelegate& modified_tab,
+ syncer::SyncError* error);
+
+ // When a Browser window is opened, we want to know so we can make sure our
+ // bookkeeping of open windows / sessions on this device is up-to-date.
+ void OnBrowserOpened();
+
+ // A local navigation occurred that triggered updates to favicon data for
+ // each URL in |updated_page_urls|. This is routed through Sessions Sync so
+ // that we can filter (exclude) favicon updates for pages that aren't
+ // currently part of the set of local open tabs, and pass relevant updates
+ // on to FaviconCache for out-of-band favicon syncing.
+ void ForwardRelevantFaviconUpdatesToFaviconCache(
+ const std::set<GURL>& updated_favicon_page_urls);
+
+ // Returns the tag used to uniquely identify this machine's session in the
+ // sync model.
+ const std::string& current_machine_tag() const {
+ DCHECK(!current_machine_tag_.empty());
+ return current_machine_tag_;
+ }
+
+ // Builds a list of all foreign sessions. Caller does NOT own SyncedSession
+ // objects.
+ // Returns true if foreign sessions were found, false otherwise.
+ bool GetAllForeignSessions(std::vector<const SyncedSession*>* sessions);
+
+ // If a valid favicon for the page at |url| is found, fills |favicon_png| with
+ // the png-encoded image and returns true. Else, returns false.
+ bool GetSyncedFaviconForPageURL(
+ const std::string& page_url,
+ scoped_refptr<base::RefCountedMemory>* favicon_png) const;
+
+ // syncer::SyncableService implementation.
+ virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
+ syncer::ModelType type,
+ const syncer::SyncDataList& initial_sync_data,
+ scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
+ scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE;
+ virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
+ virtual syncer::SyncDataList GetAllSyncData(
+ syncer::ModelType type) const OVERRIDE;
+ virtual syncer::SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, DeleteForeignSession);
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
+ WriteForeignSessionToNodeTabsFirst);
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
+ WriteForeignSessionToNodeMissingTabs);
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, Favicons);
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
+ SaveUnassociatedNodesForReassociation);
+ FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, MergeDeletesCorruptNode);
+
+ void InitializeCurrentMachineTag();
+
+ // Load and add window or tab data for a foreign session to our internal
+ // tracking.
+ void UpdateTrackerWithForeignSession(
+ const sync_pb::SessionSpecifics& specifics,
+ const base::Time& modification_time);
+
+ // Returns true if |sync_data| contained a header node for the current
+ // machine, false otherwise.
+ bool InitFromSyncModel(const syncer::SyncDataList& sync_data,
+ syncer::SyncChangeList* new_changes);
+
+ // Helper to construct a deletion SyncChange for a *tab node*.
+ // Caller should check IsValid() on the returned change, as it's possible
+ // this node could not be deleted.
+ syncer::SyncChange TombstoneTab(const sync_pb::SessionSpecifics& tab);
+
+ // Helper method to load the favicon data from the tab specifics. If the
+ // favicon is valid, stores the favicon data into the favicon cache.
+ void RefreshFaviconVisitTimesFromForeignTab(
+ const sync_pb::SessionTab& tab, const base::Time& modification_time);
+
+ // Removes a foreign session from our internal bookkeeping.
+ // Returns true if the session was found and deleted, false if no data was
+ // found for that session. This will *NOT* trigger sync deletions. See
+ // DeleteForeignSession below.
+ bool DisassociateForeignSession(const std::string& foreign_session_tag);
+
+ // Delete a foreign session and all its sync data.
+ // |change_output| *must* be provided as a link to the SyncChange pipeline
+ // that exists in the caller's context. This function will append necessary
+ // changes for processing later.
+ void DeleteForeignSession(const std::string& tag,
+ syncer::SyncChangeList* change_output);
+
+ // Used to populate a session header from the session specifics header
+ // provided.
+ void PopulateSessionHeaderFromSpecifics(
+ const sync_pb::SessionHeader& header_specifics,
+ base::Time mtime,
+ SyncedSession* session_header) const;
+
+ // Builds |session_window| from the session specifics window
+ // provided and updates the SessionTracker with foreign session data created.
+ void BuildSyncedSessionFromSpecifics(
+ const std::string& session_tag,
+ const sync_pb::SessionWindow& specifics,
+ base::Time mtime,
+ SessionWindow* session_window);
+
+ // Resync local window information. Updates the local sessions header node
+ // with the status of open windows and the order of tabs they contain. Should
+ // only be called for changes that affect a window, not a change within a
+ // single tab.
+ //
+ // RELOAD_TABS will additionally cause a resync of all tabs (same as calling
+ // AssociateTabs with a vector of all tabs).
+ //
+ // Returns: false if the local session's sync nodes were deleted and
+ // reassociation is necessary, true otherwise.
+ //
+ // |change_output| *must* be provided as a link to the SyncChange pipeline
+ // that exists in the caller's context. This function will append necessary
+ // changes for processing later.
+ enum ReloadTabsOption {
+ RELOAD_TABS,
+ DONT_RELOAD_TABS
+ };
+ void AssociateWindows(ReloadTabsOption option,
+ syncer::SyncChangeList* change_output);
+
+ // Loads and reassociates the local tabs referenced in |tabs|.
+ // |change_output| *must* be provided as a link to the SyncChange pipeline
+ // that exists in the caller's context. This function will append necessary
+ // changes for processing later.
+ void AssociateTab(SyncedTabDelegate* const tab,
+ syncer::SyncChangeList* change_output);
+
+ // Control which local tabs we're interested in syncing.
+ // Ensures the profile matches sync's profile and that the tab has valid
+ // entries.
+ bool ShouldSyncTab(const SyncedTabDelegate& tab) const;
+
+ SyncedSessionTracker session_tracker_;
+ FaviconCache favicon_cache_;
+
+ // Pool of used/available sync nodes associated with local tabs.
+ TabNodePool2 local_tab_pool_;
+
+ scoped_ptr<SyncPrefs> sync_prefs_;
+
+ scoped_ptr<syncer::SyncErrorFactory> error_handler_;
+ scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
+
+ const SyncInternalApiDelegate* const delegate_;
+
+ // Unique client tag.
+ std::string current_machine_tag_;
+
+ // User-visible machine name.
+ std::string current_session_name_;
+
+ // SyncID for the sync node containing all the window information for this
+ // client.
+ int local_session_header_node_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionsSyncManager);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_SESSIONS2_SESSIONS_SYNC_MANAGER_H_
diff --git a/chrome/browser/sync/sessions2/sessions_sync_manager_unittest.cc b/chrome/browser/sync/sessions2/sessions_sync_manager_unittest.cc
new file mode 100644
index 0000000..ac57edf
--- /dev/null
+++ b/chrome/browser/sync/sessions2/sessions_sync_manager_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright 2013 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/sessions2/sessions_sync_manager.h"
+
+#include "base/run_loop.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/browser/sync/glue/session_sync_test_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "sync/api/sync_error_factory_mock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using syncer::SyncData;
+
+namespace browser_sync {
+
+namespace {
+
+class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
+ public:
+ explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
+ : output_(output) {}
+ virtual syncer::SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const syncer::SyncChangeList& change_list) OVERRIDE {
+ if (output_)
+ output_->assign(change_list.begin(), change_list.end());
+ return syncer::SyncError();
+ }
+
+ virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
+ const OVERRIDE {
+ return syncer::SyncDataList();
+ }
+ private:
+ syncer::SyncChangeList* output_;
+};
+
+} // namespace
+
+class SessionsSyncManagerTest
+ : public testing::Test,
+ public SessionsSyncManager::SyncInternalApiDelegate {
+ public:
+ SessionsSyncManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ profile_.reset(new TestingProfile());
+ manager_.reset(new SessionsSyncManager(
+ profile_.get(),
+ scoped_ptr<SyncPrefs>(new SyncPrefs(profile_->GetPrefs())),
+ this));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ helper()->Reset();
+ manager_.reset();
+ profile_.reset();
+ }
+
+ virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
+ return scoped_ptr<DeviceInfo>(
+ new DeviceInfo(GetCacheGuid(),
+ "Wayne Gretzky's Hacking Box",
+ "Chromium 10k",
+ "Chrome 10k",
+ sync_pb::SyncEnums_DeviceType_TYPE_LINUX));
+ }
+
+ virtual std::string GetCacheGuid() const OVERRIDE {
+ return "cache_guid";
+ }
+
+ SessionsSyncManager* manager() { return manager_.get(); }
+ SessionSyncTestHelper* helper() { return &helper_; }
+
+ void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
+ syncer::SyncChangeList* output) {
+ syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing(
+ syncer::SESSIONS, initial_data,
+ scoped_ptr<syncer::SyncChangeProcessor>(
+ new TestSyncProcessorStub(output)),
+ scoped_ptr<syncer::SyncErrorFactory>(
+ new syncer::SyncErrorFactoryMock()));
+ EXPECT_FALSE(result.error().IsSet());
+ }
+
+ void InitWithNoSyncData() {
+ InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL);
+ }
+
+ syncer::SyncChangeList* FilterOutLocalHeaderCreation(
+ syncer::SyncChangeList* list) {
+ syncer::SyncChangeList::iterator it = list->begin();
+ bool found = false;
+ for (; it != list->end(); ++it) {
+ if (it->sync_data().GetTag() == manager_->current_machine_tag()) {
+ EXPECT_EQ(syncer::SyncChange::ACTION_ADD, it->change_type());
+ list->erase(it);
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ return list;
+ }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<Profile> profile_;
+ scoped_ptr<SessionsSyncManager> manager_;
+ SessionSyncTestHelper helper_;
+};
+
+TEST_F(SessionsSyncManagerTest, MergeLocalSession) {
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
+ EXPECT_FALSE(manager()->current_machine_tag().empty());
+
+ EXPECT_EQ(1U, out.size());
+ EXPECT_TRUE(out[0].IsValid());
+ EXPECT_EQ(syncer::SyncChange::ACTION_ADD, out[0].change_type());
+ const SyncData data(out[0].sync_data());
+ EXPECT_EQ(manager()->current_machine_tag(), data.GetTag());
+ const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
+ EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
+ EXPECT_TRUE(specifics.has_header());
+ const sync_pb::SessionHeader& header_s = specifics.header();
+ EXPECT_TRUE(header_s.has_device_type());
+ EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
+ EXPECT_EQ(0, header_s.window_size());
+
+ // Now take that header node and feed it in as input.
+ SyncData d(SyncData::CreateRemoteData(1, data.GetSpecifics(), base::Time()));
+ syncer::SyncDataList in(&d, &d + 1);
+ out.clear();
+ TearDown();
+ SetUp();
+ InitWithSyncDataTakeOutput(in, &out);
+ EXPECT_TRUE(out.empty());
+}
+
+TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
+ std::string tag = "tag1";
+
+ SessionID::id_type n1[] = {5, 10, 13, 17};
+ std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
+ std::vector<sync_pb::SessionSpecifics> tabs1;
+ sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
+ tag, tab_list1, &tabs1));
+ // Add a second window.
+ SessionID::id_type n2[] = {7, 15, 18, 20};
+ std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
+ helper()->AddWindowSpecifics(1, tab_list2, &meta);
+
+ // Set up initial data.
+ syncer::SyncDataList initial_data;
+ sync_pb::EntitySpecifics entity;
+ entity.mutable_session()->CopyFrom(meta);
+ initial_data.push_back(SyncData::CreateRemoteData(1, entity, base::Time()));
+ for (size_t i = 0; i < tabs1.size(); i++) {
+ sync_pb::EntitySpecifics entity;
+ entity.mutable_session()->CopyFrom(tabs1[i]);
+ initial_data.push_back(SyncData::CreateRemoteData(
+ i + 2, entity, base::Time()));
+ }
+ for (size_t i = 0; i < tab_list2.size(); ++i) {
+ sync_pb::EntitySpecifics entity;
+ helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
+ entity.mutable_session());
+ initial_data.push_back(
+ SyncData::CreateRemoteData(i + 10, entity, base::Time()));
+ }
+
+ syncer::SyncChangeList output;
+ InitWithSyncDataTakeOutput(initial_data, &output);
+ EXPECT_TRUE(FilterOutLocalHeaderCreation(&output)->empty());
+
+ std::vector<const SyncedSession*> foreign_sessions;
+ ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
+ std::vector<std::vector<SessionID::id_type> > session_reference;
+ session_reference.push_back(tab_list1);
+ session_reference.push_back(tab_list2);
+ helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+}
+
+TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
+ InitWithNoSyncData();
+ std::string tag = "tag1";
+ syncer::SyncChangeList changes;
+
+ std::vector<const SyncedSession*> foreign_sessions;
+ ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
+ manager()->DeleteForeignSession(tag, &changes);
+ ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
+ EXPECT_TRUE(changes.empty());
+
+ // Fill an instance of session specifics with a foreign session's data.
+ std::vector<sync_pb::SessionSpecifics> tabs;
+ SessionID::id_type n1[] = {5, 10, 13, 17};
+ std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
+ sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
+ tag, tab_nums1, &tabs));
+
+ // Update associator with the session's meta node, window, and tabs.
+ manager()->UpdateTrackerWithForeignSession(meta, base::Time());
+ for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
+ iter != tabs.end(); ++iter) {
+ manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
+ }
+ ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
+ ASSERT_EQ(1U, foreign_sessions.size());
+
+ // Now delete the foreign session.
+ manager()->DeleteForeignSession(tag, &changes);
+ EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
+
+ EXPECT_EQ(5U, changes.size());
+ std::set<std::string> expected_tags(&tag, &tag + 1);
+ for (int i = 0; i < 5; i++)
+ expected_tags.insert(TabNodePool2::TabIdToTag(tag, i));
+
+ for (int i = 0; i < 5; i++) {
+ SCOPED_TRACE(changes[i].ToString());
+ EXPECT_TRUE(changes[i].IsValid());
+ EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[i].change_type());
+ EXPECT_TRUE(changes[i].sync_data().IsValid());
+ EXPECT_EQ(1U, expected_tags.erase(changes[i].sync_data().GetTag()));
+ }
+}
+
+// Write a foreign session to a node, with the tabs arriving first, and then
+// retrieve it.
+TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
+ InitWithNoSyncData();
+
+ // Fill an instance of session specifics with a foreign session's data.
+ std::string tag = "tag1";
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
+ std::vector<sync_pb::SessionSpecifics> tabs1;
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
+ tag, tab_list1, &tabs1));
+
+ // Add tabs for first window.
+ for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
+ iter != tabs1.end(); ++iter) {
+ manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
+ }
+ // Update associator with the session's meta node containing one window.
+ manager()->UpdateTrackerWithForeignSession(meta, base::Time());
+
+ // Check that the foreign session was associated and retrieve the data.
+ std::vector<const SyncedSession*> foreign_sessions;
+ ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
+ ASSERT_EQ(1U, foreign_sessions.size());
+ std::vector<std::vector<SessionID::id_type> > session_reference;
+ session_reference.push_back(tab_list1);
+ helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+}
+
+// Write a foreign session to a node with some tabs that never arrive.
+TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
+ InitWithNoSyncData();
+
+ // Fill an instance of session specifics with a foreign session's data.
+ std::string tag = "tag1";
+ SessionID::id_type nums1[] = {5, 10, 13, 17};
+ std::vector<sync_pb::SessionSpecifics> tabs1;
+ std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
+ sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
+ tag, tab_list1, &tabs1));
+ // Add a second window, but this time only create two tab nodes, despite the
+ // window expecting four tabs.
+ SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
+ std::vector<SessionID::id_type> tab_list2(
+ tab_nums2, tab_nums2 + arraysize(tab_nums2));
+ helper()->AddWindowSpecifics(1, tab_list2, &meta);
+ std::vector<sync_pb::SessionSpecifics> tabs2;
+ tabs2.resize(2);
+ for (size_t i = 0; i < 2; ++i) {
+ helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
+ }
+
+ // Update associator with the session's meta node containing two windows.
+ manager()->UpdateTrackerWithForeignSession(meta, base::Time());
+ // Add tabs for first window.
+ for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
+ iter != tabs1.end(); ++iter) {
+ manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
+ }
+ // Add tabs for second window.
+ for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
+ iter != tabs2.end(); ++iter) {
+ manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
+ }
+
+ // Check that the foreign session was associated and retrieve the data.
+ std::vector<const SyncedSession*> foreign_sessions;
+ ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
+ ASSERT_EQ(1U, foreign_sessions.size());
+ ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
+ ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
+ ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
+
+ // Close the second window.
+ meta.mutable_header()->clear_window();
+ helper()->AddWindowSpecifics(0, tab_list1, &meta);
+
+ // Update associator with the session's meta node containing one window.
+ manager()->UpdateTrackerWithForeignSession(meta, base::Time());
+
+ // Check that the foreign session was associated and retrieve the data.
+ foreign_sessions.clear();
+ ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
+ ASSERT_EQ(1U, foreign_sessions.size());
+ ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
+ std::vector<std::vector<SessionID::id_type> > session_reference;
+ session_reference.push_back(tab_list1);
+ helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
+}
+
+// TODO(shashishekhar): "Move this to TabNodePool unittests."
+TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
+ syncer::SyncChangeList changes;
+ InitWithNoSyncData();
+
+ std::string local_tag = manager()->current_machine_tag();
+ // Create a free node and then dissassociate sessions so that it ends up
+ // unassociated.
+ manager()->local_tab_pool_.GetFreeTabNode(&changes);
+
+ // Update the tab_id of the node, so that it is considered a valid
+ // unassociated node otherwise it will be mistaken for a corrupted node and
+ // will be deleted before being added to the tab node pool.
+ sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
+ entity.mutable_session()->mutable_tab()->set_tab_id(1);
+ SyncData d(SyncData::CreateRemoteData(1, entity, base::Time()));
+ syncer::SyncDataList in(&d, &d + 1);
+ changes.clear();
+ TearDown();
+ SetUp();
+ InitWithSyncDataTakeOutput(in, &changes);
+ EXPECT_TRUE(FilterOutLocalHeaderCreation(&changes)->empty());
+}
+
+TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
+ syncer::SyncChangeList changes;
+ InitWithNoSyncData();
+
+ std::string local_tag = manager()->current_machine_tag();
+ int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
+ SyncData d(SyncData::CreateRemoteData(
+ 1, changes[0].sync_data().GetSpecifics(), base::Time()));
+ syncer::SyncDataList in(&d, &d + 1);
+ changes.clear();
+ TearDown();
+ SetUp();
+ InitWithSyncDataTakeOutput(in, &changes);
+ EXPECT_EQ(1U, FilterOutLocalHeaderCreation(&changes)->size());
+ EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[0].change_type());
+ EXPECT_EQ(TabNodePool2::TabIdToTag(local_tag, tab_node_id),
+ changes[0].sync_data().GetTag());
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/sessions2/tab_node_pool2.cc b/chrome/browser/sync/sessions2/tab_node_pool2.cc
index 70bc225..b0c4dc4b 100644
--- a/chrome/browser/sync/sessions2/tab_node_pool2.cc
+++ b/chrome/browser/sync/sessions2/tab_node_pool2.cc
@@ -33,10 +33,8 @@ std::string TabNodePool2::TabIdToTag(
return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
}
-void TabNodePool2::AddTabNode(int tab_node_id,
- const SessionID& tab_id) {
+void TabNodePool2::AddTabNode(int tab_node_id) {
DCHECK_GT(tab_node_id, kInvalidTabNodeID);
- DCHECK_GT(tab_id.id(), kInvalidTabID);
DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
unassociated_nodes_.insert(tab_node_id);
if (max_used_tab_node_id_ < tab_node_id)
@@ -156,7 +154,7 @@ SessionID::id_type TabNodePool2::GetTabIdFromTabNodeId(
return kInvalidTabID;
}
-void TabNodePool2::FreeUnassociatedTabNodes(
+void TabNodePool2::DeleteUnassociatedTabNodes(
syncer::SyncChangeList* append_changes) {
for (std::set<int>::iterator it = unassociated_nodes_.begin();
it != unassociated_nodes_.end();) {
diff --git a/chrome/browser/sync/sessions2/tab_node_pool2.h b/chrome/browser/sync/sessions2/tab_node_pool2.h
index db3c8b5..700dc10 100644
--- a/chrome/browser/sync/sessions2/tab_node_pool2.h
+++ b/chrome/browser/sync/sessions2/tab_node_pool2.h
@@ -90,7 +90,7 @@ class TabNodePool2 {
// Note: this should only be called when we discover tab sync nodes from
// previous sessions, not for freeing tab nodes we created through
// GetFreeTabNode (use FreeTabNode below for that).
- void AddTabNode(int tab_node_id, const SessionID& tab_id);
+ void AddTabNode(int tab_node_id);
// Returns the tab_id for |tab_node_id| if it is associated else returns
// kInvalidTabID.
@@ -107,7 +107,7 @@ class TabNodePool2 {
// |change_output| *must* be provided. It is the TabNodePool's link to
// the SyncChange pipeline that exists in the caller's context.
// See FreeTabNode for more detail.
- void FreeUnassociatedTabNodes(syncer::SyncChangeList* change_output);
+ void DeleteUnassociatedTabNodes(syncer::SyncChangeList* change_output);
// Clear tab pool.
void Clear();
diff --git a/chrome/browser/sync/sessions2/tab_node_pool2_unittest.cc b/chrome/browser/sync/sessions2/tab_node_pool2_unittest.cc
index 14942e2..d0a9d63 100644
--- a/chrome/browser/sync/sessions2/tab_node_pool2_unittest.cc
+++ b/chrome/browser/sync/sessions2/tab_node_pool2_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/sync/sessions2/tab_node_pool2.h"
+#include "base/logging.h"
#include "sync/api/sync_change.h"
#include "sync/protocol/session_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
@@ -17,65 +18,68 @@ class SyncTabNodePool2Test : public testing::Test {
int GetMaxUsedTabNodeId() const { return pool_.max_used_tab_node_id_; }
+ void AddFreeTabNodes(size_t size, const int node_ids[]);
+
TabNodePool2 pool_;
};
+void SyncTabNodePool2Test::AddFreeTabNodes(
+ size_t size, const int node_ids[]) {
+ for (size_t i = 0; i < size; ++i) {
+ pool_.free_nodes_pool_.insert(node_ids[i]);
+ }
+}
+
namespace {
TEST_F(SyncTabNodePool2Test, TabNodeIdIncreases) {
syncer::SyncChangeList changes;
// max_used_tab_node_ always increases.
- SessionID session_id;
- session_id.set_id(1);
- pool_.AddTabNode(10, session_id);
+ pool_.AddTabNode(10);
EXPECT_EQ(10, GetMaxUsedTabNodeId());
- session_id.set_id(2);
- pool_.AddTabNode(1, session_id);
+ pool_.AddTabNode(5);
EXPECT_EQ(10, GetMaxUsedTabNodeId());
- session_id.set_id(3);
- pool_.AddTabNode(1000, session_id);
+ pool_.AddTabNode(1000);
EXPECT_EQ(1000, GetMaxUsedTabNodeId());
- pool_.ReassociateTabNode(1000, 500);
-
+ pool_.ReassociateTabNode(1000, 1);
+ pool_.ReassociateTabNode(5, 2);
+ pool_.ReassociateTabNode(10, 3);
// Freeing a tab node does not change max_used_tab_node_id_.
pool_.FreeTabNode(1000, &changes);
EXPECT_TRUE(changes.empty());
- pool_.FreeUnassociatedTabNodes(&changes);
+ pool_.FreeTabNode(5, &changes);
+ EXPECT_TRUE(changes.empty());
+ pool_.FreeTabNode(10, &changes);
EXPECT_TRUE(changes.empty());
- EXPECT_EQ(1000, GetMaxUsedTabNodeId());
for (int i = 0; i < 3; ++i) {
pool_.AssociateTabNode(pool_.GetFreeTabNode(&changes), i + 1);
EXPECT_EQ(1000, GetMaxUsedTabNodeId());
}
- // We Free() nodes above, so GetFreeTabNode should not need to create.
EXPECT_TRUE(changes.empty());
-
EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+ EXPECT_TRUE(pool_.Empty());
}
TEST_F(SyncTabNodePool2Test, OldTabNodesAddAndRemove) {
syncer::SyncChangeList changes;
// VerifyOldTabNodes are added.
- // tab_node_id = 1, tab_id = 1
- SessionID session_id;
- session_id.set_id(1);
- pool_.AddTabNode(1, session_id);
- // tab_node_id = 2, tab_id = 2
- session_id.set_id(2);
- pool_.AddTabNode(2, session_id);
+ pool_.AddTabNode(1);
+ pool_.AddTabNode(2);
EXPECT_EQ(2u, pool_.Capacity());
EXPECT_TRUE(pool_.Empty());
EXPECT_TRUE(pool_.IsUnassociatedTabNode(1));
+ EXPECT_TRUE(pool_.IsUnassociatedTabNode(2));
pool_.ReassociateTabNode(1, 2);
EXPECT_TRUE(pool_.Empty());
- // Check FreeUnassociatedTabNodes returns the node to free node pool_.
- pool_.FreeUnassociatedTabNodes(&changes);
+ pool_.AssociateTabNode(2, 3);
+ EXPECT_FALSE(pool_.IsUnassociatedTabNode(1));
+ EXPECT_FALSE(pool_.IsUnassociatedTabNode(2));
+ pool_.FreeTabNode(2, &changes);
EXPECT_TRUE(changes.empty());
// 2 should be returned to free node pool_.
EXPECT_EQ(2u, pool_.Capacity());
// Should be able to free 1.
pool_.FreeTabNode(1, &changes);
- EXPECT_TRUE(changes.empty());
EXPECT_FALSE(pool_.Empty());
EXPECT_TRUE(pool_.Full());
EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
@@ -86,24 +90,24 @@ TEST_F(SyncTabNodePool2Test, OldTabNodesAddAndRemove) {
pool_.AssociateTabNode(2, 1);
EXPECT_TRUE(pool_.Empty());
EXPECT_FALSE(pool_.Full());
+ EXPECT_FALSE(pool_.Full());
}
TEST_F(SyncTabNodePool2Test, OldTabNodesReassociation) {
// VerifyOldTabNodes are reassociated correctly.
- SessionID session_id;
- session_id.set_id(1);
- pool_.AddTabNode(4, session_id);
- session_id.set_id(2);
- pool_.AddTabNode(5, session_id);
- session_id.set_id(3);
- pool_.AddTabNode(6, session_id);
+ pool_.AddTabNode(4);
+ pool_.AddTabNode(5);
+ pool_.AddTabNode(6);
EXPECT_EQ(3u, pool_.Capacity());
EXPECT_TRUE(pool_.Empty());
EXPECT_TRUE(pool_.IsUnassociatedTabNode(4));
pool_.ReassociateTabNode(4, 5);
+ pool_.AssociateTabNode(5, 6);
+ pool_.AssociateTabNode(6, 7);
// Free 5 and 6.
syncer::SyncChangeList changes;
- pool_.FreeUnassociatedTabNodes(&changes);
+ pool_.FreeTabNode(5, &changes);
+ pool_.FreeTabNode(6, &changes);
EXPECT_TRUE(changes.empty());
// 5 and 6 nodes should not be unassociated.
EXPECT_FALSE(pool_.IsUnassociatedTabNode(5));
@@ -138,14 +142,8 @@ TEST_F(SyncTabNodePool2Test, Init) {
TEST_F(SyncTabNodePool2Test, AddGet) {
syncer::SyncChangeList changes;
- SessionID session_id;
- session_id.set_id(1);
- pool_.AddTabNode(5, session_id);
- session_id.set_id(2);
- pool_.AddTabNode(10, session_id);
- pool_.FreeUnassociatedTabNodes(&changes);
- EXPECT_FALSE(pool_.Empty());
- EXPECT_TRUE(pool_.Full());
+ int free_nodes[] = {5, 10};
+ AddFreeTabNodes(2, free_nodes);
EXPECT_EQ(2U, pool_.Capacity());
EXPECT_EQ(5, pool_.GetFreeTabNode(&changes));
@@ -223,6 +221,42 @@ TEST_F(SyncTabNodePool2Test, GetFreeTabNodeCreate) {
EXPECT_EQ(0, specifics.tab_node_id());
}
+TEST_F(SyncTabNodePool2Test, TabPoolFreeNodeLimits) {
+ // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
+ // freeing the last node reduces the free node pool size to
+ // kFreeNodesLowWatermark.
+ syncer::SyncChangeList changes;
+ SessionID session_id;
+ std::vector<int> used_sync_ids;
+ for (size_t i = 1; i <= TabNodePool2::kFreeNodesHighWatermark + 1; ++i) {
+ session_id.set_id(i);
+ int sync_id = pool_.GetFreeTabNode(&changes);
+ pool_.AssociateTabNode(sync_id, i);
+ used_sync_ids.push_back(sync_id);
+ }
+
+ // Free all except one node.
+ int last_sync_id = used_sync_ids.back();
+ used_sync_ids.pop_back();
+
+ for (size_t i = 0; i < used_sync_ids.size(); ++i) {
+ pool_.FreeTabNode(used_sync_ids[i], &changes);
+ }
+
+ // Except one node all nodes should be in FreeNode pool.
+ EXPECT_FALSE(pool_.Full());
+ EXPECT_FALSE(pool_.Empty());
+ // Total capacity = 1 Associated Node + kFreeNodesHighWatermark free node.
+ EXPECT_EQ(TabNodePool2::kFreeNodesHighWatermark + 1, pool_.Capacity());
+
+ // Freeing the last sync node should drop the free nodes to
+ // kFreeNodesLowWatermark.
+ pool_.FreeTabNode(last_sync_id, &changes);
+ EXPECT_FALSE(pool_.Empty());
+ EXPECT_TRUE(pool_.Full());
+ EXPECT_EQ(TabNodePool2::kFreeNodesLowWatermark, pool_.Capacity());
+}
+
} // namespace
} // namespace browser_sync
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 2e7657f..28c49e7 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2248,6 +2248,8 @@
'browser/sync/profile_sync_service_observer.h',
'browser/sync/retry_verifier.cc',
'browser/sync/retry_verifier.h',
+ 'browser/sync/sessions2/sessions_sync_manager.cc',
+ 'browser/sync/sessions2/sessions_sync_manager.h',
'browser/sync/sessions2/tab_node_pool2.cc',
'browser/sync/sessions2/tab_node_pool2.h',
'browser/sync/sync_global_error.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 5fa3472..5bae30f 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1278,6 +1278,8 @@
'browser/sync/glue/non_ui_data_type_controller_unittest.cc',
'browser/sync/glue/search_engine_data_type_controller_unittest.cc',
'browser/sync/glue/session_model_associator_unittest.cc',
+ 'browser/sync/glue/session_sync_test_helper.cc',
+ 'browser/sync/glue/session_sync_test_helper.h',
'browser/sync/glue/shared_change_processor_mock.cc',
'browser/sync/glue/shared_change_processor_mock.h',
'browser/sync/glue/shared_change_processor_unittest.cc',
@@ -1302,6 +1304,7 @@
'browser/sync/profile_sync_service_unittest.cc',
'browser/sync/profile_sync_test_util.cc',
'browser/sync/profile_sync_test_util.h',
+ 'browser/sync/sessions2/sessions_sync_manager_unittest.cc',
'browser/sync/sessions2/tab_node_pool2_unittest.cc',
'browser/sync/sync_global_error_unittest.cc',
'browser/sync/sync_prefs_unittest.cc',