blob: 7497db2aba35e0a4cb3a8409db1fb11331ffc9f6 [file] [log] [blame]
// Copyright 2012 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 "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
#include "chrome/browser/sync/session_sync_service_factory.h"
#include "chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h"
#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
#include "chrome/browser/sync/test/integration/session_hierarchy_match_checker.h"
#include "chrome/browser/sync/test/integration/sessions_helper.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/sync/test/integration/typed_urls_helper.h"
#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/url_constants.h"
#include "components/history/core/browser/history_types.h"
#include "components/sessions/core/session_types.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "components/sync/base/time.h"
#include "components/sync/protocol/proto_value_conversions.h"
#include "components/sync/test/fake_server/sessions_hierarchy.h"
#include "components/sync_sessions/session_store.h"
#include "components/sync_sessions/session_sync_service.h"
#include "components/sync_sessions/session_sync_test_helper.h"
#include "components/sync_sessions/synced_session_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/mojo/window_open_disposition.mojom.h"
namespace {
using base::HistogramBase;
using base::HistogramSamples;
using base::HistogramTester;
using fake_server::SessionsHierarchy;
using sessions_helper::CheckInitialState;
using sessions_helper::CloseTab;
using sessions_helper::ExecJs;
using sessions_helper::GetLocalWindows;
using sessions_helper::GetSessionData;
using sessions_helper::MoveTab;
using sessions_helper::NavigateTab;
using sessions_helper::NavigateTabBack;
using sessions_helper::NavigateTabForward;
using sessions_helper::OpenTab;
using sessions_helper::OpenTabAtIndex;
using sessions_helper::OpenTabFromSourceIndex;
using sessions_helper::ScopedWindowMap;
using sessions_helper::SessionWindowMap;
using sessions_helper::SyncedSessionVector;
using sessions_helper::WaitForTabsToLoad;
using sessions_helper::WindowsMatch;
using sync_sessions::SessionSyncTestHelper;
using testing::IsEmpty;
using testing::UnorderedElementsAre;
using typed_urls_helper::GetUrlFromClient;
static const char* kURL1 = "data:text/html,<html><title>Test</title></html>";
static const char* kURL2 = "data:text/html,<html><title>Test2</title></html>";
static const char* kURL3 = "data:text/html,<html><title>Test3</title></html>";
static const char* kURL4 = "data:text/html,<html><title>Test4</title></html>";
static const char* kBaseFragmentURL =
"data:text/html,<html><title>Fragment</title><body></body></html>";
static const char* kSpecifiedFragmentURL =
"data:text/html,<html><title>Fragment</title><body></body></html>#fragment";
void ExpectUniqueSampleGE(const HistogramTester& histogram_tester,
const std::string& name,
HistogramBase::Sample sample,
HistogramBase::Count expected_inclusive_lower_bound) {
std::unique_ptr<HistogramSamples> samples =
histogram_tester.GetHistogramSamplesSinceCreation(name);
int sample_count = samples->GetCount(sample);
EXPECT_GE(sample_count, expected_inclusive_lower_bound)
<< " for histogram " << name << " sample " << sample;
EXPECT_EQ(sample_count, samples->TotalCount())
<< " for histogram " << name << " sample " << sample;
}
class IsHistoryURLSyncedChecker : public SingleClientStatusChangeChecker {
public:
IsHistoryURLSyncedChecker(const std::string& url,
fake_server::FakeServer* fake_server,
browser_sync::ProfileSyncService* service)
: SingleClientStatusChangeChecker(service),
url_(url),
fake_server_(fake_server) {}
// StatusChangeChecker implementation.
bool IsExitConditionSatisfied() override {
return fake_server_->GetCommittedHistoryURLs().count(url_) != 0;
}
std::string GetDebugMessage() const override {
return "Waiting for URLs to be commited to the server";
}
private:
const std::string url_;
fake_server::FakeServer* fake_server_;
};
class SingleClientSessionsSyncTest : public SyncTest {
public:
SingleClientSessionsSyncTest() : SyncTest(SINGLE_CLIENT) {}
~SingleClientSessionsSyncTest() override {}
void ExpectNavigationChain(const std::vector<GURL>& urls) {
ScopedWindowMap windows;
ASSERT_TRUE(GetLocalWindows(0, &windows));
ASSERT_EQ(windows.begin()->second->wrapped_window.tabs.size(), 1u);
sessions::SessionTab* tab =
windows.begin()->second->wrapped_window.tabs[0].get();
int index = 0;
EXPECT_EQ(urls.size(), tab->navigations.size());
for (auto it = tab->navigations.begin(); it != tab->navigations.end();
++it, ++index) {
EXPECT_EQ(urls[index], it->virtual_url());
}
}
// Block until the expected hierarchy is recorded on the FakeServer for
// profile 0. This will time out if the hierarchy is never
// recorded.
void WaitForHierarchyOnServer(
const fake_server::SessionsHierarchy& hierarchy) {
SessionHierarchyMatchChecker checker(hierarchy, GetSyncService(0),
GetFakeServer());
EXPECT_TRUE(checker.Wait());
}
// Shortcut to call WaitForHierarchyOnServer for only |url| in a single
// window.
void WaitForURLOnServer(const GURL& url) {
WaitForHierarchyOnServer({{url.spec()}});
}
// Simulates receiving list of accounts in the cookie jar from ListAccounts
// endpoint. Adds |account_ids| into signed in accounts, notifies
// ProfileSyncService and waits for change to propagate to sync engine.
void UpdateCookieJarAccountsAndWait(std::vector<std::string> account_ids,
bool expected_cookie_jar_mismatch) {
std::vector<gaia::ListedAccount> accounts;
for (const auto& account_id : account_ids) {
gaia::ListedAccount signed_in_account;
signed_in_account.id = account_id;
accounts.push_back(signed_in_account);
}
base::RunLoop run_loop;
EXPECT_EQ(expected_cookie_jar_mismatch,
GetClient(0)->service()->HasCookieJarMismatch(accounts));
GetClient(0)->service()->OnGaiaAccountsInCookieUpdatedWithCallback(
accounts, run_loop.QuitClosure());
run_loop.Run();
}
private:
DISALLOW_COPY_AND_ASSIGN(SingleClientSessionsSyncTest);
};
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
RequireProxyTabsForUiDelegate) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
sync_sessions::SessionSyncService* service =
SessionSyncServiceFactory::GetForProfile(GetProfile(0));
EXPECT_NE(nullptr, service->GetOpenTabsUIDelegate());
ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::PROXY_TABS));
EXPECT_EQ(nullptr, service->GetOpenTabsUIDelegate());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, Sanity) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
// Add a new session to client 0 and wait for it to sync.
ScopedWindowMap old_windows;
GURL url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, url));
ASSERT_TRUE(GetLocalWindows(0, &old_windows));
ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
// Get foreign session data from client 0.
SyncedSessionVector sessions;
ASSERT_FALSE(GetSessionData(0, &sessions));
ASSERT_EQ(0U, sessions.size());
// Verify client didn't change.
ScopedWindowMap new_windows;
ASSERT_TRUE(GetLocalWindows(0, &new_windows));
ASSERT_TRUE(WindowsMatch(old_windows, new_windows));
WaitForURLOnServer(url);
EXPECT_THAT(GetFakeServer()->GetCommittedHistoryURLs(),
UnorderedElementsAre(kURL1));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NavigateInTab) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
ASSERT_TRUE(OpenTab(0, GURL(kURL1)));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL1}}));
NavigateTab(0, GURL(kURL2));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL2}}));
EXPECT_THAT(GetFakeServer()->GetCommittedHistoryURLs(),
UnorderedElementsAre(kURL1, kURL2));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
JavascriptHistoryReplaceState) {
// Executing Javascript requires HTTP pages with an origin.
ASSERT_TRUE(embedded_test_server()->Start());
const std::string url1 =
embedded_test_server()->GetURL("/sync/simple.html").spec();
const std::string url2 =
embedded_test_server()->GetURL("/replaced_history.html").spec();
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
ASSERT_TRUE(OpenTab(0, GURL(url1)));
WaitForHierarchyOnServer(SessionsHierarchy({{url1}}));
ASSERT_TRUE(
ExecJs(/*browser_index=*/0, /*tab_index=*/0,
base::StringPrintf("history.replaceState({}, 'page 2', '%s')",
url2.c_str())));
WaitForHierarchyOnServer(SessionsHierarchy({{url2}}));
// Fetch the tab from the server for further verification.
const std::vector<sync_pb::SyncEntity> entities =
GetFakeServer()->GetSyncEntitiesByModelType(syncer::SESSIONS);
const sync_pb::TabNavigation* tab_navigation = nullptr;
for (const sync_pb::SyncEntity& entity : entities) {
if (entity.specifics().session().tab().navigation_size() == 1 &&
entity.specifics().session().tab().navigation(0).virtual_url() ==
url2) {
tab_navigation = &entity.specifics().session().tab().navigation(0);
}
}
ASSERT_NE(nullptr, tab_navigation);
EXPECT_TRUE(tab_navigation->has_replaced_navigation());
EXPECT_EQ(url1, tab_navigation->replaced_navigation().first_committed_url());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
SessionsWithoutHistorySync) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// If the user disables history sync on settings, but still enables tab sync,
// then sessions should be synced but the server should be able to tell the
// difference based on active datatypes.
ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::TYPED_URLS));
ASSERT_TRUE(CheckInitialState(0));
ASSERT_TRUE(OpenTab(0, GURL(kURL1)));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL1}}));
NavigateTab(0, GURL(kURL2));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL2}}));
EXPECT_THAT(GetFakeServer()->GetCommittedHistoryURLs(), IsEmpty());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NoSessions) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
WaitForHierarchyOnServer(SessionsHierarchy());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, ChromeHistoryPage) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
ASSERT_TRUE(OpenTab(0, GURL(chrome::kChromeUIHistoryURL)));
WaitForURLOnServer(GURL(chrome::kChromeUIHistoryURL));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NavigateThenCloseTab) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
// Two tabs are opened initially.
ASSERT_TRUE(OpenTab(0, GURL(kURL1)));
ASSERT_TRUE(OpenTab(0, GURL(kURL2)));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL1, kURL2}}));
// Close one of the two tabs immediately after issuing an navigation. We also
// issue another navigation to make sure association logic kicks in.
NavigateTab(0, GURL(kURL3));
ASSERT_TRUE(WaitForTabsToLoad(0, {GURL(kURL1), GURL(kURL3)}));
CloseTab(/*index=*/0, /*tab_index=*/1);
NavigateTab(0, GURL(kURL4));
ASSERT_TRUE(
IsHistoryURLSyncedChecker(kURL4, GetFakeServer(), GetSyncService(0))
.Wait());
// All URLs should be synced, for synced history to be complete. In
// particular, |kURL3| should be synced despite the tab being closed.
EXPECT_TRUE(
IsHistoryURLSyncedChecker(kURL3, GetFakeServer(), GetSyncService(0))
.Wait());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
NavigateThenCloseTabThenOpenTab) {
base::test::ScopedFeatureList override_features;
override_features.InitAndEnableFeature(
sync_sessions::kDeferRecyclingOfSyncTabNodesIfUnsynced);
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
// Two tabs are opened initially.
ASSERT_TRUE(OpenTab(0, GURL(kURL1)));
ASSERT_TRUE(OpenTab(0, GURL(kURL2)));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL1, kURL2}}));
// Close one of the two tabs immediately after issuing an navigation. In
// addition, a new tab is opened.
NavigateTab(0, GURL(kURL3));
ASSERT_TRUE(WaitForTabsToLoad(0, {GURL(kURL1), GURL(kURL3)}));
CloseTab(/*index=*/0, /*tab_index=*/1);
ASSERT_TRUE(OpenTab(0, GURL(kURL4)));
ASSERT_TRUE(
IsHistoryURLSyncedChecker(kURL4, GetFakeServer(), GetSyncService(0))
.Wait());
// All URLs should be synced, for synced history to be complete. In
// particular, |kURL3| should be synced despite the tab being closed.
EXPECT_TRUE(
IsHistoryURLSyncedChecker(kURL3, GetFakeServer(), GetSyncService(0))
.Wait());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, TimestampMatchesHistory) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
const GURL url(kURL1);
ScopedWindowMap windows;
ASSERT_TRUE(OpenTab(0, url));
ASSERT_TRUE(GetLocalWindows(0, &windows));
int found_navigations = 0;
for (auto it = windows.begin(); it != windows.end(); ++it) {
for (auto it2 = it->second->wrapped_window.tabs.begin();
it2 != it->second->wrapped_window.tabs.end(); ++it2) {
for (auto it3 = (*it2)->navigations.begin();
it3 != (*it2)->navigations.end(); ++it3) {
const base::Time timestamp = it3->timestamp();
history::URLRow virtual_row;
ASSERT_TRUE(GetUrlFromClient(0, it3->virtual_url(), &virtual_row));
const base::Time history_timestamp = virtual_row.last_visit();
// Propagated timestamps have millisecond-level resolution, so we avoid
// exact comparison here (i.e. usecs might differ).
ASSERT_EQ(0, (timestamp - history_timestamp).InMilliseconds());
++found_navigations;
}
}
}
ASSERT_EQ(1, found_navigations);
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, ResponseCodeIsPreserved) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
const GURL url(kURL1);
ScopedWindowMap windows;
ASSERT_TRUE(OpenTab(0, url));
ASSERT_TRUE(GetLocalWindows(0, &windows));
int found_navigations = 0;
for (auto it = windows.begin(); it != windows.end(); ++it) {
for (auto it2 = it->second->wrapped_window.tabs.begin();
it2 != it->second->wrapped_window.tabs.end(); ++it2) {
for (auto it3 = (*it2)->navigations.begin();
it3 != (*it2)->navigations.end(); ++it3) {
EXPECT_EQ(200, it3->http_status_code());
++found_navigations;
}
}
}
ASSERT_EQ(1, found_navigations);
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, FragmentURLNavigation) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
const GURL url(kBaseFragmentURL);
ASSERT_TRUE(OpenTab(0, url));
WaitForURLOnServer(url);
const GURL fragment_url(kSpecifiedFragmentURL);
NavigateTab(0, fragment_url);
WaitForURLOnServer(fragment_url);
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
NavigationChainForwardBack) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL first_url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, first_url));
WaitForURLOnServer(first_url);
GURL second_url = GURL(kURL2);
NavigateTab(0, second_url);
WaitForURLOnServer(second_url);
NavigateTabBack(0);
WaitForURLOnServer(first_url);
ExpectNavigationChain({first_url, second_url});
NavigateTabForward(0);
WaitForURLOnServer(second_url);
ExpectNavigationChain({first_url, second_url});
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
NavigationChainAlteredDestructively) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL base_url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, base_url));
WaitForURLOnServer(base_url);
GURL first_url = GURL(kURL2);
NavigateTab(0, first_url);
WaitForURLOnServer(first_url);
// Check that the navigation chain matches the above sequence of {base_url,
// first_url}.
ExpectNavigationChain({base_url, first_url});
NavigateTabBack(0);
WaitForURLOnServer(base_url);
GURL second_url = GURL(kURL3);
NavigateTab(0, second_url);
WaitForURLOnServer(second_url);
NavigateTabBack(0);
WaitForURLOnServer(base_url);
// Check that the navigation chain contains second_url where first_url was
// before.
ExpectNavigationChain({base_url, second_url});
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, OpenNewTab) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL base_url = GURL(kURL1);
ASSERT_TRUE(OpenTabAtIndex(0, 0, base_url));
WaitForURLOnServer(base_url);
GURL new_tab_url = GURL(kURL2);
ASSERT_TRUE(OpenTabAtIndex(0, 1, new_tab_url));
WaitForHierarchyOnServer(
SessionsHierarchy({{base_url.spec(), new_tab_url.spec()}}));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, OpenNewWindow) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL base_url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, base_url));
WaitForURLOnServer(base_url);
GURL new_window_url = GURL(kURL2);
AddBrowser(0);
ASSERT_TRUE(OpenTab(1, new_window_url));
WaitForHierarchyOnServer(
SessionsHierarchy({{base_url.spec()}, {new_window_url.spec()}}));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
GarbageCollectionOfForeignSessions) {
const std::string kForeignSessionTag = "ForeignSessionTag";
const SessionID kWindowId = SessionID::FromSerializedValue(5);
const SessionID kTabId1 = SessionID::FromSerializedValue(1);
const SessionID kTabId2 = SessionID::FromSerializedValue(2);
const base::Time kLastModifiedTime =
base::Time::Now() - base::TimeDelta::FromDays(100);
SessionSyncTestHelper helper;
sync_pb::EntitySpecifics tab1;
*tab1.mutable_session() =
helper.BuildTabSpecifics(kForeignSessionTag, kWindowId, kTabId1);
sync_pb::EntitySpecifics tab2;
*tab2.mutable_session() =
helper.BuildTabSpecifics(kForeignSessionTag, kWindowId, kTabId2);
// |tab2| is orphan, i.e. not referenced by the header. We do this to verify
// that such tabs are also subject to garbage collection.
sync_pb::EntitySpecifics header;
SessionSyncTestHelper::BuildSessionSpecifics(kForeignSessionTag,
header.mutable_session());
SessionSyncTestHelper::AddWindowSpecifics(kWindowId, {kTabId1},
header.mutable_session());
for (const sync_pb::EntitySpecifics& specifics : {tab1, tab2, header}) {
GetFakeServer()->InjectEntity(
syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
sync_sessions::SessionStore::GetClientTag(specifics.session()),
specifics,
/*creation_time=*/syncer::TimeToProtoTime(kLastModifiedTime),
/*last_modified_time=*/syncer::TimeToProtoTime(kLastModifiedTime)));
}
base::HistogramTester histogram_tester;
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// Verify that all entities have been deleted.
WaitForHierarchyOnServer(SessionsHierarchy());
std::vector<sync_pb::SyncEntity> entities =
fake_server_->GetSyncEntitiesByModelType(syncer::SESSIONS);
for (const sync_pb::SyncEntity& entity : entities) {
EXPECT_NE(kForeignSessionTag, entity.specifics().session().session_tag());
}
EXPECT_EQ(
3, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.SESSION",
/*LOCAL_DELETION=*/0));
}
// Regression test for crbug.com/915133 that verifies the browser doesn't crash
// if the server sends corrupt data during initial merge.
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, CorruptInitialForeignTab) {
// Tabs with a negative node ID should be ignored.
sync_pb::EntitySpecifics specifics;
specifics.mutable_session()->mutable_tab();
specifics.mutable_session()->set_tab_node_id(-1);
GetFakeServer()->InjectEntity(
syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
"someclienttag", specifics,
/*creation_time=*/0,
/*last_modified_time=*/0));
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// Foreign data should be empty.
SyncedSessionVector sessions;
ASSERT_FALSE(GetSessionData(0, &sessions));
EXPECT_EQ(0U, sessions.size());
}
// Regression test for crbug.com/915133 that verifies the browser doesn't crash
// if the server sends corrupt data as incremental update.
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, CorruptForeignTabUpdate) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// Tabs with a negative node ID should be ignored.
sync_pb::EntitySpecifics specifics;
specifics.mutable_session()->mutable_tab();
specifics.mutable_session()->set_tab_node_id(-1);
GetFakeServer()->InjectEntity(
syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
"someclienttag", specifics,
/*creation_time=*/0,
/*last_modified_time=*/0));
// Mimic a browser restart to force a reconfiguration and fetch updates.
GetClient(0)->StopSyncServiceWithoutClearingData();
ASSERT_TRUE(GetClient(0)->StartSyncService());
// Foreign data should be empty.
SyncedSessionVector sessions;
ASSERT_FALSE(GetSessionData(0, &sessions));
EXPECT_EQ(0U, sessions.size());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, TabMovedToOtherWindow) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL base_url = GURL(kURL1);
GURL moved_tab_url = GURL(kURL2);
ASSERT_TRUE(OpenTab(0, base_url));
ASSERT_TRUE(OpenTabAtIndex(0, 1, moved_tab_url));
GURL new_window_url = GURL(kURL3);
AddBrowser(0);
ASSERT_TRUE(OpenTab(1, new_window_url));
WaitForHierarchyOnServer(SessionsHierarchy(
{{base_url.spec(), moved_tab_url.spec()}, {new_window_url.spec()}}));
// Move tab 1 in browser 0 to browser 1.
MoveTab(0, 1, 1);
WaitForHierarchyOnServer(SessionsHierarchy(
{{base_url.spec()}, {new_window_url.spec(), moved_tab_url.spec()}}));
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, SourceTabIDSet) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
GURL base_url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, base_url));
WaitForURLOnServer(base_url);
GURL new_tab_url = GURL(kURL2);
ASSERT_TRUE(OpenTabFromSourceIndex(
0, 0, new_tab_url, WindowOpenDisposition::NEW_FOREGROUND_TAB));
WaitForHierarchyOnServer(
SessionsHierarchy({{base_url.spec(), new_tab_url.spec()}}));
content::WebContents* original_tab_contents =
GetBrowser(0)->tab_strip_model()->GetWebContentsAt(0);
content::WebContents* new_tab_contents =
GetBrowser(0)->tab_strip_model()->GetWebContentsAt(1);
SessionID source_tab_id = SessionTabHelper::IdForTab(original_tab_contents);
sync_sessions::SyncSessionsRouterTabHelper* new_tab_helper =
sync_sessions::SyncSessionsRouterTabHelper::FromWebContents(
new_tab_contents);
EXPECT_EQ(new_tab_helper->source_tab_id(), source_tab_id);
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, CookieJarMismatch) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
// Simulate empty list of accounts in the cookie jar. This will record cookie
// jar mismatch.
UpdateCookieJarAccountsAndWait({},
/*expected_cookie_jar_mismatch=*/true);
// The HistogramTester objects are scoped to allow more precise verification.
{
HistogramTester histogram_tester;
// Add a new session to client 0 and wait for it to sync.
GURL url = GURL(kURL1);
ASSERT_TRUE(OpenTab(0, url));
WaitForURLOnServer(url);
sync_pb::ClientToServerMessage message;
ASSERT_TRUE(GetFakeServer()->GetLastCommitMessage(&message));
ASSERT_TRUE(message.commit().config_params().cookie_jar_mismatch());
// It is possible that multiple sync cycles occurred during the call to
// OpenTab, which would cause multiple identical samples.
ExpectUniqueSampleGE(histogram_tester, "Sync.CookieJarMatchOnNavigation",
/*sample=*/false,
/*expected_inclusive_lower_bound=*/1);
ExpectUniqueSampleGE(histogram_tester, "Sync.CookieJarEmptyOnMismatch",
/*sample=*/true,
/*expected_inclusive_lower_bound=*/1);
}
// Avoid interferences from actual GaiaCookieManagerService trying to fetch
// gaia account information, which would exercise
// ProfileSyncService::OnGaiaAccountsInCookieUpdated().
GaiaCookieManagerServiceFactory::GetForProfile(GetProfile(0))->CancelAll();
// Trigger a cookie jar change (user signing in to content area).
// Updating the cookie jar has to travel to the sync engine. It is possible
// something is already running or scheduled to run on the sync thread. We
// want to block here and not create the HistogramTester below until we know
// the cookie jar stats have been updated.
UpdateCookieJarAccountsAndWait(
{GetClient(0)->service()->GetAuthenticatedAccountInfo().account_id},
/*expected_cookie_jar_mismatch=*/false);
{
HistogramTester histogram_tester;
// Trigger a sync and wait for it.
GURL url = GURL(kURL2);
NavigateTab(0, url);
WaitForURLOnServer(url);
ASSERT_NE(
0, histogram_tester.GetBucketCount("Sync.PostedClientToServerMessage",
/*COMMIT=*/1));
// Verify the cookie jar mismatch bool is set to false.
sync_pb::ClientToServerMessage message;
ASSERT_TRUE(GetFakeServer()->GetLastCommitMessage(&message));
EXPECT_FALSE(message.commit().config_params().cookie_jar_mismatch())
<< *syncer::ClientToServerMessageToValue(message, true);
// Verify the histograms were recorded properly.
ExpectUniqueSampleGE(histogram_tester, "Sync.CookieJarMatchOnNavigation",
/*sample=*/true, /*expected_inclusive_lower_bound=*/1);
histogram_tester.ExpectTotalCount("Sync.CookieJarEmptyOnMismatch", 0);
}
}
} // namespace