Home | History | Annotate | Download | only in sessions
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
      6 
      7 #include "base/strings/string_util.h"
      8 #include "chrome/browser/chrome_notification_types.h"
      9 #include "chrome/browser/sessions/session_id.h"
     10 #include "chrome/browser/sessions/session_tab_helper.h"
     11 #include "chrome/browser/sessions/session_types.h"
     12 #include "chrome/browser/sync/glue/device_info.h"
     13 #include "chrome/browser/sync/glue/session_sync_test_helper.h"
     14 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
     15 #include "chrome/browser/sync/glue/synced_window_delegate.h"
     16 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
     17 #include "chrome/browser/sync/sessions/sessions_util.h"
     18 #include "chrome/browser/sync/sessions/synced_window_delegates_getter.h"
     19 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/test/base/browser_with_test_window_test.h"
     22 #include "components/sessions/serialized_navigation_entry_test_helper.h"
     23 #include "content/public/browser/navigation_entry.h"
     24 #include "content/public/browser/notification_details.h"
     25 #include "content/public/browser/notification_service.h"
     26 #include "content/public/browser/notification_source.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "sync/api/attachments/attachment_id.h"
     29 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
     30 #include "sync/api/sync_error_factory_mock.h"
     31 #include "testing/gmock/include/gmock/gmock.h"
     32 #include "testing/gtest/include/gtest/gtest.h"
     33 
     34 using content::WebContents;
     35 using sessions::SerializedNavigationEntry;
     36 using sessions::SerializedNavigationEntryTestHelper;
     37 using syncer::SyncChange;
     38 using syncer::SyncData;
     39 
     40 namespace browser_sync {
     41 
     42 namespace {
     43 
     44 class SyncedWindowDelegateOverride : public SyncedWindowDelegate {
     45  public:
     46   explicit SyncedWindowDelegateOverride(SyncedWindowDelegate* wrapped)
     47       : wrapped_(wrapped) {
     48   }
     49   virtual ~SyncedWindowDelegateOverride() {}
     50 
     51   virtual bool HasWindow() const OVERRIDE {
     52     return wrapped_->HasWindow();
     53   }
     54 
     55   virtual SessionID::id_type GetSessionId() const OVERRIDE {
     56     return wrapped_->GetSessionId();
     57   }
     58 
     59   virtual int GetTabCount() const OVERRIDE {
     60     return wrapped_->GetTabCount();
     61   }
     62 
     63   virtual int GetActiveIndex() const OVERRIDE {
     64     return wrapped_->GetActiveIndex();
     65   }
     66 
     67   virtual bool IsApp() const OVERRIDE {
     68     return wrapped_->IsApp();
     69   }
     70 
     71   virtual bool IsTypeTabbed() const OVERRIDE {
     72     return wrapped_->IsTypeTabbed();
     73   }
     74 
     75   virtual bool IsTypePopup() const OVERRIDE {
     76     return wrapped_->IsTypePopup();
     77   }
     78 
     79   virtual bool IsTabPinned(const SyncedTabDelegate* tab) const OVERRIDE {
     80     return wrapped_->IsTabPinned(tab);
     81   }
     82 
     83   virtual SyncedTabDelegate* GetTabAt(int index) const OVERRIDE {
     84     if (tab_overrides_.find(index) != tab_overrides_.end())
     85       return tab_overrides_.find(index)->second;
     86 
     87     return wrapped_->GetTabAt(index);
     88   }
     89 
     90   void OverrideTabAt(int index,
     91                      SyncedTabDelegate* delegate,
     92                      SessionID::id_type tab_id) {
     93     tab_overrides_[index] = delegate;
     94     tab_id_overrides_[index] = tab_id;
     95   }
     96 
     97   virtual SessionID::id_type GetTabIdAt(int index) const OVERRIDE {
     98     if (tab_id_overrides_.find(index) != tab_id_overrides_.end())
     99       return tab_id_overrides_.find(index)->second;
    100     return wrapped_->GetTabIdAt(index);
    101   }
    102 
    103   virtual bool IsSessionRestoreInProgress() const OVERRIDE {
    104     return wrapped_->IsSessionRestoreInProgress();
    105   }
    106 
    107  private:
    108   std::map<int, SyncedTabDelegate*> tab_overrides_;
    109   std::map<int, SessionID::id_type> tab_id_overrides_;
    110   SyncedWindowDelegate* wrapped_;
    111 };
    112 
    113 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
    114  public:
    115   TestSyncedWindowDelegatesGetter(
    116       const std::set<SyncedWindowDelegate*>& delegates)
    117       : delegates_(delegates) {}
    118 
    119   virtual const std::set<SyncedWindowDelegate*> GetSyncedWindowDelegates()
    120       OVERRIDE {
    121     return delegates_;
    122   }
    123  private:
    124   const std::set<SyncedWindowDelegate*> delegates_;
    125 };
    126 
    127 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
    128  public:
    129   explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
    130       : output_(output) {}
    131   virtual syncer::SyncError ProcessSyncChanges(
    132       const tracked_objects::Location& from_here,
    133       const syncer::SyncChangeList& change_list) OVERRIDE {
    134     if (error_.IsSet()) {
    135       syncer::SyncError error = error_;
    136       error_ = syncer::SyncError();
    137       return error;
    138     }
    139 
    140     if (output_)
    141       output_->insert(output_->end(), change_list.begin(), change_list.end());
    142 
    143     return syncer::SyncError();
    144   }
    145 
    146   virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
    147       const OVERRIDE {
    148     return sync_data_to_return_;
    149   }
    150 
    151   void FailProcessSyncChangesWith(const syncer::SyncError& error) {
    152     error_ = error;
    153   }
    154 
    155   void SetSyncDataToReturn(const syncer::SyncDataList& data) {
    156     sync_data_to_return_ = data;
    157   }
    158 
    159  private:
    160   syncer::SyncError error_;
    161   syncer::SyncChangeList* output_;
    162   syncer::SyncDataList sync_data_to_return_;
    163 };
    164 
    165 syncer::SyncChange MakeRemoteChange(
    166     int64 id,
    167     const sync_pb::SessionSpecifics& specifics,
    168     SyncChange::SyncChangeType type) {
    169   sync_pb::EntitySpecifics entity;
    170   entity.mutable_session()->CopyFrom(specifics);
    171   return syncer::SyncChange(
    172       FROM_HERE,
    173       type,
    174       syncer::SyncData::CreateRemoteData(
    175           id,
    176           entity,
    177           base::Time(),
    178           syncer::AttachmentIdList(),
    179           syncer::AttachmentServiceProxyForTest::Create()));
    180 }
    181 
    182 void AddTabsToChangeList(
    183       const std::vector<sync_pb::SessionSpecifics>& batch,
    184       SyncChange::SyncChangeType type,
    185       syncer::SyncChangeList* change_list) {
    186   std::vector<sync_pb::SessionSpecifics>::const_iterator iter;
    187   for (iter = batch.begin();
    188        iter != batch.end(); ++iter) {
    189     sync_pb::EntitySpecifics entity;
    190     entity.mutable_session()->CopyFrom(*iter);
    191     change_list->push_back(syncer::SyncChange(
    192         FROM_HERE,
    193         type,
    194         syncer::SyncData::CreateRemoteData(
    195             iter->tab_node_id(),
    196             entity,
    197             base::Time(),
    198             syncer::AttachmentIdList(),
    199             syncer::AttachmentServiceProxyForTest::Create())));
    200   }
    201 }
    202 
    203 void AddTabsToSyncDataList(const std::vector<sync_pb::SessionSpecifics> tabs,
    204                            syncer::SyncDataList* list) {
    205   for (size_t i = 0; i < tabs.size(); i++) {
    206     sync_pb::EntitySpecifics entity;
    207     entity.mutable_session()->CopyFrom(tabs[i]);
    208     list->push_back(SyncData::CreateRemoteData(
    209         i + 2,
    210         entity,
    211         base::Time(),
    212         syncer::AttachmentIdList(),
    213         syncer::AttachmentServiceProxyForTest::Create()));
    214   }
    215 }
    216 
    217 class DummyRouter : public LocalSessionEventRouter {
    218  public:
    219   virtual ~DummyRouter() {}
    220   virtual void StartRoutingTo(LocalSessionEventHandler* handler) OVERRIDE {}
    221   virtual void Stop() OVERRIDE {}
    222 };
    223 
    224 scoped_ptr<LocalSessionEventRouter> NewDummyRouter() {
    225   return scoped_ptr<LocalSessionEventRouter>(new DummyRouter());
    226 }
    227 
    228 }  // namespace
    229 
    230 class SessionsSyncManagerTest
    231     : public BrowserWithTestWindowTest,
    232       public SessionsSyncManager::SyncInternalApiDelegate {
    233  public:
    234   SessionsSyncManagerTest() : test_processor_(NULL) {}
    235 
    236   virtual void SetUp() OVERRIDE {
    237     BrowserWithTestWindowTest::SetUp();
    238     browser_sync::NotificationServiceSessionsRouter* router(
    239         new browser_sync::NotificationServiceSessionsRouter(
    240             profile(), syncer::SyncableService::StartSyncFlare()));
    241     manager_.reset(new SessionsSyncManager(profile(), this,
    242       scoped_ptr<LocalSessionEventRouter>(router)));
    243   }
    244 
    245   virtual void TearDown() OVERRIDE {
    246     test_processor_ = NULL;
    247     helper()->Reset();
    248     manager_.reset();
    249     BrowserWithTestWindowTest::TearDown();
    250   }
    251 
    252   virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
    253     return scoped_ptr<DeviceInfo>(
    254         new DeviceInfo(GetLocalSyncCacheGUID(),
    255                        "Wayne Gretzky's Hacking Box",
    256                        "Chromium 10k",
    257                        "Chrome 10k",
    258                        sync_pb::SyncEnums_DeviceType_TYPE_LINUX));
    259   }
    260 
    261   virtual std::string GetLocalSyncCacheGUID() const OVERRIDE {
    262     return "cache_guid";
    263   }
    264 
    265   SessionsSyncManager* manager() { return manager_.get(); }
    266   SessionSyncTestHelper* helper() { return &helper_; }
    267 
    268   void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
    269                                   syncer::SyncChangeList* output) {
    270     test_processor_ = new TestSyncProcessorStub(output);
    271     syncer::SyncMergeResult result = manager_->MergeDataAndStartSyncing(
    272         syncer::SESSIONS, initial_data,
    273         scoped_ptr<syncer::SyncChangeProcessor>(test_processor_),
    274         scoped_ptr<syncer::SyncErrorFactory>(
    275             new syncer::SyncErrorFactoryMock()));
    276     EXPECT_FALSE(result.error().IsSet());
    277   }
    278 
    279   void InitWithNoSyncData() {
    280     InitWithSyncDataTakeOutput(syncer::SyncDataList(), NULL);
    281   }
    282 
    283   void TriggerProcessSyncChangesError() {
    284     test_processor_->FailProcessSyncChangesWith(syncer::SyncError(
    285         FROM_HERE, syncer::SyncError::DATATYPE_ERROR, "Error",
    286         syncer::SESSIONS));
    287   }
    288 
    289   void SetSyncData(const syncer::SyncDataList& data) {
    290      test_processor_->SetSyncDataToReturn(data);
    291   }
    292 
    293   syncer::SyncChangeList* FilterOutLocalHeaderChanges(
    294       syncer::SyncChangeList* list) {
    295     syncer::SyncChangeList::iterator it = list->begin();
    296     bool found = false;
    297     while (it != list->end()) {
    298       if (syncer::SyncDataLocal(it->sync_data()).GetTag() ==
    299           manager_->current_machine_tag()) {
    300         EXPECT_TRUE(SyncChange::ACTION_ADD == it->change_type() ||
    301                     SyncChange::ACTION_UPDATE == it->change_type());
    302         it = list->erase(it);
    303         found = true;
    304       } else {
    305         ++it;
    306       }
    307     }
    308     EXPECT_TRUE(found);
    309     return list;
    310   }
    311 
    312  private:
    313   scoped_ptr<SessionsSyncManager> manager_;
    314   SessionSyncTestHelper helper_;
    315   TestSyncProcessorStub* test_processor_;
    316 };
    317 
    318 // Test that the SyncSessionManager can properly fill in a SessionHeader.
    319 TEST_F(SessionsSyncManagerTest, PopulateSessionHeader) {
    320   sync_pb::SessionHeader header_s;
    321   header_s.set_client_name("Client 1");
    322   header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN);
    323 
    324   SyncedSession session;
    325   base::Time time = base::Time::Now();
    326   SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
    327       header_s, time, &session);
    328   ASSERT_EQ("Client 1", session.session_name);
    329   ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
    330   ASSERT_EQ(time, session.modified_time);
    331 }
    332 
    333 // Test translation between protobuf types and chrome session types.
    334 TEST_F(SessionsSyncManagerTest, PopulateSessionWindow) {
    335   sync_pb::SessionWindow window_s;
    336   window_s.add_tab(0);
    337   window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
    338   window_s.set_selected_tab_index(1);
    339 
    340   std::string tag = "tag";
    341   SyncedSession* session = manager()->session_tracker_.GetSession(tag);
    342   manager()->session_tracker_.PutWindowInSession(tag, 0);
    343   manager()->BuildSyncedSessionFromSpecifics(
    344       tag, window_s, base::Time(), session->windows[0]);
    345   ASSERT_EQ(1U, session->windows[0]->tabs.size());
    346   ASSERT_EQ(1, session->windows[0]->selected_tab_index);
    347   ASSERT_EQ(1, session->windows[0]->type);
    348   ASSERT_EQ(1U, manager()->session_tracker_.num_synced_sessions());
    349   ASSERT_EQ(1U,
    350             manager()->session_tracker_.num_synced_tabs(std::string("tag")));
    351 }
    352 
    353 namespace {
    354 
    355 class SyncedTabDelegateFake : public SyncedTabDelegate {
    356  public:
    357   SyncedTabDelegateFake() : current_entry_index_(0),
    358                             pending_entry_index_(-1),
    359                             is_supervised_(false),
    360                             sync_id_(-1),
    361                             blocked_navigations_(NULL) {}
    362   virtual ~SyncedTabDelegateFake() {}
    363 
    364   virtual int GetCurrentEntryIndex() const OVERRIDE {
    365     return current_entry_index_;
    366   }
    367   void set_current_entry_index(int i) {
    368     current_entry_index_ = i;
    369   }
    370 
    371   virtual content::NavigationEntry* GetEntryAtIndex(int i) const OVERRIDE {
    372     const int size = entries_.size();
    373     return (size < i + 1) ? NULL : entries_[i];
    374   }
    375 
    376   void AppendEntry(content::NavigationEntry* entry) {
    377     entries_.push_back(entry);
    378   }
    379 
    380   virtual int GetEntryCount() const OVERRIDE {
    381     return entries_.size();
    382   }
    383 
    384   virtual int GetPendingEntryIndex() const OVERRIDE {
    385     return pending_entry_index_;
    386   }
    387   void set_pending_entry_index(int i) {
    388     pending_entry_index_ = i;
    389   }
    390 
    391   virtual SessionID::id_type GetWindowId() const OVERRIDE {
    392     return SessionID::id_type();
    393   }
    394 
    395   virtual SessionID::id_type GetSessionId() const OVERRIDE {
    396     return SessionID::id_type();
    397   }
    398 
    399   virtual bool IsBeingDestroyed() const OVERRIDE { return false; }
    400   virtual Profile* profile() const OVERRIDE { return NULL; }
    401   virtual std::string GetExtensionAppId() const OVERRIDE {
    402     return std::string();
    403   }
    404   virtual content::NavigationEntry* GetPendingEntry() const OVERRIDE {
    405    return NULL;
    406   }
    407   virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE {
    408    return NULL;
    409   }
    410   virtual bool ProfileIsSupervised() const OVERRIDE {
    411    return is_supervised_;
    412   }
    413   void set_is_supervised(bool is_supervised) { is_supervised_ = is_supervised; }
    414   virtual const std::vector<const content::NavigationEntry*>*
    415       GetBlockedNavigations() const OVERRIDE {
    416     return blocked_navigations_;
    417   }
    418   void set_blocked_navigations(
    419       std::vector<const content::NavigationEntry*>* navs) {
    420     blocked_navigations_ = navs;
    421   }
    422   virtual bool IsPinned() const OVERRIDE {
    423    return false;
    424   }
    425   virtual bool HasWebContents() const OVERRIDE {
    426    return false;
    427   }
    428   virtual content::WebContents* GetWebContents() const OVERRIDE {
    429    return NULL;
    430   }
    431 
    432   // Session sync related methods.
    433   virtual int GetSyncId() const OVERRIDE {
    434    return sync_id_;
    435   }
    436   virtual void SetSyncId(int sync_id) OVERRIDE {
    437     sync_id_ = sync_id;
    438   }
    439 
    440   void reset() {
    441     current_entry_index_ = 0;
    442     pending_entry_index_ = -1;
    443     sync_id_ = -1;
    444     entries_.clear();
    445   }
    446 
    447  private:
    448    int current_entry_index_;
    449    int pending_entry_index_;
    450    bool is_supervised_;
    451    int sync_id_;
    452    std::vector<const content::NavigationEntry*>* blocked_navigations_;
    453    ScopedVector<content::NavigationEntry> entries_;
    454 };
    455 
    456 }  // namespace
    457 
    458 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
    459 // from ShouldSyncTab(..).
    460 TEST_F(SessionsSyncManagerTest, ValidTabs) {
    461   SyncedTabDelegateFake tab;
    462 
    463   // A null entry shouldn't crash.
    464   tab.AppendEntry(NULL);
    465   EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
    466   tab.reset();
    467 
    468   // A chrome:// entry isn't valid.
    469   content::NavigationEntry* entry(content::NavigationEntry::Create());
    470   entry->SetVirtualURL(GURL("chrome://preferences/"));
    471   tab.AppendEntry(entry);
    472   EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
    473 
    474 
    475   // A file:// entry isn't valid, even in addition to another entry.
    476   content::NavigationEntry* entry2(content::NavigationEntry::Create());
    477   entry2->SetVirtualURL(GURL("file://bla"));
    478   tab.AppendEntry(entry2);
    479   EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
    480 
    481   // Add a valid scheme entry to tab, making the tab valid.
    482   content::NavigationEntry* entry3(content::NavigationEntry::Create());
    483   entry3->SetVirtualURL(GURL("http://www.google.com"));
    484   tab.AppendEntry(entry3);
    485   EXPECT_FALSE(sessions_util::ShouldSyncTab(tab));
    486 }
    487 
    488 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
    489 // entry if the current entry is pending.
    490 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLPending) {
    491   SyncedTabDelegateFake tab;
    492   content::NavigationEntry* entry(content::NavigationEntry::Create());
    493   entry->SetVirtualURL(GURL("http://www.google.com"));
    494   tab.AppendEntry(entry);
    495   EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
    496 }
    497 
    498 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
    499 // entry if the current entry is non-pending.
    500 TEST_F(SessionsSyncManagerTest, GetCurrentVirtualURLNonPending) {
    501   SyncedTabDelegateFake tab;
    502   content::NavigationEntry* entry(content::NavigationEntry::Create());
    503   entry->SetVirtualURL(GURL("http://www.google.com"));
    504   tab.AppendEntry(entry);
    505   EXPECT_EQ(entry->GetVirtualURL(), manager()->GetCurrentVirtualURL(tab));
    506 }
    507 
    508 static const base::Time kTime1 = base::Time::FromInternalValue(100);
    509 static const base::Time kTime2 = base::Time::FromInternalValue(105);
    510 static const base::Time kTime3 = base::Time::FromInternalValue(110);
    511 static const base::Time kTime4 = base::Time::FromInternalValue(120);
    512 static const base::Time kTime5 = base::Time::FromInternalValue(130);
    513 
    514 // Populate the mock tab delegate with some data and navigation
    515 // entries and make sure that setting a SessionTab from it preserves
    516 // those entries (and clobbers any existing data).
    517 TEST_F(SessionsSyncManagerTest, SetSessionTabFromDelegate) {
    518   // Create a tab with three valid entries.
    519   SyncedTabDelegateFake tab;
    520   content::NavigationEntry* entry1(content::NavigationEntry::Create());
    521   entry1->SetVirtualURL(GURL("http://www.google.com"));
    522   entry1->SetTimestamp(kTime1);
    523   entry1->SetHttpStatusCode(200);
    524   content::NavigationEntry* entry2(content::NavigationEntry::Create());
    525   entry2->SetVirtualURL(GURL("http://www.noodle.com"));
    526   entry2->SetTimestamp(kTime2);
    527   entry2->SetHttpStatusCode(201);
    528   content::NavigationEntry* entry3(content::NavigationEntry::Create());
    529   entry3->SetVirtualURL(GURL("http://www.doodle.com"));
    530   entry3->SetTimestamp(kTime3);
    531   entry3->SetHttpStatusCode(202);
    532 
    533   tab.AppendEntry(entry1);
    534   tab.AppendEntry(entry2);
    535   tab.AppendEntry(entry3);
    536   tab.set_current_entry_index(2);
    537 
    538   SessionTab session_tab;
    539   session_tab.window_id.set_id(1);
    540   session_tab.tab_id.set_id(1);
    541   session_tab.tab_visual_index = 1;
    542   session_tab.current_navigation_index = 1;
    543   session_tab.pinned = true;
    544   session_tab.extension_app_id = "app id";
    545   session_tab.user_agent_override = "override";
    546   session_tab.timestamp = kTime5;
    547   session_tab.navigations.push_back(
    548       SerializedNavigationEntryTestHelper::CreateNavigation(
    549           "http://www.example.com", "Example"));
    550   session_tab.session_storage_persistent_id = "persistent id";
    551   manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
    552 
    553   EXPECT_EQ(0, session_tab.window_id.id());
    554   EXPECT_EQ(0, session_tab.tab_id.id());
    555   EXPECT_EQ(0, session_tab.tab_visual_index);
    556   EXPECT_EQ(2, session_tab.current_navigation_index);
    557   EXPECT_FALSE(session_tab.pinned);
    558   EXPECT_TRUE(session_tab.extension_app_id.empty());
    559   EXPECT_TRUE(session_tab.user_agent_override.empty());
    560   EXPECT_EQ(kTime4, session_tab.timestamp);
    561   ASSERT_EQ(3u, session_tab.navigations.size());
    562   EXPECT_EQ(entry1->GetVirtualURL(),
    563             session_tab.navigations[0].virtual_url());
    564   EXPECT_EQ(entry2->GetVirtualURL(),
    565             session_tab.navigations[1].virtual_url());
    566   EXPECT_EQ(entry3->GetVirtualURL(),
    567             session_tab.navigations[2].virtual_url());
    568   EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
    569   EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
    570   EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
    571   EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
    572   EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
    573   EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
    574   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    575             session_tab.navigations[0].blocked_state());
    576   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    577             session_tab.navigations[1].blocked_state());
    578   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    579             session_tab.navigations[2].blocked_state());
    580   EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
    581 }
    582 
    583 // Tests that for supervised users blocked navigations are recorded and marked
    584 // as such, while regular navigations are marked as allowed.
    585 TEST_F(SessionsSyncManagerTest, BlockedNavigations) {
    586   SyncedTabDelegateFake tab;
    587   content::NavigationEntry* entry1(content::NavigationEntry::Create());
    588   entry1->SetVirtualURL(GURL("http://www.google.com"));
    589   entry1->SetTimestamp(kTime1);
    590   tab.AppendEntry(entry1);
    591 
    592   content::NavigationEntry* entry2 = content::NavigationEntry::Create();
    593   entry2->SetVirtualURL(GURL("http://blocked.com/foo"));
    594   entry2->SetTimestamp(kTime2);
    595   content::NavigationEntry* entry3 = content::NavigationEntry::Create();
    596   entry3->SetVirtualURL(GURL("http://evil.com"));
    597   entry3->SetTimestamp(kTime3);
    598   ScopedVector<const content::NavigationEntry> blocked_navigations;
    599   blocked_navigations.push_back(entry2);
    600   blocked_navigations.push_back(entry3);
    601 
    602   tab.set_is_supervised(true);
    603   tab.set_blocked_navigations(&blocked_navigations.get());
    604 
    605   SessionTab session_tab;
    606   session_tab.window_id.set_id(1);
    607   session_tab.tab_id.set_id(1);
    608   session_tab.tab_visual_index = 1;
    609   session_tab.current_navigation_index = 1;
    610   session_tab.pinned = true;
    611   session_tab.extension_app_id = "app id";
    612   session_tab.user_agent_override = "override";
    613   session_tab.timestamp = kTime5;
    614   session_tab.navigations.push_back(
    615       SerializedNavigationEntryTestHelper::CreateNavigation(
    616           "http://www.example.com", "Example"));
    617   session_tab.session_storage_persistent_id = "persistent id";
    618   manager()->SetSessionTabFromDelegate(tab, kTime4, &session_tab);
    619 
    620   EXPECT_EQ(0, session_tab.window_id.id());
    621   EXPECT_EQ(0, session_tab.tab_id.id());
    622   EXPECT_EQ(0, session_tab.tab_visual_index);
    623   EXPECT_EQ(0, session_tab.current_navigation_index);
    624   EXPECT_FALSE(session_tab.pinned);
    625   EXPECT_TRUE(session_tab.extension_app_id.empty());
    626   EXPECT_TRUE(session_tab.user_agent_override.empty());
    627   EXPECT_EQ(kTime4, session_tab.timestamp);
    628   ASSERT_EQ(3u, session_tab.navigations.size());
    629   EXPECT_EQ(entry1->GetVirtualURL(),
    630             session_tab.navigations[0].virtual_url());
    631   EXPECT_EQ(entry2->GetVirtualURL(),
    632             session_tab.navigations[1].virtual_url());
    633   EXPECT_EQ(entry3->GetVirtualURL(),
    634             session_tab.navigations[2].virtual_url());
    635   EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
    636   EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
    637   EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
    638   EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
    639             session_tab.navigations[0].blocked_state());
    640   EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
    641             session_tab.navigations[1].blocked_state());
    642   EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
    643             session_tab.navigations[2].blocked_state());
    644   EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
    645 }
    646 
    647 // Tests that the local session header objects is created properly in
    648 // presence of no other session activity, once and only once.
    649 TEST_F(SessionsSyncManagerTest, MergeLocalSessionNoTabs) {
    650   syncer::SyncChangeList out;
    651   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
    652   EXPECT_FALSE(manager()->current_machine_tag().empty());
    653 
    654   EXPECT_EQ(2U, out.size());
    655   EXPECT_TRUE(out[0].IsValid());
    656   EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
    657   const SyncData data(out[0].sync_data());
    658   EXPECT_EQ(manager()->current_machine_tag(),
    659             syncer::SyncDataLocal(data).GetTag());
    660   const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
    661   EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
    662   EXPECT_TRUE(specifics.has_header());
    663   const sync_pb::SessionHeader& header_s = specifics.header();
    664   EXPECT_TRUE(header_s.has_device_type());
    665   EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
    666   EXPECT_EQ(0, header_s.window_size());
    667 
    668   EXPECT_TRUE(out[1].IsValid());
    669   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
    670   const SyncData data_2(out[1].sync_data());
    671   EXPECT_EQ(manager()->current_machine_tag(),
    672             syncer::SyncDataLocal(data_2).GetTag());
    673   const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
    674   EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
    675   EXPECT_TRUE(specifics2.has_header());
    676   const sync_pb::SessionHeader& header_s2 = specifics2.header();
    677   EXPECT_EQ(0, header_s2.window_size());
    678 
    679   // Now take that header node and feed it in as input.
    680   SyncData d(SyncData::CreateRemoteData(
    681       1,
    682       data.GetSpecifics(),
    683       base::Time(),
    684       syncer::AttachmentIdList(),
    685       syncer::AttachmentServiceProxyForTest::Create()));
    686   syncer::SyncDataList in(&d, &d + 1);
    687   out.clear();
    688   SessionsSyncManager manager2(profile(), this, NewDummyRouter());
    689   syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
    690       syncer::SESSIONS, in,
    691       scoped_ptr<syncer::SyncChangeProcessor>(
    692           new TestSyncProcessorStub(&out)),
    693       scoped_ptr<syncer::SyncErrorFactory>(
    694           new syncer::SyncErrorFactoryMock()));
    695   ASSERT_FALSE(result.error().IsSet());
    696 
    697   EXPECT_EQ(1U, out.size());
    698   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
    699   EXPECT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
    700 }
    701 
    702 // Ensure model association associates the pre-existing tabs.
    703 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
    704   AddTab(browser(), GURL("http://foo1"));
    705   NavigateAndCommitActiveTab(GURL("http://foo2"));
    706   AddTab(browser(), GURL("http://bar1"));
    707   NavigateAndCommitActiveTab(GURL("http://bar2"));
    708   AddTab(browser(), GURL("http://baz1"));
    709   NavigateAndCommitActiveTab(GURL("http://baz2"));
    710   const int kRestoredTabId = 1337;
    711   const int kNewTabId = 2468;
    712 
    713   syncer::SyncDataList in;
    714   syncer::SyncChangeList out;
    715   InitWithSyncDataTakeOutput(in, &out);
    716 
    717   // Should be one header add, 3 tab add/update pairs, one header update.
    718   ASSERT_EQ(8U, out.size());
    719 
    720   // For input, we set up:
    721   // * one "normal" fully loaded tab
    722   // * one "frozen" tab with no WebContents and a tab_id change
    723   // * one "frozen" tab with no WebContents and no tab_id change
    724   SyncData t0(SyncData::CreateRemoteData(
    725       1,
    726       out[2].sync_data().GetSpecifics(),
    727       base::Time(),
    728       syncer::AttachmentIdList(),
    729       syncer::AttachmentServiceProxyForTest::Create()));
    730   sync_pb::EntitySpecifics entity(out[4].sync_data().GetSpecifics());
    731   entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
    732   SyncData t1(SyncData::CreateRemoteData(
    733       2,
    734       entity,
    735       base::Time(),
    736       syncer::AttachmentIdList(),
    737       syncer::AttachmentServiceProxyForTest::Create()));
    738   SyncData t2(SyncData::CreateRemoteData(
    739       3,
    740       out[6].sync_data().GetSpecifics(),
    741       base::Time(),
    742       syncer::AttachmentIdList(),
    743       syncer::AttachmentServiceProxyForTest::Create()));
    744   in.push_back(t0);
    745   in.push_back(t1);
    746   in.push_back(t2);
    747   out.clear();
    748   manager()->StopSyncing(syncer::SESSIONS);
    749 
    750   const std::set<SyncedWindowDelegate*> windows(
    751       SyncedWindowDelegate::GetSyncedWindowDelegates());
    752   ASSERT_EQ(1U, windows.size());
    753   SyncedTabDelegateFake t1_override, t2_override;
    754   t1_override.SetSyncId(1);  // No WebContents by default.
    755   t2_override.SetSyncId(2);  // No WebContents by default.
    756   SyncedWindowDelegateOverride window_override(*windows.begin());
    757   window_override.OverrideTabAt(1, &t1_override, kNewTabId);
    758   window_override.OverrideTabAt(2, &t2_override,
    759                                 t2.GetSpecifics().session().tab().tab_id());
    760   std::set<SyncedWindowDelegate*> delegates;
    761   delegates.insert(&window_override);
    762   scoped_ptr<TestSyncedWindowDelegatesGetter> getter(
    763       new TestSyncedWindowDelegatesGetter(delegates));
    764   manager()->synced_window_getter_.reset(getter.release());
    765 
    766   syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
    767       syncer::SESSIONS, in,
    768       scoped_ptr<syncer::SyncChangeProcessor>(
    769           new TestSyncProcessorStub(&out)),
    770       scoped_ptr<syncer::SyncErrorFactory>(
    771           new syncer::SyncErrorFactoryMock()));
    772 
    773   // There should be two changes, one for the fully associated tab, and
    774   // one for the tab_id update to t1.  t2 shouldn't need to be updated.
    775   ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
    776   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
    777   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
    778   EXPECT_EQ(kNewTabId,
    779             out[1].sync_data().GetSpecifics().session().tab().tab_id());
    780 
    781   // Verify TabLinks.
    782   SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
    783   ASSERT_EQ(3U, tab_map.size());
    784   int t2_tab_id = t2.GetSpecifics().session().tab().tab_id();
    785   EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
    786   EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
    787   int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
    788   EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
    789   // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
    790   // from here (using an override similar to above) to return a new tab id
    791   // and verify that we don't see any node creations in the SyncChangeProcessor
    792   // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
    793 }
    794 
    795 // Tests MergeDataAndStartSyncing with sync data but no local data.
    796 TEST_F(SessionsSyncManagerTest, MergeWithInitialForeignSession) {
    797   std::string tag = "tag1";
    798 
    799   SessionID::id_type n1[] = {5, 10, 13, 17};
    800   std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
    801   std::vector<sync_pb::SessionSpecifics> tabs1;
    802   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
    803       tag, tab_list1, &tabs1));
    804   // Add a second window.
    805   SessionID::id_type n2[] = {7, 15, 18, 20};
    806   std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
    807   helper()->AddWindowSpecifics(1, tab_list2, &meta);
    808 
    809   // Set up initial data.
    810   syncer::SyncDataList initial_data;
    811   sync_pb::EntitySpecifics entity;
    812   entity.mutable_session()->CopyFrom(meta);
    813   initial_data.push_back(SyncData::CreateRemoteData(
    814       1,
    815       entity,
    816       base::Time(),
    817       syncer::AttachmentIdList(),
    818       syncer::AttachmentServiceProxyForTest::Create()));
    819   AddTabsToSyncDataList(tabs1, &initial_data);
    820 
    821   for (size_t i = 0; i < tab_list2.size(); ++i) {
    822     sync_pb::EntitySpecifics entity;
    823     helper()->BuildTabSpecifics(tag, 0, tab_list2[i],
    824                                 entity.mutable_session());
    825     initial_data.push_back(SyncData::CreateRemoteData(
    826         i + 10,
    827         entity,
    828         base::Time(),
    829         syncer::AttachmentIdList(),
    830         syncer::AttachmentServiceProxyForTest::Create()));
    831   }
    832 
    833   syncer::SyncChangeList output;
    834   InitWithSyncDataTakeOutput(initial_data, &output);
    835   EXPECT_TRUE(FilterOutLocalHeaderChanges(&output)->empty());
    836 
    837   std::vector<const SyncedSession*> foreign_sessions;
    838   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
    839   ASSERT_EQ(1U, foreign_sessions.size());
    840   std::vector<std::vector<SessionID::id_type> > session_reference;
    841   session_reference.push_back(tab_list1);
    842   session_reference.push_back(tab_list2);
    843   helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    844 }
    845 
    846 // This is a combination of MergeWithInitialForeignSession and
    847 // MergeLocalSessionExistingTabs. We repeat some checks performed in each of
    848 // those tests to ensure the common mixed scenario works.
    849 TEST_F(SessionsSyncManagerTest, MergeWithLocalAndForeignTabs) {
    850   // Local.
    851   AddTab(browser(), GURL("http://foo1"));
    852   NavigateAndCommitActiveTab(GURL("http://foo2"));
    853 
    854   // Foreign.
    855   std::string tag = "tag1";
    856   SessionID::id_type n1[] = {5, 10, 13, 17};
    857   std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
    858   std::vector<sync_pb::SessionSpecifics> tabs1;
    859   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
    860       tag, tab_list1, &tabs1));
    861   syncer::SyncDataList foreign_data;
    862   sync_pb::EntitySpecifics entity;
    863   entity.mutable_session()->CopyFrom(meta);
    864   foreign_data.push_back(SyncData::CreateRemoteData(
    865       1,
    866       entity,
    867       base::Time(),
    868       syncer::AttachmentIdList(),
    869       syncer::AttachmentServiceProxyForTest::Create()));
    870   AddTabsToSyncDataList(tabs1, &foreign_data);
    871 
    872   syncer::SyncChangeList output;
    873   InitWithSyncDataTakeOutput(foreign_data, &output);
    874   ASSERT_EQ(4U, output.size());
    875 
    876   // Verify the local header.
    877   EXPECT_TRUE(output[0].IsValid());
    878   EXPECT_EQ(SyncChange::ACTION_ADD, output[0].change_type());
    879   const SyncData data(output[0].sync_data());
    880   EXPECT_EQ(manager()->current_machine_tag(),
    881             syncer::SyncDataLocal(data).GetTag());
    882   const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
    883   EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
    884   EXPECT_TRUE(specifics.has_header());
    885   const sync_pb::SessionHeader& header_s = specifics.header();
    886   EXPECT_TRUE(header_s.has_device_type());
    887   EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
    888   EXPECT_EQ(0, header_s.window_size());
    889 
    890   // Verify the tab node creations and updates with content.
    891   for (int i = 1; i < 3; i++) {
    892     EXPECT_TRUE(output[i].IsValid());
    893     const SyncData data(output[i].sync_data());
    894     EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
    895                                 manager()->current_machine_tag(), true));
    896     const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
    897     EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
    898   }
    899   EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
    900   EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
    901   EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
    902 
    903   // Verify the header was updated to reflect window state.
    904   EXPECT_TRUE(output[3].IsValid());
    905   EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
    906   const SyncData data_2(output[3].sync_data());
    907   EXPECT_EQ(manager()->current_machine_tag(),
    908             syncer::SyncDataLocal(data_2).GetTag());
    909   const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
    910   EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
    911   EXPECT_TRUE(specifics2.has_header());
    912   const sync_pb::SessionHeader& header_s2 = specifics2.header();
    913   EXPECT_EQ(1, header_s2.window_size());
    914 
    915   // Verify foreign data.
    916   std::vector<const SyncedSession*> foreign_sessions;
    917   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
    918   std::vector<std::vector<SessionID::id_type> > session_reference;
    919   session_reference.push_back(tab_list1);
    920   helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
    921   // There should be one and only one foreign session. If VerifySyncedSession
    922   // was successful above this EXPECT call ensures the local session didn't
    923   // get mistakenly added to foreign tracking (Similar to ExistingTabs test).
    924   EXPECT_EQ(1U, foreign_sessions.size());
    925 }
    926 
    927 // Tests the common scenario.  Merge with both local and foreign session data
    928 // followed by updates flowing from sync and local.
    929 TEST_F(SessionsSyncManagerTest, UpdatesAfterMixedMerge) {
    930   // Add local and foreign data.
    931   AddTab(browser(), GURL("http://foo1"));
    932   NavigateAndCommitActiveTab(GURL("http://foo2"));
    933 
    934   std::string tag1 = "tag1";
    935   syncer::SyncDataList foreign_data1;
    936   std::vector<std::vector<SessionID::id_type> > meta1_reference;
    937   sync_pb::SessionSpecifics meta1;
    938 
    939   SessionID::id_type n1[] = {5, 10, 13, 17};
    940   std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
    941   meta1_reference.push_back(tab_list1);
    942   std::vector<sync_pb::SessionSpecifics> tabs1;
    943   meta1 = helper()->BuildForeignSession(tag1, tab_list1, &tabs1);
    944   sync_pb::EntitySpecifics entity;
    945   entity.mutable_session()->CopyFrom(meta1);
    946   foreign_data1.push_back(SyncData::CreateRemoteData(
    947       1,
    948       entity,
    949       base::Time(),
    950       syncer::AttachmentIdList(),
    951       syncer::AttachmentServiceProxyForTest::Create()));
    952   AddTabsToSyncDataList(tabs1, &foreign_data1);
    953 
    954   syncer::SyncChangeList output1;
    955   InitWithSyncDataTakeOutput(foreign_data1, &output1);
    956   ASSERT_EQ(4U, output1.size());
    957 
    958   // Add a second window to the foreign session.
    959   // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
    960   SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
    961   std::vector<SessionID::id_type> tab_list2(
    962       tab_nums2, tab_nums2 + arraysize(tab_nums2));
    963   meta1_reference.push_back(tab_list2);
    964   helper()->AddWindowSpecifics(1, tab_list2, &meta1);
    965   std::vector<sync_pb::SessionSpecifics> tabs2;
    966   tabs2.resize(tab_list2.size());
    967   for (size_t i = 0; i < tab_list2.size(); ++i) {
    968     helper()->BuildTabSpecifics(tag1, 0, tab_list2[i], &tabs2[i]);
    969   }
    970 
    971   syncer::SyncChangeList changes;
    972   changes.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
    973   AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
    974   manager()->ProcessSyncChanges(FROM_HERE, changes);
    975   changes.clear();
    976 
    977   // Check that the foreign session was associated and retrieve the data.
    978   std::vector<const SyncedSession*> foreign_sessions;
    979   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
    980   ASSERT_EQ(1U, foreign_sessions.size());
    981   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
    982   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
    983   helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
    984 
    985   // Add a new foreign session.
    986   std::string tag2 = "tag2";
    987   SessionID::id_type n2[] = {107, 115};
    988   std::vector<SessionID::id_type> tag2_tab_list(n2, n2 + arraysize(n2));
    989   std::vector<sync_pb::SessionSpecifics> tag2_tabs;
    990   sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
    991       tag2, tag2_tab_list, &tag2_tabs));
    992   changes.push_back(MakeRemoteChange(100, meta2, SyncChange::ACTION_ADD));
    993   AddTabsToChangeList(tag2_tabs, SyncChange::ACTION_ADD, &changes);
    994 
    995   manager()->ProcessSyncChanges(FROM_HERE, changes);
    996   changes.clear();
    997 
    998   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
    999   std::vector<std::vector<SessionID::id_type> > meta2_reference;
   1000   meta2_reference.push_back(tag2_tab_list);
   1001   ASSERT_EQ(2U, foreign_sessions.size());
   1002   ASSERT_EQ(2U, foreign_sessions[1]->windows.find(0)->second->tabs.size());
   1003   helper()->VerifySyncedSession(tag2, meta2_reference, *(foreign_sessions[1]));
   1004   foreign_sessions.clear();
   1005 
   1006   // Remove a tab from a window.
   1007   meta1_reference[0].pop_back();
   1008   tab_list1.pop_back();
   1009   sync_pb::SessionWindow* win = meta1.mutable_header()->mutable_window(0);
   1010   win->clear_tab();
   1011   for (std::vector<int>::const_iterator iter = tab_list1.begin();
   1012        iter != tab_list1.end(); ++iter) {
   1013     win->add_tab(*iter);
   1014   }
   1015   syncer::SyncChangeList removal;
   1016   removal.push_back(MakeRemoteChange(1, meta1, SyncChange::ACTION_UPDATE));
   1017   AddTabsToChangeList(tabs1, SyncChange::ACTION_UPDATE, &removal);
   1018   manager()->ProcessSyncChanges(FROM_HERE, removal);
   1019 
   1020   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1021   ASSERT_EQ(2U, foreign_sessions.size());
   1022   ASSERT_EQ(3U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
   1023   helper()->VerifySyncedSession(tag1, meta1_reference, *(foreign_sessions[0]));
   1024 }
   1025 
   1026 // Tests that this SyncSessionManager knows how to delete foreign sessions
   1027 // if it wants to.
   1028 TEST_F(SessionsSyncManagerTest, DeleteForeignSession) {
   1029   InitWithNoSyncData();
   1030   std::string tag = "tag1";
   1031   syncer::SyncChangeList changes;
   1032 
   1033   std::vector<const SyncedSession*> foreign_sessions;
   1034   ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
   1035   manager()->DeleteForeignSessionInternal(tag, &changes);
   1036   ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
   1037   EXPECT_TRUE(changes.empty());
   1038 
   1039    // Fill an instance of session specifics with a foreign session's data.
   1040   std::vector<sync_pb::SessionSpecifics> tabs;
   1041   SessionID::id_type n1[] = {5, 10, 13, 17};
   1042   std::vector<SessionID::id_type> tab_nums1(n1, n1 + arraysize(n1));
   1043   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1044       tag, tab_nums1, &tabs));
   1045 
   1046   // Update associator with the session's meta node, window, and tabs.
   1047   manager()->UpdateTrackerWithForeignSession(meta, base::Time());
   1048   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
   1049        iter != tabs.end(); ++iter) {
   1050     manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
   1051   }
   1052   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1053   ASSERT_EQ(1U, foreign_sessions.size());
   1054 
   1055   // Now delete the foreign session.
   1056   manager()->DeleteForeignSessionInternal(tag, &changes);
   1057   EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
   1058 
   1059   EXPECT_EQ(5U, changes.size());
   1060   std::set<std::string> expected_tags(&tag, &tag + 1);
   1061   for (int i = 0; i < 5; i++)
   1062     expected_tags.insert(TabNodePool::TabIdToTag(tag, i));
   1063 
   1064   for (int i = 0; i < 5; i++) {
   1065     SCOPED_TRACE(changes[i].ToString());
   1066     EXPECT_TRUE(changes[i].IsValid());
   1067     EXPECT_EQ(SyncChange::ACTION_DELETE, changes[i].change_type());
   1068     EXPECT_TRUE(changes[i].sync_data().IsValid());
   1069     EXPECT_EQ(1U,
   1070               expected_tags.erase(
   1071                   syncer::SyncDataLocal(changes[i].sync_data()).GetTag()));
   1072   }
   1073 }
   1074 
   1075 // Write a foreign session to a node, with the tabs arriving first, and then
   1076 // retrieve it.
   1077 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeTabsFirst) {
   1078   InitWithNoSyncData();
   1079 
   1080   // Fill an instance of session specifics with a foreign session's data.
   1081   std::string tag = "tag1";
   1082   SessionID::id_type nums1[] = {5, 10, 13, 17};
   1083   std::vector<sync_pb::SessionSpecifics> tabs1;
   1084   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
   1085   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1086       tag, tab_list1, &tabs1));
   1087 
   1088   syncer::SyncChangeList adds;
   1089   // Add tabs for first window, then the meta node.
   1090   AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &adds);
   1091   adds.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
   1092   manager()->ProcessSyncChanges(FROM_HERE, adds);
   1093 
   1094   // Check that the foreign session was associated and retrieve the data.
   1095   std::vector<const SyncedSession*> foreign_sessions;
   1096   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1097   ASSERT_EQ(1U, foreign_sessions.size());
   1098   std::vector<std::vector<SessionID::id_type> > session_reference;
   1099   session_reference.push_back(tab_list1);
   1100   helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
   1101 }
   1102 
   1103 // Write a foreign session to a node with some tabs that never arrive.
   1104 TEST_F(SessionsSyncManagerTest, WriteForeignSessionToNodeMissingTabs) {
   1105   InitWithNoSyncData();
   1106 
   1107   // Fill an instance of session specifics with a foreign session's data.
   1108   std::string tag = "tag1";
   1109   SessionID::id_type nums1[] = {5, 10, 13, 17};
   1110   std::vector<sync_pb::SessionSpecifics> tabs1;
   1111   std::vector<SessionID::id_type> tab_list1 (nums1, nums1 + arraysize(nums1));
   1112   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1113       tag, tab_list1, &tabs1));
   1114   // Add a second window, but this time only create two tab nodes, despite the
   1115   // window expecting four tabs.
   1116   SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
   1117   std::vector<SessionID::id_type> tab_list2(
   1118       tab_nums2, tab_nums2 + arraysize(tab_nums2));
   1119   helper()->AddWindowSpecifics(1, tab_list2, &meta);
   1120   std::vector<sync_pb::SessionSpecifics> tabs2;
   1121   tabs2.resize(2);
   1122   for (size_t i = 0; i < 2; ++i) {
   1123     helper()->BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
   1124   }
   1125 
   1126   syncer::SyncChangeList changes;
   1127   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
   1128   AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
   1129   AddTabsToChangeList(tabs2, SyncChange::ACTION_ADD, &changes);
   1130   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1131   changes.clear();
   1132 
   1133   // Check that the foreign session was associated and retrieve the data.
   1134   std::vector<const SyncedSession*> foreign_sessions;
   1135   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1136   ASSERT_EQ(1U, foreign_sessions.size());
   1137   ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
   1138   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
   1139   ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
   1140 
   1141   // Close the second window.
   1142   meta.mutable_header()->clear_window();
   1143   helper()->AddWindowSpecifics(0, tab_list1, &meta);
   1144   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_UPDATE));
   1145   // Update associator with the session's meta node containing one window.
   1146   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1147 
   1148   // Check that the foreign session was associated and retrieve the data.
   1149   foreign_sessions.clear();
   1150   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1151   ASSERT_EQ(1U, foreign_sessions.size());
   1152   ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
   1153   std::vector<std::vector<SessionID::id_type> > session_reference;
   1154   session_reference.push_back(tab_list1);
   1155   helper()->VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
   1156 }
   1157 
   1158 // Tests that the SessionsSyncManager can handle a remote client deleting
   1159 // sync nodes that belong to this local session.
   1160 TEST_F(SessionsSyncManagerTest, ProcessRemoteDeleteOfLocalSession) {
   1161   syncer::SyncChangeList out;
   1162   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
   1163   ASSERT_EQ(2U, out.size());
   1164   sync_pb::EntitySpecifics entity(out[0].sync_data().GetSpecifics());
   1165   SyncData d(SyncData::CreateRemoteData(
   1166       1,
   1167       entity,
   1168       base::Time(),
   1169       syncer::AttachmentIdList(),
   1170       syncer::AttachmentServiceProxyForTest::Create()));
   1171   SetSyncData(syncer::SyncDataList(&d, &d + 1));
   1172   out.clear();
   1173 
   1174   syncer::SyncChangeList changes;
   1175   changes.push_back(
   1176       MakeRemoteChange(1, entity.session(), SyncChange::ACTION_DELETE));
   1177   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1178   EXPECT_TRUE(manager()->local_tab_pool_out_of_sync_);
   1179   EXPECT_TRUE(out.empty());  // ChangeProcessor shouldn't see any activity.
   1180 
   1181   // This should trigger repair of the TabNodePool.
   1182   const GURL foo1("http://foo/1");
   1183   AddTab(browser(), foo1);
   1184   EXPECT_FALSE(manager()->local_tab_pool_out_of_sync_);
   1185 
   1186   // AddTab triggers two notifications, one for the tab insertion and one for
   1187   // committing the NavigationEntry. The first notification results in a tab
   1188   // we don't associate although we do update the header node.  The second
   1189   // notification triggers association of the tab, and the subsequent window
   1190   // update.  So we should see 4 changes at the SyncChangeProcessor.
   1191   ASSERT_EQ(4U, out.size());
   1192 
   1193   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
   1194   ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
   1195   EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
   1196   int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
   1197   EXPECT_EQ(TabNodePool::TabIdToTag(
   1198                 manager()->current_machine_tag(), tab_node_id),
   1199             syncer::SyncDataLocal(out[1].sync_data()).GetTag());
   1200   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
   1201   ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
   1202   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
   1203   ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
   1204 
   1205   // Verify the actual content.
   1206   const sync_pb::SessionHeader& session_header =
   1207       out[3].sync_data().GetSpecifics().session().header();
   1208   ASSERT_EQ(1, session_header.window_size());
   1209   EXPECT_EQ(1, session_header.window(0).tab_size());
   1210   const sync_pb::SessionTab& tab1 =
   1211       out[2].sync_data().GetSpecifics().session().tab();
   1212   ASSERT_EQ(1, tab1.navigation_size());
   1213   EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
   1214 
   1215   // Verify TabNodePool integrity.
   1216   EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
   1217   EXPECT_TRUE(manager()->local_tab_pool_.Empty());
   1218 
   1219   // Verify TabLinks.
   1220   SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
   1221   ASSERT_EQ(1U, tab_map.size());
   1222   int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
   1223   EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
   1224 }
   1225 
   1226 // Test that receiving a session delete from sync removes the session
   1227 // from tracking.
   1228 TEST_F(SessionsSyncManagerTest, ProcessForeignDelete) {
   1229   InitWithNoSyncData();
   1230   SessionID::id_type n[] = {5};
   1231   std::vector<sync_pb::SessionSpecifics> tabs1;
   1232   std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
   1233   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1234       "tag1", tab_list, &tabs1));
   1235 
   1236   syncer::SyncChangeList changes;
   1237   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
   1238   AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
   1239   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1240 
   1241   std::vector<const SyncedSession*> foreign_sessions;
   1242   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1243   ASSERT_EQ(1U, foreign_sessions.size());
   1244 
   1245   changes.clear();
   1246   foreign_sessions.clear();
   1247   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
   1248   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1249 
   1250   EXPECT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
   1251 }
   1252 
   1253 // TODO(shashishekhar): "Move this to TabNodePool unittests."
   1254 TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
   1255   syncer::SyncChangeList changes;
   1256   InitWithNoSyncData();
   1257 
   1258   std::string local_tag = manager()->current_machine_tag();
   1259   // Create a free node and then dissassociate sessions so that it ends up
   1260   // unassociated.
   1261   manager()->local_tab_pool_.GetFreeTabNode(&changes);
   1262 
   1263   // Update the tab_id of the node, so that it is considered a valid
   1264   // unassociated node otherwise it will be mistaken for a corrupted node and
   1265   // will be deleted before being added to the tab node pool.
   1266   sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
   1267   entity.mutable_session()->mutable_tab()->set_tab_id(1);
   1268   SyncData d(SyncData::CreateRemoteData(
   1269       1,
   1270       entity,
   1271       base::Time(),
   1272       syncer::AttachmentIdList(),
   1273       syncer::AttachmentServiceProxyForTest::Create()));
   1274   syncer::SyncDataList in(&d, &d + 1);
   1275   changes.clear();
   1276   SessionsSyncManager manager2(profile(), this, NewDummyRouter());
   1277   syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
   1278       syncer::SESSIONS, in,
   1279       scoped_ptr<syncer::SyncChangeProcessor>(
   1280           new TestSyncProcessorStub(&changes)),
   1281       scoped_ptr<syncer::SyncErrorFactory>(
   1282           new syncer::SyncErrorFactoryMock()));
   1283   ASSERT_FALSE(result.error().IsSet());
   1284   EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
   1285 }
   1286 
   1287 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
   1288   syncer::SyncChangeList changes;
   1289   InitWithNoSyncData();
   1290 
   1291   std::string local_tag = manager()->current_machine_tag();
   1292   int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
   1293   SyncData d(SyncData::CreateRemoteData(
   1294       1,
   1295       changes[0].sync_data().GetSpecifics(),
   1296       base::Time(),
   1297       syncer::AttachmentIdList(),
   1298       syncer::AttachmentServiceProxyForTest::Create()));
   1299   syncer::SyncDataList in(&d, &d + 1);
   1300   changes.clear();
   1301   TearDown();
   1302   SetUp();
   1303   InitWithSyncDataTakeOutput(in, &changes);
   1304   EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
   1305   EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
   1306   EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id),
   1307             syncer::SyncDataLocal(changes[0].sync_data()).GetTag());
   1308 }
   1309 
   1310 // Test that things work if a tab is initially ignored.
   1311 TEST_F(SessionsSyncManagerTest, AssociateWindowsDontReloadTabs) {
   1312   syncer::SyncChangeList out;
   1313   // Go to a URL that is ignored by session syncing.
   1314   AddTab(browser(), GURL("chrome://preferences/"));
   1315   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
   1316   ASSERT_EQ(2U, out.size());  // Header add and update.
   1317   EXPECT_EQ(
   1318       0,
   1319       out[1].sync_data().GetSpecifics().session().header().window_size());
   1320   out.clear();
   1321 
   1322   // Go to a sync-interesting URL.
   1323   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1324 
   1325   EXPECT_EQ(3U, out.size());  // Tab add, update, and header update.
   1326 
   1327   EXPECT_TRUE(
   1328       StartsWithASCII(syncer::SyncDataLocal(out[0].sync_data()).GetTag(),
   1329                       manager()->current_machine_tag(),
   1330                       true));
   1331   EXPECT_EQ(manager()->current_machine_tag(),
   1332             out[0].sync_data().GetSpecifics().session().session_tag());
   1333   EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
   1334 
   1335   EXPECT_TRUE(
   1336       StartsWithASCII(syncer::SyncDataLocal(out[1].sync_data()).GetTag(),
   1337                       manager()->current_machine_tag(),
   1338                       true));
   1339   EXPECT_EQ(manager()->current_machine_tag(),
   1340             out[1].sync_data().GetSpecifics().session().session_tag());
   1341   EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
   1342   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
   1343 
   1344   EXPECT_TRUE(out[2].IsValid());
   1345   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
   1346   const SyncData data(out[2].sync_data());
   1347   EXPECT_EQ(manager()->current_machine_tag(),
   1348             syncer::SyncDataLocal(data).GetTag());
   1349   const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
   1350   EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
   1351   EXPECT_TRUE(specifics.has_header());
   1352   const sync_pb::SessionHeader& header_s = specifics.header();
   1353   EXPECT_EQ(1, header_s.window_size());
   1354   EXPECT_EQ(1, header_s.window(0).tab_size());
   1355 }
   1356 
   1357 // Tests that the SyncSessionManager responds to local tab events properly.
   1358 TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
   1359   syncer::SyncChangeList out;
   1360   // Init with no local data, relies on MergeLocalSessionNoTabs.
   1361   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
   1362   ASSERT_FALSE(manager()->current_machine_tag().empty());
   1363   ASSERT_EQ(2U, out.size());
   1364 
   1365   // Copy the original header.
   1366   sync_pb::EntitySpecifics header(out[0].sync_data().GetSpecifics());
   1367   out.clear();
   1368 
   1369   const GURL foo1("http://foo/1");
   1370   const GURL foo2("http://foo/2");
   1371   const GURL bar1("http://bar/1");
   1372   const GURL bar2("http://bar/2");
   1373   AddTab(browser(), foo1);
   1374   NavigateAndCommitActiveTab(foo2);
   1375   AddTab(browser(), bar1);
   1376   NavigateAndCommitActiveTab(bar2);
   1377 
   1378   // One add, one update for each AddTab.
   1379   // One update for each NavigateAndCommit.
   1380   // = 6 total tab updates.
   1381   // One header update corresponding to each of those.
   1382   // = 6 total header updates.
   1383   // 12 total updates.
   1384   ASSERT_EQ(12U, out.size());
   1385 
   1386   // Verify the tab node creations and updates to ensure the SyncProcessor
   1387   // sees the right operations.
   1388   for (int i = 0; i < 12; i++) {
   1389     SCOPED_TRACE(i);
   1390     EXPECT_TRUE(out[i].IsValid());
   1391     const SyncData data(out[i].sync_data());
   1392     EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
   1393                                 manager()->current_machine_tag(), true));
   1394     const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
   1395     EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
   1396     if (i % 6 == 0) {
   1397       // First thing on an AddTab is a no-op header update for parented tab.
   1398       EXPECT_EQ(header.SerializeAsString(),
   1399                 data.GetSpecifics().SerializeAsString());
   1400       EXPECT_EQ(manager()->current_machine_tag(),
   1401                 syncer::SyncDataLocal(data).GetTag());
   1402     } else if (i % 6 == 1) {
   1403       // Next, the TabNodePool should create the tab node.
   1404       EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
   1405       EXPECT_EQ(TabNodePool::TabIdToTag(
   1406                     manager()->current_machine_tag(),
   1407                     data.GetSpecifics().session().tab_node_id()),
   1408                 syncer::SyncDataLocal(data).GetTag());
   1409     } else if (i % 6 == 2) {
   1410       // Then we see the tab update to the URL.
   1411       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
   1412       EXPECT_EQ(TabNodePool::TabIdToTag(
   1413                     manager()->current_machine_tag(),
   1414                     data.GetSpecifics().session().tab_node_id()),
   1415                 syncer::SyncDataLocal(data).GetTag());
   1416       ASSERT_TRUE(specifics.has_tab());
   1417     } else if (i % 6 == 3) {
   1418       // The header needs to be updated to reflect the new window state.
   1419       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
   1420       EXPECT_TRUE(specifics.has_header());
   1421     } else if (i % 6 == 4) {
   1422       // Now we move on to NavigateAndCommit.  Update the tab.
   1423       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
   1424       EXPECT_EQ(TabNodePool::TabIdToTag(
   1425                     manager()->current_machine_tag(),
   1426                     data.GetSpecifics().session().tab_node_id()),
   1427                 syncer::SyncDataLocal(data).GetTag());
   1428       ASSERT_TRUE(specifics.has_tab());
   1429     } else if (i % 6 == 5) {
   1430       // The header needs to be updated to reflect the new window state.
   1431       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
   1432       ASSERT_TRUE(specifics.has_header());
   1433       header = data.GetSpecifics();
   1434     }
   1435   }
   1436 
   1437   // Verify the actual content to ensure sync sees the right data.
   1438   // When it's all said and done, the header should reflect two tabs.
   1439   const sync_pb::SessionHeader& session_header = header.session().header();
   1440   ASSERT_EQ(1, session_header.window_size());
   1441   EXPECT_EQ(2, session_header.window(0).tab_size());
   1442 
   1443   // ASSERT_TRUEs above allow us to dive in freely here.
   1444   // Verify first tab.
   1445   const sync_pb::SessionTab& tab1_1 =
   1446       out[2].sync_data().GetSpecifics().session().tab();
   1447   ASSERT_EQ(1, tab1_1.navigation_size());
   1448   EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
   1449   const sync_pb::SessionTab& tab1_2 =
   1450       out[4].sync_data().GetSpecifics().session().tab();
   1451   ASSERT_EQ(2, tab1_2.navigation_size());
   1452   EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
   1453   EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
   1454 
   1455   // Verify second tab.
   1456   const sync_pb::SessionTab& tab2_1 =
   1457       out[8].sync_data().GetSpecifics().session().tab();
   1458   ASSERT_EQ(1, tab2_1.navigation_size());
   1459   EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
   1460   const sync_pb::SessionTab& tab2_2 =
   1461       out[10].sync_data().GetSpecifics().session().tab();
   1462   ASSERT_EQ(2, tab2_2.navigation_size());
   1463   EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
   1464   EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
   1465 }
   1466 
   1467 // Ensure model association associates the pre-existing tabs.
   1468 TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
   1469   AddTab(browser(), GURL("http://foo1"));
   1470   NavigateAndCommitActiveTab(GURL("http://foo2"));  // Adds back entry.
   1471   AddTab(browser(), GURL("http://bar1"));
   1472   NavigateAndCommitActiveTab(GURL("http://bar2"));  // Adds back entry.
   1473 
   1474   syncer::SyncChangeList out;
   1475   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
   1476   ASSERT_EQ(6U, out.size());
   1477 
   1478   // Check that this machine's data is not included in the foreign windows.
   1479   std::vector<const SyncedSession*> foreign_sessions;
   1480   ASSERT_FALSE(manager()->GetAllForeignSessions(&foreign_sessions));
   1481 
   1482   // Verify the header.
   1483   EXPECT_TRUE(out[0].IsValid());
   1484   EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
   1485   const SyncData data(out[0].sync_data());
   1486   EXPECT_EQ(manager()->current_machine_tag(),
   1487             syncer::SyncDataLocal(data).GetTag());
   1488   const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
   1489   EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
   1490   EXPECT_TRUE(specifics.has_header());
   1491   const sync_pb::SessionHeader& header_s = specifics.header();
   1492   EXPECT_TRUE(header_s.has_device_type());
   1493   EXPECT_EQ(GetLocalDeviceInfo()->client_name(), header_s.client_name());
   1494   EXPECT_EQ(0, header_s.window_size());
   1495 
   1496   // Verify the tab node creations and updates with content.
   1497   for (int i = 1; i < 5; i++) {
   1498     EXPECT_TRUE(out[i].IsValid());
   1499     const SyncData data(out[i].sync_data());
   1500     EXPECT_TRUE(StartsWithASCII(syncer::SyncDataLocal(data).GetTag(),
   1501                                 manager()->current_machine_tag(), true));
   1502     const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
   1503     EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
   1504     if (i % 2 == 1) {
   1505       EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
   1506     } else {
   1507       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
   1508       EXPECT_TRUE(specifics.has_tab());
   1509     }
   1510   }
   1511 
   1512   // Verify the header was updated to reflect new window state.
   1513   EXPECT_TRUE(out[5].IsValid());
   1514   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
   1515   const SyncData data_2(out[5].sync_data());
   1516   EXPECT_EQ(manager()->current_machine_tag(),
   1517             syncer::SyncDataLocal(data_2).GetTag());
   1518   const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
   1519   EXPECT_EQ(manager()->current_machine_tag(), specifics2.session_tag());
   1520   EXPECT_TRUE(specifics2.has_header());
   1521   const sync_pb::SessionHeader& header_s2 = specifics2.header();
   1522   EXPECT_EQ(1, header_s2.window_size());
   1523 
   1524   // Verify TabLinks.
   1525   SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
   1526   ASSERT_EQ(2U, tab_map.size());
   1527   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
   1528   // the tree based on order of tabs created
   1529   SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin();
   1530   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
   1531   EXPECT_EQ(GURL("http://foo1"), iter->second->tab()->
   1532           GetEntryAtIndex(0)->GetVirtualURL());
   1533   EXPECT_EQ(GURL("http://foo2"), iter->second->tab()->
   1534           GetEntryAtIndex(1)->GetVirtualURL());
   1535   iter++;
   1536   ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
   1537   EXPECT_EQ(GURL("http://bar1"), iter->second->tab()->
   1538       GetEntryAtIndex(0)->GetVirtualURL());
   1539   EXPECT_EQ(GURL("http://bar2"), iter->second->tab()->
   1540       GetEntryAtIndex(1)->GetVirtualURL());
   1541 }
   1542 
   1543 // Test garbage collection of stale foreign sessions.
   1544 TEST_F(SessionsSyncManagerTest, DoGarbageCollection) {
   1545   // Fill two instances of session specifics with a foreign session's data.
   1546   std::string tag1 = "tag1";
   1547   SessionID::id_type n1[] = {5, 10, 13, 17};
   1548   std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
   1549   std::vector<sync_pb::SessionSpecifics> tabs1;
   1550   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1551       tag1, tab_list1, &tabs1));
   1552   std::string tag2 = "tag2";
   1553   SessionID::id_type n2[] = {8, 15, 18, 20};
   1554   std::vector<SessionID::id_type> tab_list2(n2, n2 + arraysize(n2));
   1555   std::vector<sync_pb::SessionSpecifics> tabs2;
   1556   sync_pb::SessionSpecifics meta2(helper()->BuildForeignSession(
   1557       tag2, tab_list2, &tabs2));
   1558   // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
   1559   base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
   1560   base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
   1561 
   1562   syncer::SyncDataList foreign_data;
   1563   sync_pb::EntitySpecifics entity1, entity2;
   1564   entity1.mutable_session()->CopyFrom(meta);
   1565   entity2.mutable_session()->CopyFrom(meta2);
   1566   foreign_data.push_back(SyncData::CreateRemoteData(
   1567       1,
   1568       entity1,
   1569       tag1_time,
   1570       syncer::AttachmentIdList(),
   1571       syncer::AttachmentServiceProxyForTest::Create()));
   1572   foreign_data.push_back(SyncData::CreateRemoteData(
   1573       1,
   1574       entity2,
   1575       tag2_time,
   1576       syncer::AttachmentIdList(),
   1577       syncer::AttachmentServiceProxyForTest::Create()));
   1578   AddTabsToSyncDataList(tabs1, &foreign_data);
   1579   AddTabsToSyncDataList(tabs2, &foreign_data);
   1580 
   1581   syncer::SyncChangeList output;
   1582   InitWithSyncDataTakeOutput(foreign_data, &output);
   1583   ASSERT_EQ(2U, output.size());
   1584   output.clear();
   1585 
   1586   // Check that the foreign session was associated and retrieve the data.
   1587   std::vector<const SyncedSession*> foreign_sessions;
   1588   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1589   ASSERT_EQ(2U, foreign_sessions.size());
   1590   foreign_sessions.clear();
   1591 
   1592   // Now garbage collect and verify the non-stale session is still there.
   1593   manager()->DoGarbageCollection();
   1594   ASSERT_EQ(5U, output.size());
   1595   EXPECT_EQ(SyncChange::ACTION_DELETE, output[0].change_type());
   1596   const SyncData data(output[0].sync_data());
   1597   EXPECT_EQ(tag1, syncer::SyncDataLocal(data).GetTag());
   1598   for (int i = 1; i < 5; i++) {
   1599     EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type());
   1600     const SyncData data(output[i].sync_data());
   1601     EXPECT_EQ(TabNodePool::TabIdToTag(tag1, i),
   1602               syncer::SyncDataLocal(data).GetTag());
   1603   }
   1604 
   1605   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1606   ASSERT_EQ(1U, foreign_sessions.size());
   1607   std::vector<std::vector<SessionID::id_type> > session_reference;
   1608   session_reference.push_back(tab_list2);
   1609   helper()->VerifySyncedSession(tag2, session_reference,
   1610                                 *(foreign_sessions[0]));
   1611 }
   1612 
   1613 // Test that an update to a previously considered "stale" session,
   1614 // prior to garbage collection, will save the session from deletion.
   1615 TEST_F(SessionsSyncManagerTest, GarbageCollectionHonoursUpdate) {
   1616   std::string tag1 = "tag1";
   1617   SessionID::id_type n1[] = {5, 10, 13, 17};
   1618   std::vector<SessionID::id_type> tab_list1(n1, n1 + arraysize(n1));
   1619   std::vector<sync_pb::SessionSpecifics> tabs1;
   1620   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1621       tag1, tab_list1, &tabs1));
   1622   syncer::SyncDataList foreign_data;
   1623   sync_pb::EntitySpecifics entity1;
   1624   base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
   1625   entity1.mutable_session()->CopyFrom(meta);
   1626   foreign_data.push_back(SyncData::CreateRemoteData(
   1627       1,
   1628       entity1,
   1629       tag1_time,
   1630       syncer::AttachmentIdList(),
   1631       syncer::AttachmentServiceProxyForTest::Create()));
   1632   AddTabsToSyncDataList(tabs1, &foreign_data);
   1633   syncer::SyncChangeList output;
   1634   InitWithSyncDataTakeOutput(foreign_data, &output);
   1635   ASSERT_EQ(2U, output.size());
   1636 
   1637   // Update to a non-stale time.
   1638   sync_pb::EntitySpecifics update_entity;
   1639   update_entity.mutable_session()->CopyFrom(tabs1[0]);
   1640   syncer::SyncChangeList changes;
   1641   changes.push_back(
   1642       syncer::SyncChange(FROM_HERE,
   1643                          SyncChange::ACTION_UPDATE,
   1644                          syncer::SyncData::CreateRemoteData(
   1645                              1,
   1646                              update_entity,
   1647                              base::Time::Now(),
   1648                              syncer::AttachmentIdList(),
   1649                              syncer::AttachmentServiceProxyForTest::Create())));
   1650   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1651 
   1652   // Check that the foreign session was associated and retrieve the data.
   1653   std::vector<const SyncedSession*> foreign_sessions;
   1654   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1655   ASSERT_EQ(1U, foreign_sessions.size());
   1656   foreign_sessions.clear();
   1657 
   1658   // Verify the now non-stale session does not get deleted.
   1659   manager()->DoGarbageCollection();
   1660   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   1661   ASSERT_EQ(1U, foreign_sessions.size());
   1662   std::vector<std::vector<SessionID::id_type> > session_reference;
   1663   session_reference.push_back(tab_list1);
   1664   helper()->VerifySyncedSession(
   1665       tag1, session_reference, *(foreign_sessions[0]));
   1666 }
   1667 
   1668 // Test that swapping WebContents for a tab is properly observed and handled
   1669 // by the SessionsSyncManager.
   1670 TEST_F(SessionsSyncManagerTest, CheckPrerenderedWebContentsSwap) {
   1671   AddTab(browser(), GURL("http://foo1"));
   1672   NavigateAndCommitActiveTab(GURL("http://foo2"));
   1673 
   1674   syncer::SyncChangeList out;
   1675   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
   1676   ASSERT_EQ(4U, out.size());  // Header, tab ADD, tab UPDATE, header UPDATE.
   1677 
   1678   // To simulate WebContents swap during prerendering, create new WebContents
   1679   // and swap with old WebContents.
   1680   scoped_ptr<content::WebContents> old_web_contents;
   1681   old_web_contents.reset(browser()->tab_strip_model()->GetActiveWebContents());
   1682 
   1683   // Create new WebContents, with the required tab helpers.
   1684   WebContents* new_web_contents = WebContents::CreateWithSessionStorage(
   1685       WebContents::CreateParams(profile()),
   1686       old_web_contents->GetController().GetSessionStorageNamespaceMap());
   1687   SessionTabHelper::CreateForWebContents(new_web_contents);
   1688   TabContentsSyncedTabDelegate::CreateForWebContents(new_web_contents);
   1689   new_web_contents->GetController()
   1690       .CopyStateFrom(old_web_contents->GetController());
   1691 
   1692   // Swap the WebContents.
   1693   int index = browser()->tab_strip_model()->GetIndexOfWebContents(
   1694       old_web_contents.get());
   1695   browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
   1696 
   1697   ASSERT_EQ(9U, out.size());
   1698   EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type());
   1699   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
   1700 
   1701   // Navigate away.
   1702   NavigateAndCommitActiveTab(GURL("http://bar2"));
   1703 
   1704   // Delete old WebContents. This should not crash.
   1705   old_web_contents.reset();
   1706 
   1707   // Try more navigations and verify output size. This can also reveal
   1708   // bugs (leaks) on memcheck bots if the SessionSyncManager
   1709   // didn't properly clean up the tab pool or session tracker.
   1710   NavigateAndCommitActiveTab(GURL("http://bar3"));
   1711 
   1712   AddTab(browser(), GURL("http://bar4"));
   1713   NavigateAndCommitActiveTab(GURL("http://bar5"));
   1714   ASSERT_EQ(19U, out.size());
   1715 }
   1716 
   1717 namespace {
   1718 class SessionNotificationObserver : public content::NotificationObserver {
   1719  public:
   1720   SessionNotificationObserver() : notified_of_update_(false),
   1721                                   notified_of_refresh_(false) {
   1722     registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
   1723                    content::NotificationService::AllSources());
   1724     registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
   1725                    content::NotificationService::AllSources());
   1726   }
   1727   virtual void Observe(int type,
   1728                        const content::NotificationSource& source,
   1729                        const content::NotificationDetails& details) OVERRIDE {
   1730     switch (type) {
   1731       case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
   1732         notified_of_update_ = true;
   1733         break;
   1734       case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
   1735         notified_of_refresh_ = true;
   1736         break;
   1737       default:
   1738         NOTREACHED();
   1739         break;
   1740     }
   1741   }
   1742   bool notified_of_update() const { return notified_of_update_; }
   1743   bool notified_of_refresh() const { return notified_of_refresh_; }
   1744   void Reset() {
   1745     notified_of_update_ = false;
   1746     notified_of_refresh_ = false;
   1747   }
   1748  private:
   1749   content::NotificationRegistrar registrar_;
   1750   bool notified_of_update_;
   1751   bool notified_of_refresh_;
   1752 };
   1753 }  // namespace
   1754 
   1755 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
   1756 // sync changes.
   1757 TEST_F(SessionsSyncManagerTest, NotifiedOfUpdates) {
   1758   SessionNotificationObserver observer;
   1759   ASSERT_FALSE(observer.notified_of_update());
   1760   InitWithNoSyncData();
   1761 
   1762   SessionID::id_type n[] = {5};
   1763   std::vector<sync_pb::SessionSpecifics> tabs1;
   1764   std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
   1765   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1766       "tag1", tab_list, &tabs1));
   1767 
   1768   syncer::SyncChangeList changes;
   1769   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
   1770   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1771   EXPECT_TRUE(observer.notified_of_update());
   1772 
   1773   changes.clear();
   1774   observer.Reset();
   1775   AddTabsToChangeList(tabs1, SyncChange::ACTION_ADD, &changes);
   1776   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1777   EXPECT_TRUE(observer.notified_of_update());
   1778 
   1779   changes.clear();
   1780   observer.Reset();
   1781   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_DELETE));
   1782   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1783   EXPECT_TRUE(observer.notified_of_update());
   1784 }
   1785 
   1786 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when handling
   1787 // local hide/removal of foreign session.
   1788 TEST_F(SessionsSyncManagerTest, NotifiedOfLocalRemovalOfForeignSession) {
   1789   InitWithNoSyncData();
   1790   const std::string tag("tag1");
   1791   SessionID::id_type n[] = {5};
   1792   std::vector<sync_pb::SessionSpecifics> tabs1;
   1793   std::vector<SessionID::id_type> tab_list(n, n + arraysize(n));
   1794   sync_pb::SessionSpecifics meta(helper()->BuildForeignSession(
   1795       tag, tab_list, &tabs1));
   1796 
   1797   syncer::SyncChangeList changes;
   1798   changes.push_back(MakeRemoteChange(1, meta, SyncChange::ACTION_ADD));
   1799   manager()->ProcessSyncChanges(FROM_HERE, changes);
   1800 
   1801   SessionNotificationObserver observer;
   1802   ASSERT_FALSE(observer.notified_of_update());
   1803   manager()->DeleteForeignSession(tag);
   1804   ASSERT_TRUE(observer.notified_of_update());
   1805 }
   1806 
   1807 #if defined(OS_ANDROID) || defined(OS_IOS)
   1808 // Tests that opening the other devices page triggers a session sync refresh.
   1809 // This page only exists on mobile platforms today; desktop has a
   1810 // search-enhanced NTP without other devices.
   1811 TEST_F(SessionsSyncManagerTest, NotifiedOfRefresh) {
   1812   SessionNotificationObserver observer;
   1813   ASSERT_FALSE(observer.notified_of_refresh());
   1814   InitWithNoSyncData();
   1815   AddTab(browser(), GURL("http://foo1"));
   1816   EXPECT_FALSE(observer.notified_of_refresh());
   1817   NavigateAndCommitActiveTab(GURL("chrome://newtab/#open_tabs"));
   1818   EXPECT_TRUE(observer.notified_of_refresh());
   1819 }
   1820 #endif  // defined(OS_ANDROID) || defined(OS_IOS)
   1821 
   1822 }  // namespace browser_sync
   1823