History Sync cleanup: Delete TypedUrlSyncBridge
TYPED_URLS has been replaced by HISTORY, so this bridge was now unused.
Bug: 1365291
Change-Id: I8d55dc01ec8c56265b76843a93e8062a0f5c1eee
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4855981
Reviewed-by: Sophie Chang <sophiechang@chromium.org>
Commit-Queue: Marc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1195896}
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index fcd97b9..63259f1 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -63,8 +63,6 @@
"sync/history_sync_bridge.h",
"sync/history_sync_metadata_database.cc",
"sync/history_sync_metadata_database.h",
- "sync/typed_url_sync_bridge.cc",
- "sync/typed_url_sync_bridge.h",
"sync/typed_url_sync_metadata_database.cc",
"sync/typed_url_sync_metadata_database.h",
"sync/visit_id_remapper.cc",
@@ -176,7 +174,6 @@
"sync/history_sync_metadata_database_unittest.cc",
"sync/test_history_backend_for_sync.cc",
"sync/test_history_backend_for_sync.h",
- "sync/typed_url_sync_bridge_unittest.cc",
"sync/typed_url_sync_metadata_database_unittest.cc",
"sync/visit_id_remapper_unittest.cc",
"top_sites_database_unittest.cc",
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index fc3b2e8..62e8af1b 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -56,7 +56,6 @@
#include "components/history/core/browser/keyword_search_term_util.h"
#include "components/history/core/browser/page_usage_data.h"
#include "components/history/core/browser/sync/history_sync_bridge.h"
-#include "components/history/core/browser/sync/typed_url_sync_bridge.h"
#include "components/history/core/browser/url_row.h"
#include "components/history/core/browser/url_utils.h"
#include "components/sync/base/features.h"
@@ -413,14 +412,6 @@
InitImpl(history_database_params);
delegate_->DBLoaded();
- typed_url_sync_bridge_ = std::make_unique<TypedURLSyncBridge>(
- this, db_ ? db_->GetTypedURLMetadataDB() : nullptr,
- std::make_unique<ClientTagBasedModelTypeProcessor>(
- syncer::TYPED_URLS,
- base::BindRepeating(&syncer::ReportUnrecoverableError,
- history_database_params.channel)));
- typed_url_sync_bridge_->Init();
-
history_sync_bridge_ = std::make_unique<HistorySyncBridge>(
this, db_ ? db_->GetHistoryMetadataDB() : nullptr,
std::make_unique<ClientTagBasedModelTypeProcessor>(
@@ -1542,11 +1533,6 @@
ScheduleCommit();
}
-void HistoryBackend::SetTypedURLSyncBridgeForTest(
- std::unique_ptr<TypedURLSyncBridge> bridge) {
- typed_url_sync_bridge_ = std::move(bridge);
-}
-
bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) const {
return time < expirer_.GetCurrentExpirationTime();
}
@@ -2019,12 +2005,6 @@
}
base::WeakPtr<syncer::ModelTypeControllerDelegate>
-HistoryBackend::GetTypedURLSyncControllerDelegate() {
- DCHECK(typed_url_sync_bridge_);
- return typed_url_sync_bridge_->change_processor()->GetControllerDelegate();
-}
-
-base::WeakPtr<syncer::ModelTypeControllerDelegate>
HistoryBackend::GetHistorySyncControllerDelegate() {
DCHECK(history_sync_bridge_);
return history_sync_bridge_->change_processor()->GetControllerDelegate();
@@ -3556,10 +3536,8 @@
if (!db_)
return;
- // Notify the sync bridges about storage error. They'll report failures to the
+ // Notify the sync bridge about storage error. It'll report failures to the
// sync engine and stop accepting remote updates.
- if (typed_url_sync_bridge_)
- typed_url_sync_bridge_->OnDatabaseError();
if (history_sync_bridge_)
history_sync_bridge_->OnDatabaseError();
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 87c35c3..2a7fcca 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -73,7 +73,6 @@
class HistoryDBTask;
class HistorySyncBridge;
class InMemoryHistoryBackend;
-class TypedURLSyncBridge;
class URLDatabase;
// Returns a formatted version of `url` with the HTTP/HTTPS scheme, port,
@@ -626,9 +625,15 @@
// For each element in `urls`, updates the pre-existing URLRow in the database
// with the same ID; or ignores the element if no such row exists. Returns the
// number of records successfully updated.
+ // TODO(crbug.com/1365291): This method is only used in tests. Ideally migrate
+ // those tests to other APIs and delete this, or failing that, at least
+ // rename it to *ForTest.
size_t UpdateURLs(const URLRows& urls);
// While adding visits in batch, the source needs to be provided.
+ // TODO(crbug.com/1365291): This method is only used in tests. Ideally migrate
+ // those tests to other APIs and delete this, or failing that, at least
+ // rename it to *ForTest.
bool AddVisits(const GURL& url,
const std::vector<VisitInfo>& visits,
VisitSource visit_source);
@@ -707,11 +712,6 @@
// redirect chain.
bool GetLastVisitByTime(base::Time visit_time, VisitRow* visit_row) override;
- // Returns the sync controller delegate for syncing typed urls. The returned
- // delegate is owned by `this` object.
- base::WeakPtr<syncer::ModelTypeControllerDelegate>
- GetTypedURLSyncControllerDelegate();
-
// Returns the sync controller delegate for syncing history. The returned
// delegate is owned by `this` object.
base::WeakPtr<syncer::ModelTypeControllerDelegate>
@@ -786,6 +786,9 @@
// added for each given URL at the last visit time in the URLRow if the
// passed visit type != SOURCE_SYNCED (the sync code manages visits itself).
// Each visit will have the visit_source type set.
+ // TODO(crbug.com/1365291): This method is only used in tests. Ideally migrate
+ // those tests to other APIs and delete this, or failing that, at least
+ // rename it to *ForTest.
void AddPagesWithDetails(const URLRows& info, VisitSource visit_source);
#if defined(UNIT_TEST)
@@ -794,8 +797,6 @@
ExpireHistoryBackend* expire_backend() { return &expirer_; }
#endif
- void SetTypedURLSyncBridgeForTest(std::unique_ptr<TypedURLSyncBridge> bridge);
-
// Returns true if the passed visit time is already expired (used by the sync
// code to avoid syncing visits that would immediately be expired).
bool IsExpiredVisitTime(const base::Time& time) const override;
@@ -1111,11 +1112,6 @@
// List of observers
base::ObserverList<HistoryBackendObserver>::Unchecked observers_;
- // Used to manage syncing of the typed urls datatype. It will be null before
- // HistoryBackend::Init() is called. Defined after `observers_` because
- // it unregisters itself as observer during destruction.
- std::unique_ptr<TypedURLSyncBridge> typed_url_sync_bridge_;
-
// Used to manage syncing of the history datatype. It will be null before
// HistoryBackend::Init() is called. Defined after `observers_` because
// it unregisters itself as observer during destruction.
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index a1b8efd..36b18a0 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -45,7 +45,6 @@
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/keyword_search_term_util.h"
#include "components/history/core/browser/page_usage_data.h"
-#include "components/history/core/browser/sync/typed_url_sync_bridge.h"
#include "components/history/core/test/database_test_utils.h"
#include "components/history/core/test/history_client_fake_bookmarks.h"
#include "components/history/core/test/test_history_database.h"
@@ -3367,7 +3366,6 @@
TEST_F(HistoryBackendTest, DatabaseError) {
base::HistogramTester histogram_tester;
- backend_->SetTypedURLSyncBridgeForTest(nullptr);
backend_->DatabaseErrorCallback(SQLITE_CANTOPEN, nullptr);
// Run loop to let any posted callbacks run before TearDown().
base::RunLoop().RunUntilIdle();
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 71c86b4..898406b5 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -1459,18 +1459,6 @@
}
std::unique_ptr<syncer::ModelTypeControllerDelegate>
-HistoryService::GetTypedURLSyncControllerDelegate() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Note that a callback is bound for GetTypedURLSyncControllerDelegate()
- // because this getter itself must also run in the backend sequence, and the
- // proxy object below will take care of that.
- return std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
- backend_task_runner_,
- base::BindRepeating(&HistoryBackend::GetTypedURLSyncControllerDelegate,
- base::Unretained(history_backend_.get())));
-}
-
-std::unique_ptr<syncer::ModelTypeControllerDelegate>
HistoryService::GetHistorySyncControllerDelegate() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Note that a callback is bound for GetHistorySyncControllerDelegate()
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index 0e14e7a..d445938f 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -765,11 +765,6 @@
base::WeakPtr<syncer::SyncableService> GetDeleteDirectivesSyncableService();
// For sync codebase only: instantiates a controller delegate to interact with
- // TypedURLSyncBridge. Must be called from the UI thread.
- std::unique_ptr<syncer::ModelTypeControllerDelegate>
- GetTypedURLSyncControllerDelegate();
-
- // For sync codebase only: instantiates a controller delegate to interact with
// HistorySyncBridge. Must be called from the UI thread.
std::unique_ptr<syncer::ModelTypeControllerDelegate>
GetHistorySyncControllerDelegate();
diff --git a/components/history/core/browser/history_service_observer.h b/components/history/core/browser/history_service_observer.h
index 968af2b1..c88bd94c 100644
--- a/components/history/core/browser/history_service_observer.h
+++ b/components/history/core/browser/history_service_observer.h
@@ -54,9 +54,7 @@
//
// These metadata-only updates happen in these scenarios:
// 1. When the Page Title is updated shortly after the page loads.
- // 2. When `TypedURLSyncBridge` updates the `URLRow` data. This often happens
- // in addition to adding new visits, so `OnURLVisited` will be called too.
- // 3. When History expiration expires some, but not all visits related to
+ // 2. When History expiration expires some, but not all visits related to
// a URL. In that case, the URL's metadata is updated.
//
// `changed_urls` lists the information for each of the URLs affected. The
diff --git a/components/history/core/browser/sync/typed_url_sync_bridge.cc b/components/history/core/browser/sync/typed_url_sync_bridge.cc
deleted file mode 100644
index ecd3234..0000000
--- a/components/history/core/browser/sync/typed_url_sync_bridge.cc
+++ /dev/null
@@ -1,1294 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/history/core/browser/sync/typed_url_sync_bridge.h"
-
-#include "base/auto_reset.h"
-#include "base/big_endian.h"
-#include "base/functional/bind.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/ranges/algorithm.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "components/sync/model/metadata_batch.h"
-#include "components/sync/model/mutable_data_batch.h"
-#include "components/sync/model/sync_metadata_store_change_list.h"
-#include "components/sync/protocol/entity_metadata.pb.h"
-#include "net/base/url_util.h"
-
-namespace history {
-
-namespace {
-
-// The server backend can't handle arbitrarily large node sizes, so to keep
-// the size under control we limit the visit array.
-static const int kMaxTypedUrlVisits = 100;
-
-// There's no limit on how many visits the history DB could have for a given
-// typed URL, so we limit how many we fetch from the DB to avoid crashes due to
-// running out of memory (http://crbug.com/89793). This value is different
-// from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be
-// RELOAD visits, which will be stripped.
-static const int kMaxVisitsToFetch = 1000;
-
-// This is the threshold at which we start throttling sync updates for typed
-// URLs - any URLs with a typed_count >= this threshold will be throttled.
-static const int kTypedUrlVisitThrottleThreshold = 10;
-
-// This is the multiple we use when throttling sync updates. If the multiple is
-// N, we sync up every Nth update (i.e. when typed_count % N == 0).
-static const int kTypedUrlVisitThrottleMultiple = 10;
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class SyncTypedUrlDatabaseError {
- kMergeFullSyncDataRead = 0,
- kMergeFullSyncDataWriteData = 1,
- kMergeFullSyncDataWriteMetadata = 2,
- kApplyIncrementalSyncChangesWriteData = 3,
- kApplyIncrementalSyncChangesWriteMetadata = 4,
- kOnURLsDeletedReadMetadata = 5,
- kOnDatabaseError = 6,
- kLoadMetadataOpen = 7,
- kLoadMetadataRead = 8,
- kMaxValue = kLoadMetadataRead
-};
-
-static bool VisitsAreSorted(const std::vector<VisitRow>& visits) {
- return base::ranges::is_sorted(visits, /*comp=*/{}, &VisitRow::visit_time);
-}
-
-std::string GetStorageKeyFromURLRow(const URLRow& row) {
- DCHECK_NE(row.id(), 0);
- std::string storage_key(sizeof(row.id()), 0);
- base::WriteBigEndian<URLID>(&storage_key[0], row.id());
- return storage_key;
-}
-
-bool HasTypedUrl(const std::vector<VisitRow>& visits) {
- return base::ranges::any_of(visits, [](const VisitRow& visit) {
- return ui::PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_TYPED);
- });
-}
-
-void RecordDatabaseError(SyncTypedUrlDatabaseError error) {
- base::UmaHistogramEnumeration("Sync.TypedURLDatabaseError", error);
-}
-
-} // namespace
-
-TypedURLSyncBridge::URLWithVisits::URLWithVisits(
- const GURL& url,
- const std::vector<VisitInfo>& visits)
- : url(url), visits(visits) {}
-
-TypedURLSyncBridge::URLWithVisits::~URLWithVisits() = default;
-
-TypedURLSyncBridge::URLWithVisits::URLWithVisits(URLWithVisits&&) = default;
-
-TypedURLSyncBridge::TypedURLSyncBridge(
- HistoryBackend* history_backend,
- TypedURLSyncMetadataDatabase* sync_metadata_database,
- std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
- : ModelTypeSyncBridge(std::move(change_processor)),
- history_backend_(history_backend),
- sync_metadata_database_(sync_metadata_database) {
- DCHECK(history_backend_);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-TypedURLSyncBridge::~TypedURLSyncBridge() = default;
-
-std::unique_ptr<syncer::MetadataChangeList>
-TypedURLSyncBridge::CreateMetadataChangeList() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return std::make_unique<syncer::SyncMetadataStoreChangeList>(
- sync_metadata_database_, syncer::TYPED_URLS,
- base::BindRepeating(&syncer::ModelTypeChangeProcessor::ReportError,
- change_processor()->GetWeakPtr()));
-}
-
-absl::optional<syncer::ModelError> TypedURLSyncBridge::MergeFullSyncData(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityChangeList entity_data) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- // Create a mapping of all local data by URL. These will be narrowed down
- // by MergeURLWithSync() to include only the entries different from sync
- // server data.
- std::map<GURL, URLRow> new_db_urls;
- std::map<GURL, std::vector<VisitRow>> local_visit_vectors;
-
- if (!GetValidURLsAndVisits(&local_visit_vectors, &new_db_urls)) {
- RecordDatabaseError(SyncTypedUrlDatabaseError::kMergeFullSyncDataRead);
- return syncer::ModelError(
- FROM_HERE, "Could not get the typed_url entries from HistoryBackend.");
- }
-
- // New sync data organized for different write operations to history backend.
- std::vector<URLRow> new_synced_urls;
- std::vector<URLRow> updated_synced_urls;
- std::vector<URLWithVisits> new_synced_visits;
-
- // Iterate through entity_data and check for all the urls that
- // sync already knows about. MergeURLWithSync() will remove urls that
- // are the same as the synced ones from `new_db_urls`.
- for (const std::unique_ptr<syncer::EntityChange>& entity_change :
- entity_data) {
- DCHECK(entity_change->data().specifics.has_typed_url());
- const sync_pb::TypedUrlSpecifics& specifics =
- entity_change->data().specifics.typed_url();
- if (ShouldIgnoreUrl(GURL(specifics.url()))) {
- continue;
- }
-
- // Ignore old sync urls that don't have any transition data stored with
- // them, or transition data that does not match the visit data (will be
- // deleted later by GarbageCollectionDirective).
- if (specifics.visit_transitions_size() == 0 ||
- specifics.visit_transitions_size() != specifics.visits_size()) {
- DCHECK_EQ(specifics.visits_size(), specifics.visit_transitions_size());
- DLOG(WARNING)
- << "Ignoring obsolete sync url with no visit transition info.";
-
- continue;
- }
- MergeURLWithSync(specifics, &new_db_urls, &local_visit_vectors,
- &new_synced_urls, &new_synced_visits,
- &updated_synced_urls);
- }
-
- absl::optional<syncer::ModelError> error =
- WriteToHistoryBackend(&new_synced_urls, &updated_synced_urls, nullptr,
- &new_synced_visits, nullptr);
- if (error) {
- RecordDatabaseError(SyncTypedUrlDatabaseError::kMergeFullSyncDataWriteData);
- return error;
- }
-
- // Update storage key here first, and then send updated typed URL to sync
- // below, otherwise processor will have duplicate entries.
- for (const std::unique_ptr<syncer::EntityChange>& entity_change :
- entity_data) {
- DCHECK(entity_change->data().specifics.has_typed_url());
- std::string storage_key = GetStorageKeyInternal(
- entity_change->data().specifics.typed_url().url());
- if (storage_key.empty()) {
- // ignore entity change
- change_processor()->UntrackEntityForClientTagHash(
- entity_change->data().client_tag_hash);
- } else {
- change_processor()->UpdateStorageKey(entity_change->data(), storage_key,
- metadata_change_list.get());
- }
- }
-
- // Send new/updated typed URL to sync.
- for (const auto& [url, url_row] : new_db_urls) {
- SendTypedURLToProcessor(url_row, local_visit_vectors[url],
- metadata_change_list.get());
- }
-
- absl::optional<syncer::ModelError> metadata_error =
- change_processor()->GetError();
- if (metadata_error) {
- RecordDatabaseError(
- SyncTypedUrlDatabaseError::kMergeFullSyncDataWriteMetadata);
- }
- return metadata_error;
-}
-
-absl::optional<syncer::ModelError>
-TypedURLSyncBridge::ApplyIncrementalSyncChanges(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityChangeList entity_changes) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- std::vector<GURL> pending_deleted_urls;
- std::vector<URLWithVisits> new_synced_visits;
- std::vector<VisitRow> deleted_visits;
- std::vector<URLRow> updated_synced_urls;
- std::vector<URLRow> new_synced_urls;
-
- for (const std::unique_ptr<syncer::EntityChange>& entity_change :
- entity_changes) {
- if (entity_change->type() == syncer::EntityChange::ACTION_DELETE) {
- URLRow url_row;
- int64_t url_id = TypedURLSyncMetadataDatabase::StorageKeyToURLID(
- entity_change->storage_key());
- if (!history_backend_->GetURLByID(url_id, &url_row)) {
- // Ignoring the case that there is no matching URLRow with URLID
- // `url_id`.
- continue;
- }
-
- pending_deleted_urls.push_back(url_row.url());
- continue;
- }
-
- DCHECK(entity_change->data().specifics.has_typed_url());
- const sync_pb::TypedUrlSpecifics& specifics =
- entity_change->data().specifics.typed_url();
-
- GURL url(specifics.url());
-
- if (ShouldIgnoreUrl(url)) {
- continue;
- }
-
- DCHECK(specifics.visits_size());
- sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(specifics);
- if (filtered_url.visits_size() == 0) {
- continue;
- }
-
- UpdateFromSync(filtered_url, &new_synced_visits, &deleted_visits,
- &updated_synced_urls, &new_synced_urls);
- }
-
- absl::optional<syncer::ModelError> error = WriteToHistoryBackend(
- &new_synced_urls, &updated_synced_urls, &pending_deleted_urls,
- &new_synced_visits, &deleted_visits);
- if (error) {
- RecordDatabaseError(
- SyncTypedUrlDatabaseError::kApplyIncrementalSyncChangesWriteData);
- return error;
- }
-
- // New entities were either ignored or written to history DB and assigned a
- // storage key. Notify processor about updated storage keys.
- for (const std::unique_ptr<syncer::EntityChange>& entity_change :
- entity_changes) {
- if (entity_change->type() == syncer::EntityChange::ACTION_ADD) {
- std::string storage_key = GetStorageKeyInternal(
- entity_change->data().specifics.typed_url().url());
- if (storage_key.empty()) {
- // ignore entity change
- change_processor()->UntrackEntityForClientTagHash(
- entity_change->data().client_tag_hash);
- } else {
- change_processor()->UpdateStorageKey(entity_change->data(), storage_key,
- metadata_change_list.get());
- }
- }
- }
-
- absl::optional<syncer::ModelError> metadata_error =
- change_processor()->GetError();
- if (metadata_error) {
- RecordDatabaseError(
- SyncTypedUrlDatabaseError::kApplyIncrementalSyncChangesWriteMetadata);
- }
- return metadata_error;
-}
-
-void TypedURLSyncBridge::GetData(StorageKeyList storage_keys,
- DataCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- auto batch = std::make_unique<syncer::MutableDataBatch>();
- for (const std::string& key : storage_keys) {
- URLRow url_row;
- URLID url_id = TypedURLSyncMetadataDatabase::StorageKeyToURLID(key);
-
- if (!history_backend_->GetURLByID(url_id, &url_row)) {
- // Ignoring the case which no matching URLRow with URLID `url_id`.
- DLOG(ERROR) << "Could not find URL for id: " << url_id;
- continue;
- }
-
- std::vector<VisitRow> visits_vector;
- if (!FixupURLAndGetVisits(&url_row, &visits_vector) ||
- visits_vector.empty()) {
- continue;
- }
- std::unique_ptr<syncer::EntityData> entity_data =
- CreateEntityData(url_row, visits_vector);
- if (!entity_data) {
- // Cannot create EntityData, ex. no TYPED visits.
- continue;
- }
-
- batch->Put(key, std::move(entity_data));
- }
-
- std::move(callback).Run(std::move(batch));
-}
-
-void TypedURLSyncBridge::GetAllDataForDebugging(DataCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- std::vector<URLRow> typed_urls;
- if (!history_backend_->GetAllTypedURLs(&typed_urls)) {
- change_processor()->ReportError(
- {FROM_HERE, "Could not get the typed_url entries."});
- return;
- }
-
- auto batch = std::make_unique<syncer::MutableDataBatch>();
- for (URLRow& url : typed_urls) {
- std::vector<VisitRow> visits_vector;
- if (!FixupURLAndGetVisits(&url, &visits_vector) || visits_vector.empty()) {
- continue;
- }
- std::unique_ptr<syncer::EntityData> entity_data =
- CreateEntityData(url, visits_vector);
- if (!entity_data) {
- // Cannot create EntityData, ex. no TYPED visits.
- continue;
- }
-
- batch->Put(GetStorageKeyFromURLRow(url), std::move(entity_data));
- }
-
- std::move(callback).Run(std::move(batch));
-}
-
-// Must be exactly the value of GURL::spec() for backwards comparability with
-// the previous (Directory + SyncableService) iteration of sync integration.
-// This can be large but it is assumed that this is not held in memory at steady
-// state.
-std::string TypedURLSyncBridge::GetClientTag(
- const syncer::EntityData& entity_data) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(entity_data.specifics.has_typed_url())
- << "EntityData does not have typed urls specifics.";
-
- return entity_data.specifics.typed_url().url();
-}
-
-// Prefer to use URLRow::id() to uniquely identify entities when coordinating
-// with sync because it has a significantly low memory cost than a URL.
-std::string TypedURLSyncBridge::GetStorageKey(
- const syncer::EntityData& entity_data) {
- NOTREACHED() << "TypedURLSyncBridge do not support GetStorageKey.";
- return std::string();
-}
-
-bool TypedURLSyncBridge::SupportsGetStorageKey() const {
- return false;
-}
-
-void TypedURLSyncBridge::OnURLVisited(HistoryBackend* history_backend,
- const URLRow& url_row,
- const VisitRow& visit_row) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(sync_metadata_database_);
- DCHECK_GE(url_row.typed_count(), 0);
-
- if (processing_syncer_changes_) {
- return; // These are changes originating from us, ignore.
- }
-
- if (!change_processor()->IsTrackingMetadata()) {
- return; // Sync processor not yet ready, don't sync.
- }
- if (!ShouldSyncVisit(url_row.typed_count(), visit_row.transition)) {
- return;
- }
-
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
- CreateMetadataChangeList();
-
- UpdateSyncFromLocal(url_row, /*is_from_expiration=*/false,
- metadata_change_list.get());
-}
-
-void TypedURLSyncBridge::OnURLsModified(HistoryBackend* history_backend,
- const std::vector<URLRow>& changed_urls,
- bool is_from_expiration) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(sync_metadata_database_);
-
- if (processing_syncer_changes_) {
- return; // These are changes originating from us, ignore.
- }
-
- if (!change_processor()->IsTrackingMetadata()) {
- return; // Sync processor not yet ready, don't sync.
- }
-
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
- CreateMetadataChangeList();
-
- for (const URLRow& row : changed_urls) {
- DCHECK_GE(row.typed_count(), 0);
- // If there were any errors updating the sync node, just ignore them and
- // continue on to process the next URL.
- UpdateSyncFromLocal(row, is_from_expiration, metadata_change_list.get());
- }
-}
-
-void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend,
- bool all_history,
- bool expired,
- const std::vector<URLRow>& deleted_rows,
- const std::set<GURL>& favicon_urls) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(sync_metadata_database_);
-
- if (processing_syncer_changes_) {
- return; // These are changes originating from us, ignore.
- }
-
- if (!change_processor()->IsTrackingMetadata()) {
- return; // Sync processor not yet ready, don't sync.
- }
-
- // Ignore URLs expired due to old age (we don't want to sync them as deletions
- // to avoid extra traffic up to the server, and also to make sure that a
- // client with a bad clock setting won't go on an expiration rampage and
- // delete all history from every client). The server will gracefully age out
- // the sync DB entries when they've been idle for long enough.
- if (expired) {
- // Delete metadata from the DB and ask the processor to untrack the entries.
- for (const URLRow& row : deleted_rows) {
- ExpireMetadataForURL(row);
- }
- return;
- }
-
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
- CreateMetadataChangeList();
-
- if (all_history) {
- auto batch = std::make_unique<syncer::MetadataBatch>();
- if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) {
- RecordDatabaseError(
- SyncTypedUrlDatabaseError::kOnURLsDeletedReadMetadata);
- change_processor()->ReportError({FROM_HERE,
- "Failed reading typed url metadata from "
- "TypedURLSyncMetadataDatabase."});
- return;
- }
-
- syncer::EntityMetadataMap metadata_map(batch->TakeAllMetadata());
- for (const auto& [storage_key, metadata] : metadata_map) {
- change_processor()->Delete(storage_key, metadata_change_list.get());
- }
- } else {
- // Delete rows.
- for (const URLRow& row : deleted_rows) {
- std::string storage_key = GetStorageKeyFromURLRow(row);
- change_processor()->Delete(storage_key, metadata_change_list.get());
- }
- }
-}
-
-void TypedURLSyncBridge::OnVisitUpdated(const VisitRow& visit,
- VisitUpdateReason reason) {}
-
-void TypedURLSyncBridge::OnVisitDeleted(const VisitRow& visit) {}
-
-void TypedURLSyncBridge::Init() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- history_backend_observation_.Observe(history_backend_.get());
- LoadMetadata();
-}
-
-void TypedURLSyncBridge::OnDatabaseError() {
- sync_metadata_database_ = nullptr;
- RecordDatabaseError(SyncTypedUrlDatabaseError::kOnDatabaseError);
- change_processor()->ReportError(
- {FROM_HERE, "HistoryDatabase encountered error"});
-}
-
-// static
-bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
- const URLRow& url,
- const std::vector<VisitRow>& visits,
- sync_pb::TypedUrlSpecifics* typed_url) {
- DCHECK(!url.last_visit().is_null());
- DCHECK(!visits.empty());
- DCHECK_EQ(url.last_visit().ToInternalValue(),
- visits.back().visit_time.ToInternalValue());
-
- typed_url->set_url(url.url().spec());
- typed_url->set_title(base::UTF16ToUTF8(url.title()));
- typed_url->set_hidden(url.hidden());
-
- DCHECK(VisitsAreSorted(visits));
-
- bool only_typed = false;
- int skip_count = 0;
-
- if (!HasTypedUrl(visits)) {
- // This URL has no TYPED visits, don't sync it
- return false;
- }
-
- if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
- int typed_count = 0;
- int total = 0;
- // Walk the passed-in visit vector and count the # of typed visits.
- for (const VisitRow& visit : visits) {
- // We ignore reload visits.
- if (PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_RELOAD)) {
- continue;
- }
- ++total;
- if (PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_TYPED)) {
- ++typed_count;
- }
- }
-
- // We should have at least one typed visit. This can sometimes happen if
- // the history DB has an inaccurate count for some reason (there's been
- // bugs in the history code in the past which has left users in the wild
- // with incorrect counts - http://crbug.com/84258).
- DCHECK_GT(typed_count, 0);
-
- if (typed_count > kMaxTypedUrlVisits) {
- only_typed = true;
- skip_count = typed_count - kMaxTypedUrlVisits;
- } else if (total > kMaxTypedUrlVisits) {
- skip_count = total - kMaxTypedUrlVisits;
- }
- }
-
- for (const VisitRow& visit : visits) {
- // Skip reload visits.
- if (PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_RELOAD)) {
- continue;
- }
-
- // If we only have room for typed visits, then only add typed visits.
- if (only_typed && !PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_TYPED)) {
- continue;
- }
-
- if (skip_count > 0) {
- // We have too many entries to fit, so we need to skip the oldest ones.
- // Only skip typed URLs if there are too many typed URLs to fit.
- if (only_typed || !PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_TYPED)) {
- --skip_count;
- continue;
- }
- }
- typed_url->add_visits(visit.visit_time.ToInternalValue());
- typed_url->add_visit_transitions(visit.transition);
- }
- DCHECK_EQ(skip_count, 0);
-
- CHECK_GT(typed_url->visits_size(), 0);
- CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
- CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
-
- return true;
-}
-
-// static
-TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
- const sync_pb::TypedUrlSpecifics& sync_url,
- const URLRow& url,
- std::vector<VisitRow>* visits,
- URLRow* new_url,
- std::vector<VisitInfo>* new_visits) {
- DCHECK(new_url);
- DCHECK_EQ(sync_url.url(), url.url().spec());
- DCHECK_EQ(sync_url.url(), new_url->url().spec());
- DCHECK(visits->size());
- DCHECK_GT(sync_url.visits_size(), 0);
- CHECK_EQ(sync_url.visits_size(), sync_url.visit_transitions_size());
-
- // Convert these values only once.
- std::u16string sync_url_title(base::UTF8ToUTF16(sync_url.title()));
- base::Time sync_url_last_visit = base::Time::FromInternalValue(
- sync_url.visits(sync_url.visits_size() - 1));
-
- // This is a bitfield representing what we'll need to update with the output
- // value.
- MergeResult different = DIFF_NONE;
-
- // Check if the non-incremented values changed.
- if ((sync_url_title != url.title()) || (sync_url.hidden() != url.hidden())) {
- // Use the values from the most recent visit.
- if (sync_url_last_visit >= url.last_visit()) {
- new_url->set_title(sync_url_title);
- new_url->set_hidden(sync_url.hidden());
- different |= DIFF_LOCAL_ROW_CHANGED;
- } else {
- new_url->set_title(url.title());
- new_url->set_hidden(url.hidden());
- different |= DIFF_UPDATE_NODE;
- }
- } else {
- // No difference.
- new_url->set_title(url.title());
- new_url->set_hidden(url.hidden());
- }
-
- size_t sync_url_num_visits = sync_url.visits_size();
- size_t history_num_visits = visits->size();
- size_t sync_url_visit_index = 0;
- size_t history_visit_index = 0;
- base::Time earliest_history_time = (*visits)[0].visit_time;
- // Walk through the two sets of visits and figure out if any new visits were
- // added on either side.
- while (sync_url_visit_index < sync_url_num_visits ||
- history_visit_index < history_num_visits) {
- // Time objects are initialized to "earliest possible time".
- base::Time sync_url_time, history_time;
- if (sync_url_visit_index < sync_url_num_visits) {
- sync_url_time =
- base::Time::FromInternalValue(sync_url.visits(sync_url_visit_index));
- }
- if (history_visit_index < history_num_visits) {
- history_time = (*visits)[history_visit_index].visit_time;
- }
- if (sync_url_visit_index >= sync_url_num_visits ||
- (history_visit_index < history_num_visits &&
- sync_url_time > history_time)) {
- // We found a visit in the history DB that doesn't exist in the sync DB,
- // so mark the sync_url as modified so the caller will update the sync
- // node.
- different |= DIFF_UPDATE_NODE;
- ++history_visit_index;
- } else if (history_visit_index >= history_num_visits ||
- sync_url_time < history_time) {
- // Found a visit in the sync node that doesn't exist in the history DB, so
- // add it to our list of new visits and set the appropriate flag so the
- // caller will update the history DB.
- // If the sync_url visit is older than any existing visit in the history
- // DB, don't re-add it - this keeps us from resurrecting visits that were
- // aged out locally.
- //
- // TODO(sync): This extra check should be unnecessary now that filtering
- // expired visits is performed separately. Non-expired visits older than
- // the earliest existing history visits should still be synced, so this
- // check should be removed.
- if (sync_url_time > earliest_history_time) {
- different |= DIFF_LOCAL_VISITS_ADDED;
- new_visits->push_back(VisitInfo(
- sync_url_time, ui::PageTransitionFromInt(sync_url.visit_transitions(
- sync_url_visit_index))));
- }
- // This visit is added to visits below.
- ++sync_url_visit_index;
- } else {
- // Same (already synced) entry found in both DBs - no need to do anything.
- ++sync_url_visit_index;
- ++history_visit_index;
- }
- }
-
- DCHECK(VisitsAreSorted(*visits));
- if (different & DIFF_LOCAL_VISITS_ADDED) {
- // If the server does not have the same visits as the local db, then the
- // new visits from the server need to be added to the vector containing
- // local visits. These visits will be passed to the server.
- // Insert new visits into the appropriate place in the visits vector.
- auto visit_ix = visits->begin();
- for (const auto& [new_visit_time, new_page_transition] : *new_visits) {
- while (visit_ix != visits->end() &&
- new_visit_time > visit_ix->visit_time) {
- ++visit_ix;
- }
- visit_ix = visits->insert(
- visit_ix,
- VisitRow(url.id(), new_visit_time, /*arg_referring_visit=*/0,
- new_page_transition, /*arg_segment_id=*/0,
- HistoryBackend::IsTypedIncrement(new_page_transition),
- /*arg_opener_visit=*/0));
- ++visit_ix;
- }
- }
- DCHECK(VisitsAreSorted(*visits));
-
- new_url->set_last_visit(visits->back().visit_time);
- return different;
-}
-
-// static
-void TypedURLSyncBridge::DiffVisits(
- const std::vector<VisitRow>& history_visits,
- const sync_pb::TypedUrlSpecifics& sync_specifics,
- std::vector<VisitInfo>* new_visits,
- std::vector<VisitRow>* removed_visits) {
- DCHECK(new_visits);
- size_t old_visit_count = history_visits.size();
- size_t new_visit_count = sync_specifics.visits_size();
- size_t old_index = 0;
- size_t new_index = 0;
- while (old_index < old_visit_count && new_index < new_visit_count) {
- base::Time new_visit_time =
- base::Time::FromInternalValue(sync_specifics.visits(new_index));
- if (history_visits[old_index].visit_time < new_visit_time) {
- if (new_index > 0 && removed_visits) {
- // If there are visits missing from the start of the node, that
- // means that they were probably clipped off due to our code that
- // limits the size of the sync nodes - don't delete them from our
- // local history.
- removed_visits->push_back(history_visits[old_index]);
- }
- ++old_index;
- } else if (history_visits[old_index].visit_time > new_visit_time) {
- new_visits->push_back(VisitInfo(
- new_visit_time, ui::PageTransitionFromInt(
- sync_specifics.visit_transitions(new_index))));
- ++new_index;
- } else {
- ++old_index;
- ++new_index;
- }
- }
-
- if (removed_visits) {
- for (; old_index < old_visit_count; ++old_index) {
- removed_visits->push_back(history_visits[old_index]);
- }
- }
-
- for (; new_index < new_visit_count; ++new_index) {
- new_visits->push_back(VisitInfo(
- base::Time::FromInternalValue(sync_specifics.visits(new_index)),
- ui::PageTransitionFromInt(
- sync_specifics.visit_transitions(new_index))));
- }
-}
-
-// static
-void TypedURLSyncBridge::UpdateURLRowFromTypedUrlSpecifics(
- const sync_pb::TypedUrlSpecifics& typed_url,
- URLRow* new_url) {
- DCHECK_GT(typed_url.visits_size(), 0);
- CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
- if (!new_url->url().is_valid()) {
- new_url->set_url(GURL(typed_url.url()));
- }
- new_url->set_title(base::UTF8ToUTF16(typed_url.title()));
- new_url->set_hidden(typed_url.hidden());
- // Only provide the initial value for the last_visit field - after that, let
- // the history code update the last_visit field on its own.
- if (new_url->last_visit().is_null()) {
- new_url->set_last_visit(base::Time::FromInternalValue(
- typed_url.visits(typed_url.visits_size() - 1)));
- }
-}
-
-void TypedURLSyncBridge::LoadMetadata() {
- if (!history_backend_ || !sync_metadata_database_) {
- RecordDatabaseError(SyncTypedUrlDatabaseError::kLoadMetadataOpen);
- change_processor()->ReportError(
- {FROM_HERE, "Failed to load TypedURLSyncMetadataDatabase."});
- return;
- }
-
- auto batch = std::make_unique<syncer::MetadataBatch>();
- if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) {
- RecordDatabaseError(SyncTypedUrlDatabaseError::kLoadMetadataRead);
- change_processor()->ReportError({FROM_HERE,
- "Failed reading typed url metadata from "
- "TypedURLSyncMetadataDatabase."});
- return;
- }
- change_processor()->ModelReadyToSync(std::move(batch));
-}
-
-void TypedURLSyncBridge::MergeURLWithSync(
- const sync_pb::TypedUrlSpecifics& server_typed_url,
- std::map<GURL, URLRow>* local_typed_urls,
- std::map<GURL, std::vector<VisitRow>>* local_visit_vectors,
- std::vector<URLRow>* new_synced_urls,
- std::vector<URLWithVisits>* new_synced_visits,
- std::vector<URLRow>* updated_synced_urls) {
- DCHECK_NE(0, server_typed_url.visits_size());
- DCHECK_EQ(server_typed_url.visits_size(),
- server_typed_url.visit_transitions_size());
-
- // Ignore empty urls.
- if (server_typed_url.url().empty()) {
- DVLOG(1) << "Ignoring empty URL in sync DB";
- return;
- }
- // Now, get rid of the expired visits. If there are no un-expired visits
- // left, ignore this url - any local data should just replace it.
- sync_pb::TypedUrlSpecifics sync_url = FilterExpiredVisits(server_typed_url);
- if (sync_url.visits_size() == 0) {
- DVLOG(1) << "Ignoring expired URL in sync DB: " << sync_url.url();
- return;
- }
-
- // Check if local db already has the url from sync.
- auto it = local_typed_urls->find(GURL(sync_url.url()));
- if (it == local_typed_urls->end()) {
- // There are no matching typed urls from the local db, check for untyped
- URLRow untyped_url(GURL(sync_url.url()));
-
- // The URL may still exist in the local db if it is an untyped url.
- // An untyped url will transition to a typed url after receiving visits
- // from sync, and sync should receive any visits already existing locally
- // for the url, so the full list of visits is consistent.
- bool is_existing_url =
- history_backend_->GetURL(untyped_url.url(), &untyped_url);
- if (is_existing_url) {
- // Add a new entry to `local_typed_urls`, and set the iterator to it.
- std::vector<VisitRow> untyped_visits;
- // TODO(crbug.com/1075573): We early return on urls with all visits
- // expired. It does not feel right as we might get new non-expired visits
- // through sync.
- if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits) ||
- untyped_visits.empty()) {
- return;
- }
- (*local_visit_vectors)[untyped_url.url()] = untyped_visits;
-
- // Store row info that will be used to update sync's visits.
- (*local_typed_urls)[untyped_url.url()] = untyped_url;
-
- // Set iterator `it` to point to this entry.
- it = local_typed_urls->find(untyped_url.url());
- DCHECK(it != local_typed_urls->end());
- // Continue with merge below.
- } else {
- // The url is new to the local history DB.
- // Create new db entry for url.
- URLRow new_url(GURL(sync_url.url()));
- UpdateURLRowFromTypedUrlSpecifics(sync_url, &new_url);
- new_synced_urls->push_back(new_url);
-
- // Add entries for url visits.
- std::vector<VisitInfo> added_visits;
- size_t visit_count = sync_url.visits_size();
-
- for (size_t index = 0; index < visit_count; ++index) {
- base::Time visit_time =
- base::Time::FromInternalValue(sync_url.visits(index));
- ui::PageTransition transition =
- ui::PageTransitionFromInt(sync_url.visit_transitions(index));
- added_visits.emplace_back(visit_time, transition);
- }
- new_synced_visits->emplace_back(new_url.url(), added_visits);
- return;
- }
- }
-
- // Same URL exists in sync data and in history data - compare the
- // entries to see if there's any difference.
- auto& [url, url_row] = *it;
- std::vector<VisitRow>& visits = (*local_visit_vectors)[url];
- std::vector<VisitInfo> added_visits;
-
- // Empty URLs should be filtered out by ShouldIgnoreUrl() previously.
- DCHECK(!url_row.url().spec().empty());
-
- // Initialize fields in `new_url` to the same values as the fields in
- // the existing URLRow in the history DB. This is needed because we
- // overwrite the existing value in WriteToHistoryBackend(), but some of
- // the values in that structure are not synced (like typed_count).
- URLRow new_url(url_row);
-
- MergeResult difference =
- MergeUrls(sync_url, url_row, &visits, &new_url, &added_visits);
-
- if (difference != DIFF_NONE) {
- url_row = new_url;
- if (difference & DIFF_UPDATE_NODE) {
- // We don't want to resurrect old visits that have been aged out by
- // other clients, so remove all visits that are older than the
- // earliest existing visit in the sync node.
- //
- // TODO(sync): This logic should be unnecessary now that filtering of
- // expired visits is performed separately. Non-expired visits older than
- // the earliest existing sync visits should still be synced, so this
- // logic should be removed.
- if (sync_url.visits_size() > 0) {
- base::Time earliest_visit =
- base::Time::FromInternalValue(sync_url.visits(0));
- for (auto i = visits.begin();
- i != visits.end() && i->visit_time < earliest_visit;) {
- i = visits.erase(i);
- }
- // Should never be possible to delete all the items, since the
- // visit vector contains newer local visits it will keep and/or the
- // visits in typed_url.visits newer than older local visits.
- DCHECK_GT(visits.size(), 0U);
- }
- DCHECK_EQ(new_url.last_visit().ToInternalValue(),
- visits.back().visit_time.ToInternalValue());
- }
- if (difference & DIFF_LOCAL_ROW_CHANGED) {
- // Add entry to updated_synced_urls to update the local db.
- DCHECK_EQ(url_row.id(), new_url.id());
- updated_synced_urls->push_back(new_url);
- }
- if (difference & DIFF_LOCAL_VISITS_ADDED) {
- // Add entry with new visits to new_synced_visits to update the local db.
- new_synced_visits->emplace_back(url, added_visits);
- }
- } else {
- // No difference in urls, erase from map
- local_typed_urls->erase(it);
- }
-}
-
-void TypedURLSyncBridge::UpdateFromSync(
- const sync_pb::TypedUrlSpecifics& typed_url,
- std::vector<URLWithVisits>* visits_to_add,
- std::vector<VisitRow>* visits_to_remove,
- std::vector<URLRow>* updated_urls,
- std::vector<URLRow>* new_urls) {
- URLRow new_url(GURL(typed_url.url()));
- std::vector<VisitRow> existing_visits;
- bool existing_url = history_backend_->GetURL(new_url.url(), &new_url);
- if (existing_url) {
- // This URL already exists locally - fetch the visits so we can
- // merge them below.
- // TODO(crbug.com/1075573): We early return on urls with all visits
- // expired. It does not feel right as we might get new non-expired visits
- // through sync.
- if (!FixupURLAndGetVisits(&new_url, &existing_visits) ||
- existing_visits.empty()) {
- return;
- }
- }
- visits_to_add->emplace_back(new_url.url(), std::vector<VisitInfo>());
-
- // Update the URL with information from the typed URL.
- UpdateURLRowFromTypedUrlSpecifics(typed_url, &new_url);
-
- // Figure out which visits we need to add.
- DiffVisits(existing_visits, typed_url, &visits_to_add->back().visits,
- visits_to_remove);
-
- if (existing_url) {
- updated_urls->push_back(new_url);
- } else {
- new_urls->push_back(new_url);
- }
-}
-
-void TypedURLSyncBridge::UpdateSyncFromLocal(
- URLRow row,
- bool is_from_expiration,
- syncer::MetadataChangeList* metadata_change_list) {
- if (ShouldIgnoreUrl(row.url())) {
- return;
- }
-
- // Get the visits for this node.
- std::vector<VisitRow> visit_vector;
- if (!FixupURLAndGetVisits(&row, &visit_vector)) {
- return;
- }
-
- // We want to also deal with URLs that have all visits expired (that return
- // empty `visit_vector` from FixupURLAndGetVisits()) so that these get expired
- // or deleted.
- if (HasTypedUrl(visit_vector)) {
- SendTypedURLToProcessor(row, visit_vector, metadata_change_list);
- } else {
- std::string storage_key = GetStorageKeyFromURLRow(row);
- // If the URL has no typed visits any more we should get rid of it. It is
- // possible that this URL never had typed visits and thus it has no sync
- // entity and no sync metadata. We do not need to check for this case
- // as all the code below is no-op if there is no sync metadata for `row`.
- if (is_from_expiration) {
- // Only remove its metadata as we do not sync up deletions for expired
- // entities (see the comment in OnURLsDeleted()).
- ExpireMetadataForURL(row);
- } else {
- // This change is caused by the user explicitly removing some visits, we
- // should also remove the entity from sync.
- change_processor()->Delete(storage_key, metadata_change_list);
- }
- }
-}
-
-void TypedURLSyncBridge::ExpireMetadataForURL(const URLRow& row) {
- std::string storage_key = GetStorageKeyFromURLRow(row);
- // The following functions need to tolerate if there exists no metadata
- // for `storage_key` as we might call this function multiple times for a given
- // url.
- sync_metadata_database_->ClearEntityMetadata(syncer::TYPED_URLS, storage_key);
- change_processor()->UntrackEntityForStorageKey(storage_key);
-}
-
-absl::optional<syncer::ModelError> TypedURLSyncBridge::WriteToHistoryBackend(
- const std::vector<URLRow>* new_urls,
- const std::vector<URLRow>* updated_urls,
- const std::vector<GURL>* deleted_urls,
- const std::vector<URLWithVisits>* new_visits,
- const std::vector<VisitRow>* deleted_visits) {
- DCHECK_EQ(processing_syncer_changes_, false);
- // Set flag to stop accepting history change notifications from backend.
- base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
-
- if (deleted_urls && !deleted_urls->empty()) {
- history_backend_->DeleteURLs(*deleted_urls);
- }
-
- if (new_urls) {
- history_backend_->AddPagesWithDetails(*new_urls, SOURCE_SYNCED);
- }
-
- if (updated_urls) {
- // This is an existing entry in the URL database. We don't verify the
- // visit_count or typed_count values here, because either one (or both)
- // could be zero in the case of bookmarks, or in the case of a URL
- // transitioning from non-typed to typed as a result of this sync.
- // In the field we sometimes run into errors on specific URLs. It's OK
- // to just continue on (we can try writing again on the next model
- // association).
- history_backend_->UpdateURLs(*updated_urls);
- }
-
- if (new_visits) {
- for (const auto& [url, visit_infos] : *new_visits) {
- // If there are no visits to add, just skip this.
- if (visit_infos.empty()) {
- continue;
- }
- if (!history_backend_->AddVisits(url, visit_infos, SOURCE_SYNCED)) {
- return syncer::ModelError(FROM_HERE,
- "Could not add visits to HistoryBackend.");
- }
- }
- }
-
- if (deleted_visits) {
- if (!history_backend_->RemoveVisits(*deleted_visits,
- DeletionInfo::Reason::kOther)) {
- return syncer::ModelError(FROM_HERE,
- "Could not remove visits from HistoryBackend.");
- // This is bad news, since it means we may end up resurrecting history
- // entries on the next reload. It's unavoidable so we'll just keep on
- // syncing.
- }
- }
-
- return {};
-}
-
-sync_pb::TypedUrlSpecifics TypedURLSyncBridge::FilterExpiredVisits(
- const sync_pb::TypedUrlSpecifics& source) {
- // Make a copy of the source, then regenerate the visits.
- sync_pb::TypedUrlSpecifics specifics(source);
- specifics.clear_visits();
- specifics.clear_visit_transitions();
- for (int i = 0; i < source.visits_size(); ++i) {
- base::Time time = base::Time::FromInternalValue(source.visits(i));
- if (!history_backend_->IsExpiredVisitTime(time)) {
- specifics.add_visits(source.visits(i));
- specifics.add_visit_transitions(source.visit_transitions(i));
- }
- }
- DCHECK(specifics.visits_size() == specifics.visit_transitions_size());
- return specifics;
-}
-
-bool TypedURLSyncBridge::ShouldIgnoreUrl(const GURL& url) {
- // Ignore empty URLs. Not sure how this can happen (maybe import from other
- // busted browsers, or misuse of the history API, or just plain bugs) but we
- // can't deal with them.
- if (url.spec().empty()) {
- return true;
- }
-
- // Ignore local file URLs.
- if (url.SchemeIsFile()) {
- return true;
- }
-
- // Ignore localhost URLs.
- if (net::IsLocalhost(url)) {
- return true;
- }
-
- // Ignore username and password, since history backend will remove user name
- // and password in database_utils::GurlToDatabaseUrl and send
- // username/password removed url to sync later.
- if (url.has_username() || url.has_password()) {
- return true;
- }
-
- return false;
-}
-
-bool TypedURLSyncBridge::ShouldIgnoreVisits(
- const std::vector<VisitRow>& visits) {
- // We ignore URLs that were imported, but have never been visited by
- // chromium.
- static const int kFirstImportedSource = SOURCE_FIREFOX_IMPORTED;
- VisitSourceMap map;
- if (!history_backend_->GetVisitsSource(visits, &map)) {
- return false; // If we can't read the visit, assume it's not imported.
- }
-
- // Walk the list of visits and look for a non-imported item.
- for (const VisitRow& visit : visits) {
- if (map.count(visit.visit_id) == 0 ||
- map[visit.visit_id] < kFirstImportedSource) {
- return false;
- }
- }
- // We only saw imported visits, so tell the caller to ignore them.
- return true;
-}
-
-bool TypedURLSyncBridge::ShouldSyncVisit(int typed_count,
- ui::PageTransition transition) {
- // Just use an ad-hoc criteria to determine whether to ignore this
- // notification. For most users, the distribution of visits is roughly a bell
- // curve with a long tail - there are lots of URLs with < 5 visits so we want
- // to make sure we sync up every visit to ensure the proper ordering of
- // suggestions. But there are relatively few URLs with > 10 visits, and those
- // tend to be more broadly distributed such that there's no need to sync up
- // every visit to preserve their relative ordering.
- return (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) &&
- (typed_count < kTypedUrlVisitThrottleThreshold ||
- (typed_count % kTypedUrlVisitThrottleMultiple) == 0));
-}
-
-bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url,
- std::vector<VisitRow>* visits) {
- if (!history_backend_->GetMostRecentVisitsForURL(url->id(), kMaxVisitsToFetch,
- visits)) {
- // Couldn't load the visits for this URL due to some kind of DB error.
- // Don't bother writing this URL to the history DB (if we ignore the
- // error and continue, we might end up duplicating existing visits).
- DLOG(ERROR) << "Could not load visits for url: " << url->url();
- return false;
- }
-
- // Sometimes (due to a bug elsewhere in the history or sync code, or due to
- // a crash between adding a URL to the history database and updating the
- // visit DB) the visit vector for a URL can be empty. If this happens, just
- // create a new visit whose timestamp is the same as the last_visit time.
- // This is a workaround for http://crbug.com/84258.
- if (visits->empty()) {
- DVLOG(1) << "Found empty visits for URL: " << url->url();
- if (url->last_visit().is_null()) {
- // If modified URL is bookmarked, history backend treats it as modified
- // even if all its visits are deleted. Return empty visits to stop further
- // processing.
- return true;
- }
-
- VisitRow visit(url->id(), url->last_visit(), /*arg_referring_visit=*/0,
- ui::PAGE_TRANSITION_TYPED,
- /*arg_segment_id=*/0,
- /*arg_incremented_omnibox_typed_score=*/true,
- /*arg_opener_visit=*/0);
- visits->push_back(visit);
- }
-
- // GetMostRecentVisitsForURL() returns the data in the opposite order that
- // we need it, so reverse it.
- base::ranges::reverse(*visits);
-
- // Sometimes, the last_visit field in the URL doesn't match the timestamp of
- // the last visit in our visit array (they come from different tables, so
- // crashes/bugs can cause them to mismatch), so just set it here.
- url->set_last_visit(visits->back().visit_time);
- DCHECK(VisitsAreSorted(*visits));
-
- // Removes all visits that are older than the current expiration time. Visits
- // are in ascending order now, so we can check from beginning to check how
- // many expired visits.
- size_t num_expired_visits = 0;
- for (const VisitRow& visit : *visits) {
- base::Time time = visit.visit_time;
- if (history_backend_->IsExpiredVisitTime(time)) {
- ++num_expired_visits;
- } else {
- break;
- }
- }
- if (num_expired_visits != 0) {
- if (num_expired_visits == visits->size()) {
- DVLOG(1) << "All visits are expired for url: " << url->url();
- visits->clear();
- return true;
- }
- visits->erase(visits->begin(), visits->begin() + num_expired_visits);
- }
- DCHECK(VisitsAreSorted(*visits));
-
- return true;
-}
-
-std::unique_ptr<syncer::EntityData> TypedURLSyncBridge::CreateEntityData(
- const URLRow& row,
- const std::vector<VisitRow>& visits) {
- auto entity_data = std::make_unique<syncer::EntityData>();
- sync_pb::TypedUrlSpecifics* specifics =
- entity_data->specifics.mutable_typed_url();
-
- if (!WriteToTypedUrlSpecifics(row, visits, specifics)) {
- // Cannot write to specifics, ex. no TYPED visits.
- return nullptr;
- }
- entity_data->name = row.url().spec();
- return entity_data;
-}
-
-bool TypedURLSyncBridge::GetValidURLsAndVisits(
- std::map<GURL, std::vector<VisitRow>>* url_to_visit,
- std::map<GURL, URLRow>* url_to_urlrow) {
- DCHECK(url_to_visit);
- DCHECK(url_to_urlrow);
-
- std::vector<URLRow> local_typed_urls;
- if (!history_backend_->GetAllTypedURLs(&local_typed_urls)) {
- return false;
- }
- for (URLRow& url : local_typed_urls) {
- DCHECK_EQ(0U, url_to_visit->count(url.url()));
- if (!FixupURLAndGetVisits(&url, &((*url_to_visit)[url.url()])) ||
- ShouldIgnoreUrl(url.url()) ||
- ShouldIgnoreVisits((*url_to_visit)[url.url()])) {
- // Ignore this URL if we couldn't load the visits or if there's some
- // other problem with it (it was empty, or imported and never visited).
- } else {
- // Add url to url_to_urlrow.
- (*url_to_urlrow)[url.url()] = url;
- }
- }
- return true;
-}
-
-std::string TypedURLSyncBridge::GetStorageKeyInternal(const std::string& url) {
- DCHECK(history_backend_);
-
- URLRow existing_url;
- bool is_existing_url = history_backend_->GetURL(GURL(url), &existing_url);
-
- if (!is_existing_url) {
- // The typed url did not save to local history database, so return empty
- // string.
- return std::string();
- }
-
- return GetStorageKeyFromURLRow(existing_url);
-}
-
-void TypedURLSyncBridge::SendTypedURLToProcessor(
- const URLRow& row,
- const std::vector<VisitRow>& visits,
- syncer::MetadataChangeList* metadata_change_list) {
- DCHECK(!visits.empty());
- DCHECK(metadata_change_list);
-
- std::unique_ptr<syncer::EntityData> entity_data =
- CreateEntityData(row, visits);
- if (!entity_data) {
- // Cannot create EntityData, ex. no TYPED visits.
- return;
- }
-
- std::string storage_key = GetStorageKeyFromURLRow(row);
- change_processor()->Put(storage_key, std::move(entity_data),
- metadata_change_list);
-}
-
-} // namespace history
diff --git a/components/history/core/browser/sync/typed_url_sync_bridge.h b/components/history/core/browser/sync/typed_url_sync_bridge.h
deleted file mode 100644
index 2810519f..0000000
--- a/components/history/core/browser/sync/typed_url_sync_bridge.h
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_HISTORY_CORE_BROWSER_SYNC_TYPED_URL_SYNC_BRIDGE_H_
-#define COMPONENTS_HISTORY_CORE_BROWSER_SYNC_TYPED_URL_SYNC_BRIDGE_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/memory/raw_ptr.h"
-#include "base/scoped_observation.h"
-#include "components/history/core/browser/history_backend.h"
-#include "components/history/core/browser/history_backend_observer.h"
-#include "components/history/core/browser/sync/typed_url_sync_metadata_database.h"
-#include "components/sync/model/metadata_change_list.h"
-#include "components/sync/model/model_type_change_processor.h"
-#include "components/sync/model/model_type_sync_bridge.h"
-#include "components/sync/model/sync_error.h"
-
-namespace history {
-
-class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
- public HistoryBackendObserver {
- public:
- // `sync_metadata_store` is owned by `history_backend`, and must outlive
- // TypedURLSyncBridge.
- TypedURLSyncBridge(
- HistoryBackend* history_backend,
- TypedURLSyncMetadataDatabase* sync_metadata_store,
- std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
-
- TypedURLSyncBridge(const TypedURLSyncBridge&) = delete;
- TypedURLSyncBridge& operator=(const TypedURLSyncBridge&) = delete;
-
- ~TypedURLSyncBridge() override;
-
- // syncer::ModelTypeSyncBridge implementation.
- std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
- override;
- absl::optional<syncer::ModelError> MergeFullSyncData(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityChangeList entity_data) override;
- absl::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
- std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
- syncer::EntityChangeList entity_changes) override;
- void GetData(StorageKeyList storage_keys, DataCallback callback) override;
- void GetAllDataForDebugging(DataCallback callback) override;
- std::string GetClientTag(const syncer::EntityData& entity_data) override;
- std::string GetStorageKey(const syncer::EntityData& entity_data) override;
- bool SupportsGetStorageKey() const override;
-
- // HistoryBackendObserver:
- void OnURLVisited(HistoryBackend* history_backend,
- const URLRow& row,
- const VisitRow& visit_row) override;
- void OnURLsModified(HistoryBackend* history_backend,
- const std::vector<URLRow>& changed_urls,
- bool is_from_expiration) override;
- void OnURLsDeleted(HistoryBackend* history_backend,
- bool all_history,
- bool expired,
- const std::vector<URLRow>& deleted_rows,
- const std::set<GURL>& favicon_urls) override;
- void OnVisitUpdated(const VisitRow& visit, VisitUpdateReason reason) override;
- void OnVisitDeleted(const VisitRow& visit) override;
-
- // Must be called after creation and before any operations.
- void Init();
-
- // Called by HistoryBackend when database error is reported through
- // DatabaseErrorCallback.
- void OnDatabaseError();
-
- // Return true if this function successfully converts the passed URL
- // information to a TypedUrlSpecifics structure for writing to the sync DB.
- [[nodiscard]] static bool WriteToTypedUrlSpecifics(
- const URLRow& url,
- const std::vector<VisitRow>& visits,
- sync_pb::TypedUrlSpecifics* specifics);
-
- private:
- friend class TypedURLSyncBridgeTest;
-
- struct URLWithVisits {
- URLWithVisits(const GURL& url, const std::vector<VisitInfo>& visits);
- ~URLWithVisits();
-
- URLWithVisits(URLWithVisits&&);
-
- GURL url;
- std::vector<VisitInfo> visits;
- };
-
- // Bitfield returned from MergeUrls to specify the result of a merge.
- typedef uint32_t MergeResult;
- static const MergeResult DIFF_NONE = 0;
- static const MergeResult DIFF_UPDATE_NODE = 1 << 0;
- static const MergeResult DIFF_LOCAL_ROW_CHANGED = 1 << 1;
- static const MergeResult DIFF_LOCAL_VISITS_ADDED = 1 << 2;
-
- // Merges the URL information in `typed_url` with the URL information from the
- // history database in `url` and `visits`, and returns a bitmask with the
- // results of the merge:
- // DIFF_UPDATE_NODE - changes have been made to `new_url` and `visits` which
- // should be persisted to the sync node.
- // DIFF_LOCAL_ROW_CHANGED - The history data in `new_url` should be persisted
- // to the history DB.
- // DIFF_LOCAL_VISITS_ADDED - `new_visits` contains a list of visits that
- // should be written to the history DB for this URL. Deletions are not
- // written to the DB - each client is left to age out visits on their own.
- static MergeResult MergeUrls(const sync_pb::TypedUrlSpecifics& typed_url,
- const URLRow& url,
- std::vector<VisitRow>* visits,
- URLRow* new_url,
- std::vector<VisitInfo>* new_visits);
-
- // Diffs the set of visits between the history DB and the sync DB, using the
- // sync DB as the canonical copy. Result is the set of `new_visits` and
- // `removed_visits` that can be applied to the history DB to make it match
- // the sync DB version. `removed_visits` can be null if the caller does not
- // care about which visits to remove.
- static void DiffVisits(const std::vector<VisitRow>& history_visits,
- const sync_pb::TypedUrlSpecifics& sync_specifics,
- std::vector<VisitInfo>* new_visits,
- std::vector<VisitRow>* removed_visits);
-
- // Fills `new_url` with formatted data from `typed_url`.
- static void UpdateURLRowFromTypedUrlSpecifics(
- const sync_pb::TypedUrlSpecifics& typed_url,
- URLRow* new_url);
-
- // Synchronously load sync metadata from the TypedURLSyncMetadataDatabase and
- // pass it to the processor so that it can start tracking changes.
- void LoadMetadata();
-
- // Compares `server_typed_url` from the server against local history to decide
- // how to merge any existing data, and updates appropriate data containers to
- // write to server and backend.
- void MergeURLWithSync(const sync_pb::TypedUrlSpecifics& server_typed_url,
- std::map<GURL, URLRow>* local_typed_urls,
- std::map<GURL, std::vector<VisitRow>>* visit_vectors,
- std::vector<URLRow>* new_synced_urls,
- std::vector<URLWithVisits>* new_synced_visits,
- std::vector<URLRow>* updated_synced_urls);
-
- // Given a typed URL in the sync DB, looks for an existing entry in the
- // local history DB and generates a list of visits to add to the
- // history DB to bring it up to date (avoiding duplicates).
- // Updates the passed `visits_to_add` and `visits_to_remove` vectors with the
- // visits to add to/remove from the history DB, and adds a new entry to either
- // `updated_urls` or `new_urls` depending on whether the URL already existed
- // in the history DB.
- void UpdateFromSync(const sync_pb::TypedUrlSpecifics& typed_url,
- std::vector<URLWithVisits>* visits_to_add,
- std::vector<VisitRow>* visits_to_remove,
- std::vector<URLRow>* updated_urls,
- std::vector<URLRow>* new_urls);
-
- // Utility routine that (a) updates an existing sync node or (b) creates a
- // new one for the passed `typed_url` if one does not already exist or (c)
- // removes metadata for `row` if `is_from_expiration` is true and the `row`
- // has no more typed visits.
- void UpdateSyncFromLocal(URLRow row,
- bool is_from_expiration,
- syncer::MetadataChangeList* metadata_change_list);
-
- // Deletes metadata for an expired URL `row` but does not send up the deletion
- // to the server (each client expires them independently). It is an no-op when
- // called on an url with already expired metadata.
- void ExpireMetadataForURL(const URLRow& row);
-
- // Writes new typed url data from sync server to history backend.
- absl::optional<syncer::ModelError> WriteToHistoryBackend(
- const std::vector<URLRow>* new_urls,
- const std::vector<URLRow>* updated_urls,
- const std::vector<GURL>* deleted_urls,
- const std::vector<URLWithVisits>* new_visits,
- const std::vector<VisitRow>* deleted_visits);
-
- // Given a TypedUrlSpecifics object, removes all visits that are older than
- // the current expiration time. Note that this can result in having no visits
- // at all.
- sync_pb::TypedUrlSpecifics FilterExpiredVisits(
- const sync_pb::TypedUrlSpecifics& specifics);
-
- // Helper function that determines if we should ignore a URL for the purposes
- // of sync, because it contains invalid data.
- bool ShouldIgnoreUrl(const GURL& url);
-
- // Helper function that determines if we should ignore a URL for the purposes
- // of sync, based on the visits the URL had.
- bool ShouldIgnoreVisits(const std::vector<VisitRow>& visits);
-
- // Returns true if the caller should sync as a result of the passed visit
- // notification. We use this to throttle the number of sync changes we send
- // to the server so we don't hit the server for every
- // single typed URL visit.
- bool ShouldSyncVisit(int typed_count, ui::PageTransition transition);
-
- // Fetches visits from the history DB corresponding to the passed URL. This
- // function compensates for the fact that the history DB has rather poor data
- // integrity (duplicate visits, visit timestamps that don't match the
- // last_visit timestamp, huge data sets that exhaust memory when fetched,
- // expired visits that are not deleted by `ExpireHistoryBackend`, etc) by
- // modifying the passed `url` object and `visits` vector. The order of
- // `visits` will be from the oldest to the newest order.
- // Returns false if we could not fetch the visits for the passed URL, DB
- // error. If there are no visits for the passed url, or all the visits are
- // expired, it returns true but `visits` is empty.
- bool FixupURLAndGetVisits(URLRow* url, std::vector<VisitRow>* visits);
-
- // Create an EntityData by URL `row` and its visits `visits`.
- std::unique_ptr<syncer::EntityData> CreateEntityData(
- const URLRow& row,
- const std::vector<VisitRow>& visits);
-
- // Get all the typed urls and visits from the history db, after filtering
- // them, put them into `url_to_visit` and `url_to_urlrow`.
- // Return false if cannot get urls from HistoryBackend.
- bool GetValidURLsAndVisits(
- std::map<GURL, std::vector<VisitRow>>* url_to_visit,
- std::map<GURL, URLRow>* url_to_urlrow);
-
- // Get URLID from HistoryBackend, and return URLID as storage key.
- std::string GetStorageKeyInternal(const std::string& url);
-
- // Send local typed url to processor().
- void SendTypedURLToProcessor(
- const URLRow& row,
- const std::vector<VisitRow>& visits,
- syncer::MetadataChangeList* metadata_change_list);
-
- // A non-owning pointer to the backend, which we're syncing local changes from
- // and sync changes to.
- const raw_ptr<HistoryBackend, DanglingUntriaged> history_backend_;
-
- // Whether we're currently processing changes from the syncer. While this is
- // true, we ignore any local url changes, since we triggered them.
- bool processing_syncer_changes_ = false;
-
- // A non-owning pointer to the database, which is for storing typed urls sync
- // metadata and state.
- raw_ptr<TypedURLSyncMetadataDatabase, AcrossTasksDanglingUntriaged>
- sync_metadata_database_;
-
- // Since HistoryBackend use SequencedTaskRunner, so should use SequenceChecker
- // here.
- SEQUENCE_CHECKER(sequence_checker_);
-
- // Tracks observed history backend, for receiving updates from history
- // backend.
- base::ScopedObservation<HistoryBackend, HistoryBackendObserver>
- history_backend_observation_{this};
-};
-
-} // namespace history
-
-#endif // COMPONENTS_HISTORY_CORE_BROWSER_SYNC_TYPED_URL_SYNC_BRIDGE_H_
diff --git a/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc b/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc
deleted file mode 100644
index b396a2d..0000000
--- a/components/history/core/browser/sync/typed_url_sync_bridge_unittest.cc
+++ /dev/null
@@ -1,1818 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/history/core/browser/sync/typed_url_sync_bridge.h"
-
-#include <algorithm>
-#include <memory>
-
-#include "base/big_endian.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/functional/bind.h"
-#include "base/memory/raw_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/single_thread_task_runner.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "components/history/core/browser/history_backend.h"
-#include "components/history/core/browser/history_backend_client.h"
-#include "components/history/core/browser/history_database_params.h"
-#include "components/history/core/browser/in_memory_history_backend.h"
-#include "components/history/core/test/test_history_database.h"
-#include "components/sync/model/data_batch.h"
-#include "components/sync/protocol/entity_metadata.pb.h"
-#include "components/sync/protocol/entity_specifics.pb.h"
-#include "components/sync/protocol/typed_url_specifics.pb.h"
-#include "components/sync/test/mock_model_type_change_processor.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace history {
-
-namespace {
-
-using base::Time;
-using sync_pb::TypedUrlSpecifics;
-using syncer::DataBatch;
-using syncer::EntityChange;
-using syncer::EntityChangeList;
-using syncer::EntityData;
-using syncer::MetadataBatch;
-using syncer::MetadataChangeList;
-using syncer::MockModelTypeChangeProcessor;
-using testing::_;
-using testing::AllOf;
-using testing::DoAll;
-using testing::IsEmpty;
-using testing::Mock;
-using testing::NiceMock;
-using testing::Pointee;
-using testing::Return;
-using testing::UnorderedElementsAre;
-
-// Constants used to limit size of visits processed. See
-// equivalent constants in typed_url_sync_bridge.cc for descriptions.
-const int kMaxTypedUrlVisits = 100;
-const int kVisitThrottleThreshold = 10;
-const int kVisitThrottleMultiple = 10;
-
-// Visits with this timestamp are treated as expired.
-const int kExpiredVisit = -1;
-
-// Helper constants for tests.
-const char kTitle[] = "pie";
-const char kTitle2[] = "cookie";
-const char kURL[] = "http://pie.com/";
-const char kURL2[] = "http://cookie.com/";
-
-// Action SaveArgPointeeMove<k>(pointer) saves the value pointed to by the k-th
-// (0-based) argument of the mock function by moving it to *pointer.
-ACTION_TEMPLATE(SaveArgPointeeMove,
- HAS_1_TEMPLATE_PARAMS(int, k),
- AND_1_VALUE_PARAMS(pointer)) {
- *pointer = std::move(*testing::get<k>(args));
-}
-
-// Matches that TypedUrlSpecifics has expected URL.
-MATCHER_P(HasURLInSpecifics, url, "") {
- return arg.specifics.typed_url().url() == url;
-}
-
-// Matches that TypedUrlSpecifics has expected title.
-MATCHER_P(HasTitleInSpecifics, title, "") {
- return arg.specifics.typed_url().title() == title;
-}
-
-MATCHER(HasTypedUrlInSpecifics, "") {
- return arg.specifics.has_typed_url();
-}
-
-MATCHER(IsValidStorageKey, "") {
- return TypedURLSyncMetadataDatabase::StorageKeyToURLID(arg) > 0;
-}
-
-Time SinceEpoch(int64_t microseconds_since_epoch) {
- return Time::FromDeltaSinceWindowsEpoch(
- base::Microseconds(microseconds_since_epoch));
-}
-
-bool URLsEqual(const URLRow& row, const sync_pb::TypedUrlSpecifics& specifics) {
- return ((row.url().spec().compare(specifics.url()) == 0) &&
- (base::UTF16ToUTF8(row.title()).compare(specifics.title()) == 0) &&
- (row.hidden() == specifics.hidden()));
-}
-
-bool URLsEqual(const URLRow& lhs, const URLRow& rhs) {
- // Only compare synced fields (ignore typed_count and visit_count as those
- // are maintained by the history subsystem).
- return (lhs.url().spec().compare(rhs.url().spec()) == 0) &&
- (lhs.title().compare(rhs.title()) == 0) &&
- (lhs.hidden() == rhs.hidden());
-}
-
-void AddNewestVisit(ui::PageTransition transition,
- int64_t visit_time,
- URLRow* url,
- std::vector<VisitRow>* visits) {
- Time time = SinceEpoch(visit_time);
- visits->insert(visits->begin(),
- VisitRow(url->id(), time, 0, transition, 0,
- HistoryBackend::IsTypedIncrement(transition), 0));
-
- if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
- url->set_typed_count(url->typed_count() + 1);
- }
-
- url->set_last_visit(time);
- url->set_visit_count(visits->size());
-}
-
-void AddOldestVisit(ui::PageTransition transition,
- int visit_time,
- URLRow* url,
- std::vector<VisitRow>* visits) {
- Time time = SinceEpoch(visit_time);
- visits->push_back(VisitRow(url->id(), time, 0, transition, 0,
- HistoryBackend::IsTypedIncrement(transition), 0));
-
- if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
- url->set_typed_count(url->typed_count() + 1);
- }
-
- url->set_visit_count(visits->size());
-}
-
-// Create a new row object and the typed visit corresponding with the time at
-// `last_visit` in the `visits` vector.
-URLRow MakeTypedUrlRow(const std::string& url,
- const std::string& title,
- int typed_count,
- int64_t last_visit,
- bool hidden,
- std::vector<VisitRow>* visits) {
- // Give each URL a unique ID, to mimic the behavior of the real database.
- GURL gurl(url);
- URLRow history_url(gurl);
- history_url.set_title(base::UTF8ToUTF16(title));
- history_url.set_typed_count(typed_count);
- history_url.set_hidden(hidden);
-
- Time last_visit_time = SinceEpoch(last_visit);
- history_url.set_last_visit(last_visit_time);
-
- ui::PageTransition transition = ui::PAGE_TRANSITION_RELOAD;
- bool incremented_omnibox_typed_score = false;
- if (typed_count > 0) {
- transition = ui::PAGE_TRANSITION_TYPED;
- incremented_omnibox_typed_score = true;
- }
- visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, transition,
- 0, incremented_omnibox_typed_score, 0));
-
- history_url.set_visit_count(visits->size());
- return history_url;
-}
-
-// Create a new row object and a typed and a reload visit with appropriate
-// times.
-URLRow MakeTypedUrlRowWithTwoVisits(const std::string& url,
- const std::string& title,
- int64_t typed_visit,
- int64_t reload_visit,
- std::vector<VisitRow>* visits) {
- // Give each URL a unique ID, to mimic the behavior of the real database.
- GURL gurl(url);
- URLRow history_url(gurl);
- history_url.set_title(base::UTF8ToUTF16(title));
- history_url.set_typed_count(1);
- history_url.set_visit_count(2);
- history_url.set_hidden(false);
-
- Time typed_visit_time = SinceEpoch(typed_visit);
- Time reload_visit_time = SinceEpoch(reload_visit);
-
- history_url.set_last_visit(std::max(typed_visit_time, reload_visit_time));
-
- visits->push_back(VisitRow(history_url.id(), typed_visit_time, 0,
- ui::PAGE_TRANSITION_TYPED, 0, true, 0));
- // Add a non-typed visit for time `last_visit`.
- visits->push_back(VisitRow(history_url.id(), reload_visit_time, 0,
- ui::PAGE_TRANSITION_RELOAD, 0, false, 0));
- return history_url;
-}
-
-void VerifyEqual(const TypedUrlSpecifics& s1, const TypedUrlSpecifics& s2) {
- // Instead of just comparing serialized strings, manually check fields to show
- // differences on failure.
- EXPECT_EQ(s1.url(), s2.url());
- EXPECT_EQ(s1.title(), s2.title());
- EXPECT_EQ(s1.hidden(), s2.hidden());
- EXPECT_EQ(s1.visits_size(), s2.visits_size());
- EXPECT_EQ(s1.visit_transitions_size(), s2.visit_transitions_size());
- EXPECT_EQ(s1.visits_size(), s1.visit_transitions_size());
- int size = s1.visits_size();
- for (int i = 0; i < size; ++i) {
- EXPECT_EQ(s1.visits(i), s2.visits(i)) << "visits differ at index " << i;
- EXPECT_EQ(s1.visit_transitions(i), s2.visit_transitions(i))
- << "visit_transitions differ at index " << i;
- }
-}
-
-void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> expected,
- std::unique_ptr<DataBatch> batch) {
- while (batch->HasNext()) {
- auto [key, data] = batch->Next();
- auto iter = expected.find(key);
- ASSERT_NE(iter, expected.end());
- VerifyEqual(iter->second, data->specifics.typed_url());
- // Removing allows us to verify we don't see the same item multiple times,
- // and that we saw everything we expected.
- expected.erase(iter);
- }
- EXPECT_TRUE(expected.empty());
-}
-
-std::string IntToStorageKey(int id) {
- std::string storage_key(sizeof(URLID), 0);
- base::WriteBigEndian<URLID>(&storage_key[0], id);
- return storage_key;
-}
-
-void StoreMetadata(const std::string& storage_key,
- std::unique_ptr<EntityData> entity_data,
- MetadataChangeList* metadata_change_list) {
- sync_pb::EntityMetadata metadata;
- metadata.set_sequence_number(1);
- metadata_change_list->UpdateMetadata(storage_key, metadata);
-}
-
-class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
- public:
- TestHistoryBackendDelegate() = default;
-
- TestHistoryBackendDelegate(const TestHistoryBackendDelegate&) = delete;
- TestHistoryBackendDelegate& operator=(const TestHistoryBackendDelegate&) =
- delete;
-
- bool CanAddURL(const GURL& url) const override { return true; }
- void NotifyProfileError(sql::InitStatus init_status,
- const std::string& diagnostics) override {}
- void SetInMemoryBackend(
- std::unique_ptr<InMemoryHistoryBackend> backend) override {}
- void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
- const GURL& icon_url) override {}
- void NotifyURLVisited(const URLRow& url_row,
- const VisitRow& visit_row,
- absl::optional<int64_t> local_navigation_id) override {}
- void NotifyURLsModified(const std::vector<URLRow>& changed_urls) override {}
- void NotifyURLsDeleted(DeletionInfo deletion_info) override {}
- void NotifyKeywordSearchTermUpdated(const URLRow& row,
- KeywordID keyword_id,
- const std::u16string& term) override {}
- void NotifyKeywordSearchTermDeleted(URLID url_id) override {}
- void DBLoaded() override {}
-};
-
-class TestHistoryBackendForSync : public HistoryBackend {
- public:
- explicit TestHistoryBackendForSync(
- std::unique_ptr<HistoryBackendClient> backend_client)
- : HistoryBackend(std::make_unique<TestHistoryBackendDelegate>(),
- std::move(backend_client),
- base::SingleThreadTaskRunner::GetCurrentDefault()) {}
-
- bool IsExpiredVisitTime(const Time& time) const override {
- return time.ToDeltaSinceWindowsEpoch().InMicroseconds() == kExpiredVisit;
- }
-
- URLID GetIdByUrl(const GURL& gurl) {
- return db()->GetRowForURL(gurl, nullptr);
- }
-
- void SetVisitsForUrl(URLRow* new_url, const std::vector<VisitRow> visits) {
- if (!GetURL(new_url->url(), nullptr)) {
- URLRow to_insert = *new_url;
- // AddVisits() increments counts so we should decrement it now to get a
- // consistent result in the end.
- for (const auto& visit : visits) {
- to_insert.set_visit_count(to_insert.visit_count() - 1);
- if (ui::PageTransitionCoreTypeIs(visit.transition,
- ui::PAGE_TRANSITION_TYPED)) {
- to_insert.set_typed_count(to_insert.typed_count() - 1);
- }
- }
- AddPagesWithDetails({to_insert}, SOURCE_SYNCED);
- }
-
- std::vector<VisitInfo> added_visits;
- for (const auto& visit : visits) {
- added_visits.emplace_back(visit.visit_time, visit.transition);
- }
- AddVisits(new_url->url(), added_visits, SOURCE_SYNCED);
- new_url->set_id(GetIdByUrl(new_url->url()));
- }
-
- private:
- ~TestHistoryBackendForSync() override = default;
-};
-
-class MockHistoryBackendClient : public HistoryBackendClient {
- public:
- MOCK_METHOD(bool, IsPinnedURL, (const GURL& url), (override));
- MOCK_METHOD(std::vector<URLAndTitle>, GetPinnedURLs, (), (override));
- MOCK_METHOD(bool, IsWebSafe, (const GURL& url), (override));
-};
-
-} // namespace
-
-class TypedURLSyncBridgeTest : public testing::Test {
- public:
- void SetUp() override {
- auto history_backend_client =
- std::make_unique<NiceMock<MockHistoryBackendClient>>();
- history_backend_client_ = history_backend_client.get();
- ON_CALL(*history_backend_client_, IsPinnedURL).WillByDefault(Return(false));
-
- fake_history_backend_ =
- new TestHistoryBackendForSync(std::move(history_backend_client));
- ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
- fake_history_backend_->Init(
- false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath()));
- auto bridge = std::make_unique<TypedURLSyncBridge>(
- fake_history_backend_.get(),
- fake_history_backend_->db()->GetTypedURLMetadataDB(),
- mock_processor_.CreateForwardingProcessor());
- typed_url_sync_bridge_ = bridge.get();
- typed_url_sync_bridge_->Init();
- typed_url_sync_bridge_->history_backend_observation_.Reset();
- fake_history_backend_->SetTypedURLSyncBridgeForTest(std::move(bridge));
- }
-
- void TearDown() override { fake_history_backend_->Closing(); }
-
- // Starts sync for `typed_url_sync_bridge_` with `initial_data` as the
- // initial sync data.
- void StartSyncing(const std::vector<TypedUrlSpecifics>& specifics) {
- ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(true));
- // Set change processor.
- const auto error =
- bridge()->MergeFullSyncData(bridge()->CreateMetadataChangeList(),
- CreateEntityChangeList(specifics));
-
- EXPECT_FALSE(error);
- }
-
- void BuildAndPushLocalChanges(
- size_t num_typed_urls,
- size_t num_reload_urls,
- const std::vector<std::string>& urls,
- std::vector<URLRow>* rows,
- std::vector<std::vector<VisitRow>>* visit_vectors) {
- const size_t total_urls = num_typed_urls + num_reload_urls;
- DCHECK(urls.size() >= total_urls);
- DCHECK(bridge());
-
- if (total_urls) {
- // Create new URL rows, populate the mock backend with its visits, and
- // send to the sync service.
- std::vector<URLRow> changed_urls;
-
- for (size_t i = 0; i < total_urls; ++i) {
- const int typed = i < num_typed_urls ? 1 : 0;
- std::vector<VisitRow> visits;
- visit_vectors->push_back(visits);
- rows->push_back(MakeTypedUrlRow(urls[i], kTitle, typed, i + 3, false,
- &visit_vectors->back()));
- fake_history_backend_->SetVisitsForUrl(&rows->back(),
- visit_vectors->back());
- changed_urls.push_back(rows->back());
- }
-
- bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls,
- /*is_from_expiration=*/false);
- }
- }
-
- std::vector<VisitRow> ApplyUrlAndVisitsChange(
- const std::string& url,
- const std::string& title,
- int typed_count,
- int64_t last_visit,
- bool hidden,
- EntityChange::ChangeType change_type) {
- std::vector<VisitRow> visits;
- URLRow row =
- MakeTypedUrlRow(url, title, typed_count, last_visit, hidden, &visits);
- sync_pb::TypedUrlSpecifics typed_url_specifics;
- WriteToTypedUrlSpecifics(row, visits, &typed_url_specifics);
- std::string storage_key = GetStorageKey(typed_url_specifics.url());
-
- EntityChangeList entity_changes;
- switch (change_type) {
- case EntityChange::ACTION_ADD:
- entity_changes.push_back(EntityChange::CreateAdd(
- std::string(), SpecificsToEntity(typed_url_specifics)));
- break;
- case EntityChange::ACTION_UPDATE:
- entity_changes.push_back(EntityChange::CreateUpdate(
- storage_key, SpecificsToEntity(typed_url_specifics)));
- break;
- case EntityChange::ACTION_DELETE:
- entity_changes.push_back(EntityChange::CreateDelete(storage_key));
- break;
- }
-
- std::unique_ptr<MetadataChangeList> metadata_changes =
- bridge()->CreateMetadataChangeList();
-
- bridge()->ApplyIncrementalSyncChanges(std::move(metadata_changes),
- std::move(entity_changes));
- return visits;
- }
-
- void AddObserver() {
- bridge()->history_backend_observation_.Observe(fake_history_backend_.get());
- }
-
- void RemoveObserver() { bridge()->history_backend_observation_.Reset(); }
-
- // Fills `specifics` with the sync data for `url` and `visits`.
- static bool WriteToTypedUrlSpecifics(const URLRow& url,
- const std::vector<VisitRow>& visits,
- TypedUrlSpecifics* specifics) {
- return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics);
- }
-
- std::string GetStorageKey(const std::string& url) {
- return bridge()->GetStorageKeyInternal(url);
- }
-
- std::set<std::string> GetAllSyncMetadataKeys() {
- MetadataBatch metadata_batch;
- metadata_store()->GetAllSyncMetadata(&metadata_batch);
- std::set<std::string> keys;
- for (const auto& [storage_key, metadata] :
- metadata_batch.GetAllMetadata()) {
- keys.insert(storage_key);
- }
- return keys;
- }
-
- EntityData SpecificsToEntity(const TypedUrlSpecifics& specifics) {
- EntityData data;
- *data.specifics.mutable_typed_url() = specifics;
- return data;
- }
-
- EntityChangeList CreateEntityChangeList(
- const std::vector<TypedUrlSpecifics>& specifics_vector) {
- EntityChangeList entity_change_list;
- for (const auto& specifics : specifics_vector) {
- entity_change_list.push_back(EntityChange::CreateAdd(
- GetStorageKey(specifics.url()), SpecificsToEntity(specifics)));
- }
- return entity_change_list;
- }
-
- std::map<std::string, TypedUrlSpecifics> ExpectedMap(
- const std::vector<TypedUrlSpecifics>& specifics_vector) {
- std::map<std::string, TypedUrlSpecifics> map;
- for (const auto& specifics : specifics_vector) {
- map[GetStorageKey(specifics.url())] = specifics;
- }
- return map;
- }
-
- void VerifyAllLocalHistoryData(
- const std::vector<TypedUrlSpecifics>& expected) {
- bridge()->GetAllDataForDebugging(
- base::BindOnce(&VerifyDataBatch, ExpectedMap(expected)));
- }
-
- void VerifyGetData(TypedURLSyncBridge::StorageKeyList storage_keys,
- const std::vector<TypedUrlSpecifics>& expected) {
- bridge()->GetData(storage_keys,
- base::BindOnce(&VerifyDataBatch, ExpectedMap(expected)));
- }
-
- static void DiffVisits(const std::vector<VisitRow>& history_visits,
- const sync_pb::TypedUrlSpecifics& sync_specifics,
- std::vector<VisitInfo>* new_visits,
- std::vector<VisitRow>* removed_visits) {
- TypedURLSyncBridge::DiffVisits(history_visits, sync_specifics, new_visits,
- removed_visits);
- }
-
- static VisitRow CreateVisit(ui::PageTransition type, int64_t timestamp) {
- return VisitRow(0, SinceEpoch(timestamp), 0, type, 0,
- HistoryBackend::IsTypedIncrement(type), 0);
- }
-
- static TypedURLSyncBridge::MergeResult MergeUrls(
- const sync_pb::TypedUrlSpecifics& typed_url,
- const URLRow& url,
- std::vector<VisitRow>* visits,
- URLRow* new_url,
- std::vector<VisitInfo>* new_visits) {
- return TypedURLSyncBridge::MergeUrls(typed_url, url, visits, new_url,
- new_visits);
- }
-
- static sync_pb::TypedUrlSpecifics MakeTypedUrlSpecifics(const char* url,
- const char* title,
- int64_t last_visit,
- bool hidden) {
- sync_pb::TypedUrlSpecifics typed_url;
- typed_url.set_url(url);
- typed_url.set_title(title);
- typed_url.set_hidden(hidden);
- typed_url.add_visits(last_visit);
- typed_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
- return typed_url;
- }
-
- static const TypedURLSyncBridge::MergeResult DIFF_NONE =
- TypedURLSyncBridge::DIFF_NONE;
- static const TypedURLSyncBridge::MergeResult DIFF_UPDATE_NODE =
- TypedURLSyncBridge::DIFF_UPDATE_NODE;
- static const TypedURLSyncBridge::MergeResult DIFF_LOCAL_ROW_CHANGED =
- TypedURLSyncBridge::DIFF_LOCAL_ROW_CHANGED;
- static const TypedURLSyncBridge::MergeResult DIFF_LOCAL_VISITS_ADDED =
- TypedURLSyncBridge::DIFF_LOCAL_VISITS_ADDED;
-
- TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; }
-
- TypedURLSyncMetadataDatabase* metadata_store() {
- return bridge()->sync_metadata_database_;
- }
-
- protected:
- base::test::SingleThreadTaskEnvironment task_environment_;
- base::ScopedTempDir test_dir_;
- raw_ptr<MockHistoryBackendClient, DanglingUntriaged> history_backend_client_;
- scoped_refptr<TestHistoryBackendForSync> fake_history_backend_;
- raw_ptr<TypedURLSyncBridge> typed_url_sync_bridge_ = nullptr;
- NiceMock<MockModelTypeChangeProcessor> mock_processor_;
-};
-
-// Add two typed urls locally and verify bridge can get them from GetAllData.
-TEST_F(TypedURLSyncBridgeTest, GetAllData) {
- // Add two urls to backend.
- std::vector<VisitRow> visits1, visits2, visits3;
- URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2);
- URLRow expired_row = MakeTypedUrlRow("http://expired.com/", kTitle, 1,
- kExpiredVisit, false, &visits3);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
- fake_history_backend_->SetVisitsForUrl(&expired_row, visits3);
-
- // Create the same data in sync.
- TypedUrlSpecifics typed_url1, typed_url2;
- WriteToTypedUrlSpecifics(row1, visits1, &typed_url1);
- WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
-
- // Check that the local cache is still correct, expired row is filtered out.
- VerifyAllLocalHistoryData({typed_url1, typed_url2});
-}
-
-// Add two typed urls locally and verify bridge can get them from GetData.
-TEST_F(TypedURLSyncBridgeTest, GetData) {
- // Add two urls to backend.
- std::vector<VisitRow> visits1, visits2;
- URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
-
- // Create the same data in sync.
- TypedUrlSpecifics typed_url1, typed_url2;
- WriteToTypedUrlSpecifics(row1, visits1, &typed_url1);
- WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
-
- // Check that the local cache is still correct.
- VerifyGetData({IntToStorageKey(1)}, {typed_url1});
- VerifyGetData({IntToStorageKey(2)}, {typed_url2});
-}
-
-// Add a typed url locally and one to sync with the same data. Starting sync
-// should result in no changes.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlNoChange) {
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&row, visits);
-
- // Create the same data in sync.
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(row, visits, typed_url);
-
- EXPECT_CALL(mock_processor_, Put).Times(0);
-
- // Even Sync already know the url, bridge still need to tell sync about
- // storage keys.
- const std::string expected_storage_key = IntToStorageKey(1);
- EXPECT_CALL(mock_processor_,
- UpdateStorageKey(
- AllOf(HasURLInSpecifics(kURL), HasTitleInSpecifics(kTitle)),
- expected_storage_key, _));
- StartSyncing({*typed_url});
-
- // Check that the local cache was is still correct.
- VerifyAllLocalHistoryData({*typed_url});
-}
-
-// Add a corrupted typed url locally, has typed url count 1, but no real typed
-// url visit. Starting sync should not pick up this url.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlNoTypedUrl) {
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow row = MakeTypedUrlRow(kURL, kTitle, 0, 3, false, &visits);
-
- // Mark typed_count to 1 even when there is no typed url visit.
- row.set_typed_count(1);
- fake_history_backend_->SetVisitsForUrl(&row, visits);
-
- EXPECT_CALL(mock_processor_, Put).Times(0);
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // There's also no metadata written as there's no call to Put() (where the
- // test could mock storing metadata).
- EXPECT_THAT(GetAllSyncMetadataKeys(), IsEmpty());
-}
-
-// Starting sync with no sync data should just push the local url to sync.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptySync) {
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&row, visits);
-
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(DoAll(SaveArgPointeeMove<1>(&entity_data), StoreMetadata));
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Check that the local cache is still correct.
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(row, visits, typed_url);
- VerifyAllLocalHistoryData({*typed_url});
-
- EXPECT_THAT(GetAllSyncMetadataKeys(),
- UnorderedElementsAre(GetStorageKey(kURL)));
-
- // Check that the server was updated correctly.
- const TypedUrlSpecifics& committed_specifics =
- entity_data.specifics.typed_url();
-
- ASSERT_EQ(1, committed_specifics.visits_size());
- EXPECT_EQ(3, committed_specifics.visits(0));
- ASSERT_EQ(1, committed_specifics.visit_transitions_size());
- EXPECT_EQ(static_cast<const int>(visits[0].transition),
- committed_specifics.visit_transitions(0));
-}
-
-// Starting sync with no local data should just push the synced url into the
-// backend.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptyLocal) {
- // Create the sync data.
- std::vector<VisitRow> visits;
- URLRow row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(row, visits, typed_url);
-
- // Verify processor receive correct update storage key.
- EXPECT_CALL(mock_processor_, Put).Times(0);
- EXPECT_CALL(mock_processor_,
- UpdateStorageKey(
- AllOf(HasURLInSpecifics(kURL), HasTitleInSpecifics(kTitle)),
- IntToStorageKey(1), _));
- StartSyncing({*typed_url});
-
- // Check that the backend was updated correctly.
- std::vector<VisitRow> all_visits;
- Time server_time = SinceEpoch(3);
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- ASSERT_EQ(1U, all_visits.size());
- EXPECT_EQ(server_time, all_visits[0].visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[0].transition, visits[0].transition));
-}
-
-// Add a url to the local and sync data before sync begins, with the sync data
-// having more recent visits. Check that starting sync updates the backend
-// with the sync visit, while the older local visit is not pushed to sync.
-// The title should be updated to the sync version due to the more recent
-// timestamp.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlOldLocal) {
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow local_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&local_row, visits);
-
- // Create sync data for the same url with a more recent visit.
- std::vector<VisitRow> server_visits;
- URLRow server_row =
- MakeTypedUrlRow(kURL, kTitle2, 1, 6, false, &server_visits);
- server_row.set_id(fake_history_backend_->GetIdByUrl(GURL(kURL)));
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
-
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(DoAll(SaveArgPointeeMove<1>(&entity_data), StoreMetadata));
- StartSyncing({*typed_url});
-
- // Check that the backend was updated correctly.
- std::vector<VisitRow> all_visits;
- Time server_time = SinceEpoch(6);
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- ASSERT_EQ(2U, all_visits.size());
- EXPECT_EQ(server_time, all_visits.back().visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits.back().transition, server_visits[0].transition));
- URLRow url_row;
- EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- EXPECT_EQ(kTitle2, base::UTF16ToUTF8(url_row.title()));
-
- EXPECT_THAT(GetAllSyncMetadataKeys(),
- UnorderedElementsAre(GetStorageKey(kURL)));
-
- // Check that the sync was updated correctly.
- // The local history visit should not be added to sync because it is older
- // than sync's oldest visit.
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
- ASSERT_EQ(1, url_specifics.visits_size());
- EXPECT_EQ(6, url_specifics.visits(0));
- ASSERT_EQ(1, url_specifics.visit_transitions_size());
- EXPECT_EQ(static_cast<const int>(visits[0].transition),
- url_specifics.visit_transitions(0));
-}
-
-// Add a url to the local and sync data before sync begins, with the local data
-// having more recent visits. Check that starting sync updates the sync
-// with the local visits, while the older sync visit is not pushed to the
-// backend. Sync's title should be updated to the local version due to the more
-// recent timestamp.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlOldSync) {
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow local_row = MakeTypedUrlRow(kURL, kTitle2, 1, 3, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&local_row, visits);
-
- // Create sync data for the same url with an older visit.
- std::vector<VisitRow> server_visits;
- URLRow server_row =
- MakeTypedUrlRow(kURL, kTitle, 1, 2, false, &server_visits);
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
-
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- StartSyncing({*typed_url});
-
- // Check that the backend was not updated.
- std::vector<VisitRow> all_visits;
- Time local_visit_time = SinceEpoch(3);
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- ASSERT_EQ(1U, all_visits.size());
- EXPECT_EQ(local_visit_time, all_visits[0].visit_time);
-
- // Check that the server was updated correctly.
- // The local history visit should not be added to sync because it is older
- // than sync's oldest visit.
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
- ASSERT_EQ(1, url_specifics.visits_size());
- EXPECT_EQ(3, url_specifics.visits(0));
- EXPECT_EQ(kTitle2, url_specifics.title());
- ASSERT_EQ(1, url_specifics.visit_transitions_size());
- EXPECT_EQ(static_cast<const int>(visits[0].transition),
- url_specifics.visit_transitions(0));
-}
-
-// Check that there is no crash during start sync, if history backend and sync
-// have same url, but sync has username/password in it.
-// Also check sync will not accept url with username and password.
-TEST_F(TypedURLSyncBridgeTest, MergeUrlsWithUsernameAndPassword) {
- const char kURLWithUsernameAndPassword[] =
- "http://username:password@pie.com/";
-
- // Add a url to backend.
- std::vector<VisitRow> visits;
- URLRow local_row = MakeTypedUrlRow(kURL, kTitle2, 1, 3, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&local_row, visits);
-
- // Create sync data for the same url but contain username and password.
- std::vector<VisitRow> server_visits;
- URLRow server_row = MakeTypedUrlRow(kURLWithUsernameAndPassword, kTitle, 1, 3,
- false, &server_visits);
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
-
- // Check username/password url is not synced.
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _));
-
- // Make sure there is no crash when merge two urls.
- StartSyncing({*typed_url});
-
- // Notify typed url sync service of the update.
- bridge()->OnURLVisited(fake_history_backend_.get(), server_row,
- server_visits.front());
-}
-
-// Starting sync with both local and sync have same typed URL, but different
-// visit. After merge, both local and sync should have two same visits.
-TEST_F(TypedURLSyncBridgeTest, SimpleMerge) {
- // Add a url to backend.
- std::vector<VisitRow> visits1;
- URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
-
- // Create the sync data.
- std::vector<VisitRow> visits2;
- URLRow row2 = MakeTypedUrlRow(kURL, kTitle, 1, 4, false, &visits2);
- sync_pb::EntitySpecifics entity_specifics;
- sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
- WriteToTypedUrlSpecifics(row2, visits2, typed_url);
-
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _));
- StartSyncing({*typed_url});
-
- // Check that the backend was updated correctly.
- std::vector<VisitRow> all_visits;
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- ASSERT_EQ(2U, all_visits.size());
- EXPECT_EQ(SinceEpoch(3), all_visits[0].visit_time);
- EXPECT_EQ(SinceEpoch(4), all_visits[1].visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[0].transition, visits1[0].transition));
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[1].transition, visits2[0].transition));
-}
-
-// Create a local typed URL with one TYPED visit after sync has started. Check
-// that sync is sent an ADD change for the new URL.
-TEST_F(TypedURLSyncBridgeTest, AddLocalTypedUrl) {
- // Create a local typed URL (simulate a typed visit) that is not already
- // in sync. Check that sync is sent an ADD change for the existing URL.
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(IsValidStorageKey(), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- StartSyncing(std::vector<TypedUrlSpecifics>());
- BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors);
-
- URLRow url_row = url_rows.front();
- std::vector<VisitRow> visits = visit_vectors.front();
-
- // Get typed url specifics.
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
-
- EXPECT_TRUE(URLsEqual(url_row, url_specifics));
- ASSERT_EQ(1, url_specifics.visits_size());
- ASSERT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
- EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(0));
- EXPECT_EQ(static_cast<const int>(visits[0].transition),
- url_specifics.visit_transitions(0));
-}
-
-// Update a local typed URL that is already synced. Check that sync is sent an
-// UPDATE for the existing url, but RELOAD visits aren't synced.
-TEST_F(TypedURLSyncBridgeTest, UpdateLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Update the URL row, adding another typed visit to the visit vector.
- std::vector<URLRow> changed_urls;
- std::vector<VisitRow> visits;
- URLRow url_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, 7, &url_row, &visits);
- AddNewestVisit(ui::PAGE_TRANSITION_RELOAD, 8, &url_row, &visits);
- AddNewestVisit(ui::PAGE_TRANSITION_LINK, 9, &url_row, &visits);
- fake_history_backend_->SetVisitsForUrl(&url_row, visits);
- changed_urls.push_back(url_row);
-
- // Notify typed url sync service of the update.
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls,
- /*is_from_expiration=*/false);
-
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
- EXPECT_TRUE(URLsEqual(url_row, url_specifics));
- ASSERT_EQ(3, url_specifics.visits_size());
-
- // Check that each visit has been translated/communicated correctly.
- // Note that the specifics record visits in chronological order, and the
- // visits from the db are in reverse chronological order.
- EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(2));
- EXPECT_EQ(static_cast<const int>(visits[0].transition),
- url_specifics.visit_transitions(2));
- EXPECT_EQ(visits[2].visit_time.ToInternalValue(), url_specifics.visits(1));
- EXPECT_EQ(static_cast<const int>(visits[2].transition),
- url_specifics.visit_transitions(1));
- EXPECT_EQ(visits[3].visit_time.ToInternalValue(), url_specifics.visits(0));
- EXPECT_EQ(static_cast<const int>(visits[3].transition),
- url_specifics.visit_transitions(0));
-}
-
-// Append a RELOAD visit to a typed url that is already synced. Check that sync
-// does not receive any updates.
-TEST_F(TypedURLSyncBridgeTest, ReloadVisitLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
-
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _));
- BuildAndPushLocalChanges(1, 0, {kURL}, &url_rows, &visit_vectors);
-
- // Check that Put method has been already called.
- Mock::VerifyAndClearExpectations(&mock_processor_);
-
- // Update the URL row, adding another typed visit to the visit vector.
- URLRow url_row = url_rows.front();
- std::vector<URLRow> changed_urls;
- std::vector<VisitRow> new_visits;
- AddNewestVisit(ui::PAGE_TRANSITION_RELOAD, 7, &url_row, &new_visits);
- fake_history_backend_->SetVisitsForUrl(&url_row, new_visits);
- changed_urls.push_back(url_row);
-
- // Notify typed url sync service of the update.
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row,
- new_visits.front());
- // No change pass to processor
-}
-
-// Appends a LINK visit to an existing typed url. Check that sync does not
-// receive any changes.
-TEST_F(TypedURLSyncBridgeTest, LinkVisitLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- StartSyncing(std::vector<TypedUrlSpecifics>());
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _));
- BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors);
-
- // Check that Put method has been already called.
- Mock::VerifyAndClearExpectations(&mock_processor_);
-
- // Update the URL row, adding a non-typed visit to the visit vector.
- URLRow url_row = url_rows.front();
- std::vector<VisitRow> new_visits;
- AddNewestVisit(ui::PAGE_TRANSITION_LINK, 6, &url_row, &new_visits);
- fake_history_backend_->SetVisitsForUrl(&url_row, new_visits);
-
- // Notify typed url sync service of non-typed visit, expect no change.
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row,
- new_visits.front());
- // No change pass to processor
-}
-
-// Appends a series of LINK visits followed by a TYPED one to an existing typed
-// url. Check that sync receives an UPDATE with the newest visit data.
-TEST_F(TypedURLSyncBridgeTest, TypedVisitLocalTypedUrl) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Update the URL row, adding another typed visit to the visit vector.
- std::vector<VisitRow> visits;
- URLRow url_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
- AddOldestVisit(ui::PAGE_TRANSITION_LINK, 1, &url_row, &visits);
- AddNewestVisit(ui::PAGE_TRANSITION_LINK, 6, &url_row, &visits);
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, 7, &url_row, &visits);
- fake_history_backend_->SetVisitsForUrl(&url_row, visits);
-
- // Notify typed url sync service of typed visit.
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row, visits.front());
-
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
-
- EXPECT_TRUE(URLsEqual(url_row, url_specifics));
- EXPECT_EQ(4, url_specifics.visits_size());
-
- // Check that each visit has been translated/communicated correctly.
- // Note that the specifics record visits in chronological order, and the
- // visits from the db are in reverse chronological order.
- int r = url_specifics.visits_size() - 1;
- for (int i = 0; i < url_specifics.visits_size(); ++i, --r) {
- EXPECT_EQ(visits[i].visit_time.ToInternalValue(), url_specifics.visits(r));
- EXPECT_EQ(static_cast<const int>(visits[i].transition),
- url_specifics.visit_transitions(r));
- }
-}
-
-// Delete several (but not all) local typed urls. Check that sync receives the
-// DELETE changes, and the non-deleted urls remain synced.
-TEST_F(TypedURLSyncBridgeTest, DeleteLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back("http://pie.com/");
- urls.push_back("http://cake.com/");
- urls.push_back("http://google.com/");
- urls.push_back("http://foo.com/");
-
- StartSyncing(std::vector<TypedUrlSpecifics>());
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _))
- .Times(4);
- BuildAndPushLocalChanges(4, 0, urls, &url_rows, &visit_vectors);
-
- // Delete some urls from backend and create deleted row vector.
- std::vector<URLRow> rows;
- std::set<std::string> deleted_storage_keys;
- for (size_t i = 0; i < 3u; ++i) {
- const std::string storage_key = GetStorageKey(url_rows[i].url().spec());
- deleted_storage_keys.insert(storage_key);
- fake_history_backend_->DeleteURL(url_rows[i].url());
- rows.push_back(url_rows[i]);
- }
-
- // Notify typed url sync service.
- for (const std::string& storage_key : deleted_storage_keys) {
- EXPECT_CALL(mock_processor_, Delete(storage_key, _));
- }
- bridge()->OnURLsDeleted(fake_history_backend_.get(), false, false, rows,
- std::set<GURL>());
-}
-
-// Delete the last typed visit for one (but not all) local typed urls. Check
-// that sync receives the DELETE changes, and the non-deleted urls remain
-// synced.
-TEST_F(TypedURLSyncBridgeTest, DeleteLocalTypedUrlVisit) {
- std::vector<VisitRow> visits1, visits2;
- URLRow row1 = MakeTypedUrlRowWithTwoVisits(kURL, kTitle,
- /*typed_visit=*/2,
- /*reload_visit=*/4, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, /*typed_count=*/2,
- /*last_visit=*/10, false, &visits2);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
-
- StartSyncing({});
-
- // Simulate deletion of the last typed visit (e.g. by clearing browsing data),
- // the deletion must get synced up.
- fake_history_backend_->ExpireHistoryBetween({},
- /*begin_time=*/SinceEpoch(1),
- /*end_time=*/SinceEpoch(3),
- /*user_initiated*/ true);
- URLRow row1_updated;
- ASSERT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &row1_updated));
- std::vector<URLRow> changed_urls{row1_updated};
- EXPECT_CALL(mock_processor_, Delete(GetStorageKey(kURL), _));
- bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls,
- /*is_from_expiration=*/false);
-}
-
-// Expire a local typed url (but not all). This has only impact on local store
-// (metadata in the db and in-memory maps), nothing gets synced up.
-TEST_F(TypedURLSyncBridgeTest, ExpireLocalTypedUrl) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Add two URLs into the history db and notify the bridge to get it synced up
- // and thus also metadata written into the DB.
- std::vector<VisitRow> visits1, visits2;
- URLRow row1 = MakeTypedUrlRow(kURL, kTitle, /*typed_count=*/1,
- /*last_visit=*/2, /*hidden=*/false, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, /*typed_count=*/1,
- /*last_visit=*/3, /*hidden=*/false, &visits2);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
-
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _))
- .Times(2u)
- .WillRepeatedly(StoreMetadata);
- bridge()->OnURLsModified(fake_history_backend_.get(), {row1, row2},
- /*is_from_expiration=*/false);
-
- std::string storage_key1 = GetStorageKey(kURL);
- std::string storage_key2 = GetStorageKey(kURL2);
- EXPECT_THAT(GetAllSyncMetadataKeys(),
- UnorderedElementsAre(storage_key1, storage_key2));
-
- // Simulate expiration - delete a url from the backend.
- fake_history_backend_->DeleteURL(GURL(kURL));
-
- // Notify typed url bridge of these URLs getting expired.
- EXPECT_CALL(mock_processor_, Delete).Times(0);
- EXPECT_CALL(mock_processor_, UntrackEntityForStorageKey(storage_key1));
- bridge()->OnURLsDeleted(fake_history_backend_.get(), /*all_history=*/false,
- /*expired=*/true, {row1}, std::set<GURL>());
-
- // The urls are removed from the metadata store.
- EXPECT_THAT(GetAllSyncMetadataKeys(), UnorderedElementsAre(storage_key2));
-}
-
-// Expire the last local typed visit for a URL (with some non-typed visits
-// remaining). This results in the sync entity getting untracked. This has only
-// impact on local store (metadata in the db and in-memory maps), nothing gets
-// synced up.
-TEST_F(TypedURLSyncBridgeTest, ExpireLocalTypedVisit) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Add two URLs into the history db and notify the bridge to get it synced up
- // and thus also metadata written into the DB.
- std::vector<VisitRow> visits1, visits2;
- URLRow row1 = MakeTypedUrlRowWithTwoVisits(kURL, kTitle, /*typed_visit=*/2,
- /*reload_visit=*/5, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, /*typed_count=*/1,
- /*last_visit=*/4, /*hidden=*/false, &visits2);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
-
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _))
- .Times(2u)
- .WillRepeatedly(StoreMetadata);
- bridge()->OnURLsModified(fake_history_backend_.get(), {row1, row2},
- /*is_from_expiration=*/false);
-
- std::string storage_key1 = GetStorageKey(kURL);
- std::string storage_key2 = GetStorageKey(kURL2);
- EXPECT_THAT(GetAllSyncMetadataKeys(),
- UnorderedElementsAre(storage_key1, storage_key2));
-
- // Simulate expiration of all visits before time 3.
- fake_history_backend_->ExpireHistoryBeforeForTesting(SinceEpoch(3));
- URLRow row1_updated;
- ASSERT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &row1_updated));
- EXPECT_EQ(row1_updated.typed_count(), 0);
- EXPECT_NE(row1_updated.last_visit(), base::Time());
-
- // Notify typed url sync service of these URLs getting expired (it does not
- // matter that we pass in the old version of row1, the bridge will fix it up).
- EXPECT_CALL(mock_processor_, Delete).Times(0);
- EXPECT_CALL(mock_processor_, UntrackEntityForStorageKey(storage_key1));
- bridge()->OnURLsModified(fake_history_backend_.get(), {row1},
- /*is_from_expiration=*/true);
-
- // The urls are removed from the metadata store.
- EXPECT_THAT(GetAllSyncMetadataKeys(), UnorderedElementsAre(storage_key2));
-}
-
-// Expire the last local typed visit (with no other visits left in the DB but
-// keeping the url in the DB which happens e.g. for bookmarked urls). This
-// results in the sync entity getting untracked. This has only impact on local
-// store (metadata in the db and in-memory maps), nothing gets synced up.
-TEST_F(TypedURLSyncBridgeTest, ExpireLastLocalVisit) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Add two URLs into the history db and notify the bridge to get it synced up
- // and thus also metadata written into the DB.
- std::vector<VisitRow> visits1, visits2;
- URLRow row1 = MakeTypedUrlRow(kURL, kTitle, /*typed_count=*/1,
- /*last_visit=*/1, /*hidden=*/false, &visits1);
- URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, /*typed_count=*/1,
- /*last_visit=*/3, /*hidden=*/false, &visits2);
- fake_history_backend_->SetVisitsForUrl(&row1, visits1);
- fake_history_backend_->SetVisitsForUrl(&row2, visits2);
-
- URLRow row1_original;
- ASSERT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &row1_original));
-
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _))
- .Times(2u)
- .WillRepeatedly(StoreMetadata);
- bridge()->OnURLsModified(fake_history_backend_.get(), {row1, row2},
- /*is_from_expiration=*/false);
-
- std::string storage_key1 = GetStorageKey(kURL);
- std::string storage_key2 = GetStorageKey(kURL2);
- EXPECT_THAT(GetAllSyncMetadataKeys(),
- UnorderedElementsAre(storage_key1, storage_key2));
-
- // Simulate expiration of all visits before time 2. Simulate kURL is
- // bookmarked so that it does not get deleted despite there's no visit left.
- EXPECT_CALL(*history_backend_client_, IsPinnedURL(GURL(kURL)))
- .WillOnce(Return(true));
- fake_history_backend_->ExpireHistoryBeforeForTesting(SinceEpoch(2));
- URLRow row1_updated;
- ASSERT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &row1_updated));
- EXPECT_EQ(row1_updated.typed_count(), 0);
- EXPECT_EQ(row1_updated.last_visit(), base::Time());
-
- // Notify typed url sync service of these URLs getting expired (it does not
- // matter that we pass in the old version of row1, the bridge will fix it up).
- EXPECT_CALL(mock_processor_, Delete).Times(0);
- EXPECT_CALL(mock_processor_, UntrackEntityForStorageKey(storage_key1));
- bridge()->OnURLsModified(fake_history_backend_.get(), {row1_updated},
- /*is_from_expiration=*/true);
-
- // The urls are removed from the metadata store.
- EXPECT_THAT(GetAllSyncMetadataKeys(), UnorderedElementsAre(storage_key2));
-}
-
-// Saturate the visits for a typed url with both TYPED and LINK navigations.
-// Check that no more than kMaxTypedURLVisits are synced, and that LINK visits
-// are dropped rather than TYPED ones.
-TEST_F(TypedURLSyncBridgeTest, MaxVisitLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- EXPECT_CALL(mock_processor_, Put).Times(0);
- StartSyncing(std::vector<TypedUrlSpecifics>());
- BuildAndPushLocalChanges(0, 1, urls, &url_rows, &visit_vectors);
-
- URLRow url_row = url_rows.front();
- std::vector<VisitRow> visits;
-
- // Add `kMaxTypedUrlVisits` + 10 visits to the url. The 10 oldest
- // non-typed visits are expected to be skipped.
- int i = 1;
- for (; i <= kMaxTypedUrlVisits - 20; ++i) {
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
- }
- for (; i <= kMaxTypedUrlVisits; ++i) {
- AddNewestVisit(ui::PAGE_TRANSITION_LINK, i, &url_row, &visits);
- }
- for (; i <= kMaxTypedUrlVisits + 10; ++i) {
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
- }
-
- fake_history_backend_->SetVisitsForUrl(&url_row, visits);
-
- // Notify typed url sync service of typed visit.
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row, visits.front());
-
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
- ASSERT_EQ(kMaxTypedUrlVisits, url_specifics.visits_size());
-
- // Check that each visit has been translated/communicated correctly.
- // Note that the specifics records visits in chronological order, and the
- // visits from the db are in reverse chronological order.
- int num_typed_visits_synced = 0;
- int num_other_visits_synced = 0;
- int r = url_specifics.visits_size() - 1;
- for (int j = 0; j < url_specifics.visits_size(); ++j, --r) {
- if (url_specifics.visit_transitions(j) ==
- static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED)) {
- ++num_typed_visits_synced;
- } else {
- ++num_other_visits_synced;
- }
- }
- EXPECT_EQ(kMaxTypedUrlVisits - 10, num_typed_visits_synced);
- EXPECT_EQ(10, num_other_visits_synced);
-}
-
-// Add enough visits to trigger throttling of updates to a typed url. Check that
-// sync does not receive an update until the proper throttle interval has been
-// reached.
-TEST_F(TypedURLSyncBridgeTest, ThrottleVisitLocalTypedUrl) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- EXPECT_CALL(mock_processor_, Put).Times(0);
- StartSyncing(std::vector<TypedUrlSpecifics>());
- BuildAndPushLocalChanges(0, 1, urls, &url_rows, &visit_vectors);
-
- URLRow url_row = url_rows.front();
- std::vector<VisitRow> visits;
-
- // Add enough visits to the url so that typed count is above the throttle
- // limit, and not right on the interval that gets synced.
- int i = 1;
- for (; i < kVisitThrottleThreshold + kVisitThrottleMultiple / 2; ++i) {
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
- }
- fake_history_backend_->SetVisitsForUrl(&url_row, visits);
-
- // Notify typed url sync service of typed visit.
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row, visits.front());
-
- visits.clear();
- for (; i % kVisitThrottleMultiple != 1; ++i) {
- AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
- }
- --i; // Account for the increment before the condition ends.
- fake_history_backend_->SetVisitsForUrl(&url_row, visits);
-
- // Notify typed url sync service of typed visit.
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- bridge()->OnURLVisited(fake_history_backend_.get(), url_row, visits.front());
-
- ASSERT_EQ(i, entity_data.specifics.typed_url().visits_size());
-}
-
-// Create a remote typed URL and visit, then send to sync bridge after sync
-// has started. Check that local DB is received the new URL and visit.
-TEST_F(TypedURLSyncBridgeTest, AddUrlAndVisits) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
- EXPECT_CALL(mock_processor_, Put).Times(0);
- EXPECT_CALL(mock_processor_, UntrackEntityForStorageKey).Times(0);
-
- EXPECT_CALL(mock_processor_,
- UpdateStorageKey(
- AllOf(HasURLInSpecifics(kURL), HasTitleInSpecifics(kTitle)),
- IntToStorageKey(1), _));
- std::vector<VisitRow> visits =
- ApplyUrlAndVisitsChange(kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3,
- /*hidden=*/false, EntityChange::ACTION_ADD);
-
- Time visit_time = SinceEpoch(3);
- std::vector<VisitRow> all_visits;
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- EXPECT_EQ(1U, all_visits.size());
- EXPECT_EQ(visit_time, all_visits[0].visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[0].transition, visits[0].transition));
- URLRow url_row;
- EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
-}
-
-// Create a remote typed URL with expired visit, then send to sync bridge after
-// sync has started. Check that local DB did not receive the expired URL and
-// visit.
-TEST_F(TypedURLSyncBridgeTest, AddExpiredUrlAndVisits) {
- EXPECT_CALL(mock_processor_, Put).Times(0);
- EXPECT_CALL(mock_processor_, UpdateStorageKey).Times(0);
-
- EXPECT_CALL(mock_processor_, UntrackEntityForClientTagHash);
- StartSyncing(std::vector<TypedUrlSpecifics>());
- ApplyUrlAndVisitsChange(kURL, kTitle, /*typed_count=*/1,
- /*last_visit=*/kExpiredVisit,
- /*hidden=*/false, EntityChange::ACTION_ADD);
-
- ASSERT_EQ(0, fake_history_backend_->GetIdByUrl(GURL(kURL)));
-}
-
-// Update a remote typed URL and create a new visit that is already synced, then
-// send the update to sync bridge. Check that local DB is received an
-// UPDATE for the existing url and new visit.
-TEST_F(TypedURLSyncBridgeTest, UpdateUrlAndVisits) {
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- std::vector<VisitRow> visits =
- ApplyUrlAndVisitsChange(kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3,
- /*hidden=*/false, EntityChange::ACTION_ADD);
- Time visit_time = SinceEpoch(3);
- std::vector<VisitRow> all_visits;
- URLRow url_row;
-
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
-
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
-
- EXPECT_EQ(1U, all_visits.size());
- EXPECT_EQ(visit_time, all_visits[0].visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[0].transition, visits[0].transition));
- EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
-
- std::vector<VisitRow> new_visits = ApplyUrlAndVisitsChange(
- kURL, kTitle2, /*typed_count=*/2, /*last_visit=*/6,
- /*hidden=*/false, EntityChange::ACTION_UPDATE);
-
- Time new_visit_time = SinceEpoch(6);
- url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
-
- EXPECT_EQ(2U, all_visits.size());
- EXPECT_EQ(new_visit_time, all_visits.back().visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits.back().transition, new_visits[0].transition));
- EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- EXPECT_EQ(kTitle2, base::UTF16ToUTF8(url_row.title()));
-}
-
-// Delete a typed urls which already synced. Check that local DB receives the
-// DELETE changes.
-TEST_F(TypedURLSyncBridgeTest, DeleteUrlAndVisits) {
- std::vector<URLRow> url_rows;
- std::vector<std::vector<VisitRow>> visit_vectors;
- std::vector<std::string> urls;
- urls.push_back(kURL);
-
- StartSyncing(std::vector<TypedUrlSpecifics>());
- EXPECT_CALL(mock_processor_,
- Put(IsValidStorageKey(), Pointee(HasTypedUrlInSpecifics()), _));
- BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors);
-
- Time visit_time = SinceEpoch(3);
- std::vector<VisitRow> all_visits;
- URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_NE(0, url_id);
- fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
- EXPECT_EQ(1U, all_visits.size());
- EXPECT_EQ(visit_time, all_visits[0].visit_time);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- all_visits[0].transition, visit_vectors[0][0].transition));
- URLRow url_row;
- EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
-
- // Add observer back to check if TypedUrlSyncBridge receive delete
- // changes back from fake_history_backend_.
- AddObserver();
-
- ApplyUrlAndVisitsChange(kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3,
- /*hidden=*/false, EntityChange::ACTION_DELETE);
-
- EXPECT_FALSE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
- url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
- ASSERT_EQ(0, url_id);
-}
-
-// Create two set of visits for history DB and sync DB, two same set of visits
-// are same. Check DiffVisits will return empty set of diff visits.
-TEST_F(TypedURLSyncBridgeTest, DiffVisitsSame) {
- std::vector<VisitRow> old_visits;
- sync_pb::TypedUrlSpecifics new_url;
-
- const int64_t visits[] = {1024, 2065, 65534, 1237684};
-
- for (int64_t visit : visits) {
- old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
- 0, true, 0);
- new_url.add_visits(visit);
- new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
- }
-
- std::vector<VisitInfo> new_visits;
- std::vector<VisitRow> removed_visits;
-
- DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
- EXPECT_TRUE(new_visits.empty());
- EXPECT_TRUE(removed_visits.empty());
-}
-
-// Create two set of visits for history DB and sync DB. Check DiffVisits will
-// return correct set of diff visits.
-TEST_F(TypedURLSyncBridgeTest, DiffVisitsRemove) {
- std::vector<VisitRow> old_visits;
- sync_pb::TypedUrlSpecifics new_url;
-
- const int64_t visits_left[] = {1, 2, 1024, 1500, 2065,
- 6000, 65534, 1237684, 2237684};
- const int64_t visits_right[] = {1024, 2065, 65534, 1237684};
-
- // DiffVisits will not remove the first visit, because we never delete visits
- // from the start of the array (since those visits can get truncated by the
- // size-limiting code).
- const int64_t visits_removed[] = {1500, 6000, 2237684};
-
- for (int64_t visit : visits_left) {
- old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
- 0, true, 0);
- }
-
- for (int64_t visit : visits_right) {
- new_url.add_visits(visit);
- new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
- }
-
- std::vector<VisitInfo> new_visits;
- std::vector<VisitRow> removed_visits;
-
- DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
- EXPECT_TRUE(new_visits.empty());
- ASSERT_EQ(removed_visits.size(), std::size(visits_removed));
- for (size_t i = 0; i < std::size(visits_removed); ++i) {
- EXPECT_EQ(removed_visits[i].visit_time.ToInternalValue(),
- visits_removed[i]);
- }
-}
-
-// Create two set of visits for history DB and sync DB. Check DiffVisits will
-// return correct set of diff visits.
-TEST_F(TypedURLSyncBridgeTest, DiffVisitsAdd) {
- std::vector<VisitRow> old_visits;
- sync_pb::TypedUrlSpecifics new_url;
-
- const int64_t visits_left[] = {1024, 2065, 65534, 1237684};
- const int64_t visits_right[] = {1, 1024, 1500, 2065,
- 6000, 65534, 1237684, 2237684};
-
- const int64_t visits_added[] = {1, 1500, 6000, 2237684};
-
- for (int64_t visit : visits_left) {
- old_visits.emplace_back(0, SinceEpoch(visit), 0, ui::PAGE_TRANSITION_TYPED,
- 0, true, 0);
- }
-
- for (int64_t visit : visits_right) {
- new_url.add_visits(visit);
- new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
- }
-
- std::vector<VisitInfo> new_visits;
- std::vector<VisitRow> removed_visits;
-
- DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
- EXPECT_TRUE(removed_visits.empty());
- ASSERT_TRUE(new_visits.size() == std::size(visits_added));
- for (size_t i = 0; i < std::size(visits_added); ++i) {
- EXPECT_EQ(new_visits[i].first.ToInternalValue(), visits_added[i]);
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- new_visits[i].second, ui::PAGE_TRANSITION_TYPED));
- }
-}
-
-// Create three visits, check RELOAD visit is removed by
-// WriteToTypedUrlSpecifics so it won't apply to sync DB.
-TEST_F(TypedURLSyncBridgeTest, WriteTypedUrlSpecifics) {
- std::vector<VisitRow> visits;
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, 1));
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_RELOAD, 2));
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, 3));
-
- URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, 100, false, &visits));
- sync_pb::TypedUrlSpecifics typed_url;
- WriteToTypedUrlSpecifics(url, visits, &typed_url);
- // RELOAD visits should be removed.
- EXPECT_EQ(2, typed_url.visits_size());
- EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
- EXPECT_EQ(1, typed_url.visits(0));
- EXPECT_EQ(3, typed_url.visits(1));
- EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
- typed_url.visit_transitions(0));
- EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_LINK),
- typed_url.visit_transitions(1));
-}
-
-// Create 101 visits, check WriteToTypedUrlSpecifics will only keep 100 visits.
-TEST_F(TypedURLSyncBridgeTest, TooManyVisits) {
- std::vector<VisitRow> visits;
- int64_t timestamp = 1000;
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, timestamp++));
- for (int i = 0; i < 100; ++i) {
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, timestamp++));
- }
- URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, timestamp++, false, &visits));
- sync_pb::TypedUrlSpecifics typed_url;
- WriteToTypedUrlSpecifics(url, visits, &typed_url);
- // # visits should be capped at 100.
- EXPECT_EQ(100, typed_url.visits_size());
- EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
- EXPECT_EQ(1000, typed_url.visits(0));
- // Visit with timestamp of 1001 should be omitted since we should have
- // skipped that visit to stay under the cap.
- EXPECT_EQ(1002, typed_url.visits(1));
- EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
- typed_url.visit_transitions(0));
- EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_LINK),
- typed_url.visit_transitions(1));
-}
-
-// Create 306 visits, check WriteToTypedUrlSpecifics will only keep 100 typed
-// visits.
-TEST_F(TypedURLSyncBridgeTest, TooManyTypedVisits) {
- std::vector<VisitRow> visits;
- int64_t timestamp = 1000;
- for (int i = 0; i < 102; ++i) {
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, timestamp++));
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, timestamp++));
- visits.push_back(CreateVisit(ui::PAGE_TRANSITION_RELOAD, timestamp++));
- }
- URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, timestamp++, false, &visits));
- sync_pb::TypedUrlSpecifics typed_url;
- WriteToTypedUrlSpecifics(url, visits, &typed_url);
- // # visits should be capped at 100.
- EXPECT_EQ(100, typed_url.visits_size());
- EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
- // First two typed visits should be skipped.
- EXPECT_EQ(1006, typed_url.visits(0));
-
- // Ensure there are no non-typed visits since that's all that should fit.
- for (int i = 0; i < typed_url.visits_size(); ++i) {
- EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
- typed_url.visit_transitions(i));
- }
-}
-
-// Create a typed url without visit, check WriteToTypedUrlSpecifics will return
-// false for it.
-TEST_F(TypedURLSyncBridgeTest, NoTypedVisits) {
- std::vector<VisitRow> visits;
- URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, 1000, false, &visits));
- sync_pb::TypedUrlSpecifics typed_url;
- EXPECT_FALSE(WriteToTypedUrlSpecifics(url, visits, &typed_url));
- // URLs with no typed URL visits should not been written to specifics.
- EXPECT_EQ(0, typed_url.visits_size());
-}
-
-TEST_F(TypedURLSyncBridgeTest, MergeUrls) {
- std::vector<VisitRow> visits1;
- URLRow row1(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits1));
- sync_pb::TypedUrlSpecifics specs1(
- MakeTypedUrlSpecifics(kURL, kTitle, 3, false));
- URLRow new_row1((GURL(kURL)));
- std::vector<VisitInfo> new_visits1;
- EXPECT_TRUE(TypedURLSyncBridgeTest::MergeUrls(specs1, row1, &visits1,
- &new_row1, &new_visits1) ==
- TypedURLSyncBridgeTest::DIFF_NONE);
-
- std::vector<VisitRow> visits2;
- URLRow row2(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits2));
- sync_pb::TypedUrlSpecifics specs2(
- MakeTypedUrlSpecifics(kURL, kTitle, 3, true));
- std::vector<VisitRow> expected_visits2;
- URLRow expected2(
- MakeTypedUrlRow(kURL, kTitle, 2, 3, true, &expected_visits2));
- URLRow new_row2((GURL(kURL)));
- std::vector<VisitInfo> new_visits2;
- EXPECT_TRUE(TypedURLSyncBridgeTest::MergeUrls(specs2, row2, &visits2,
- &new_row2, &new_visits2) ==
- TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED);
- EXPECT_TRUE(URLsEqual(new_row2, expected2));
-
- std::vector<VisitRow> visits3;
- URLRow row3(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits3));
- sync_pb::TypedUrlSpecifics specs3(
- MakeTypedUrlSpecifics(kURL, kTitle2, 3, true));
- std::vector<VisitRow> expected_visits3;
- URLRow expected3(
- MakeTypedUrlRow(kURL, kTitle2, 2, 3, true, &expected_visits3));
- URLRow new_row3((GURL(kURL)));
- std::vector<VisitInfo> new_visits3;
- EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED |
- TypedURLSyncBridgeTest::DIFF_NONE,
- TypedURLSyncBridgeTest::MergeUrls(specs3, row3, &visits3, &new_row3,
- &new_visits3));
- EXPECT_TRUE(URLsEqual(new_row3, expected3));
-
- // Create one node in history DB with timestamp of 3, and one node in sync
- // DB with timestamp of 4. Result should contain one new item (4).
- std::vector<VisitRow> visits4;
- URLRow row4(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits4));
- sync_pb::TypedUrlSpecifics specs4(
- MakeTypedUrlSpecifics(kURL, kTitle2, 4, false));
- std::vector<VisitRow> expected_visits4;
- URLRow expected4(
- MakeTypedUrlRow(kURL, kTitle2, 2, 4, false, &expected_visits4));
- URLRow new_row4((GURL(kURL)));
- std::vector<VisitInfo> new_visits4;
- EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_UPDATE_NODE |
- TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED |
- TypedURLSyncBridgeTest::DIFF_LOCAL_VISITS_ADDED,
- TypedURLSyncBridgeTest::MergeUrls(specs4, row4, &visits4, &new_row4,
- &new_visits4));
- EXPECT_EQ(1U, new_visits4.size());
- EXPECT_EQ(specs4.visits(0), new_visits4[0].first.ToInternalValue());
- EXPECT_TRUE(URLsEqual(new_row4, expected4));
- EXPECT_EQ(2U, visits4.size());
-
- std::vector<VisitRow> visits5;
- URLRow row5(MakeTypedUrlRow(kURL, kTitle, 1, 4, false, &visits5));
- sync_pb::TypedUrlSpecifics specs5(
- MakeTypedUrlSpecifics(kURL, kTitle, 3, false));
- std::vector<VisitRow> expected_visits5;
- URLRow expected5(
- MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &expected_visits5));
- URLRow new_row5((GURL(kURL)));
- std::vector<VisitInfo> new_visits5;
-
- // UPDATE_NODE should be set because row5 has a newer last_visit timestamp.
- EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_UPDATE_NODE |
- TypedURLSyncBridgeTest::DIFF_NONE,
- TypedURLSyncBridgeTest::MergeUrls(specs5, row5, &visits5, &new_row5,
- &new_visits5));
- EXPECT_TRUE(URLsEqual(new_row5, expected5));
- EXPECT_EQ(0U, new_visits5.size());
-}
-
-// Tests to ensure that we don't resurrect expired URLs (URLs that have been
-// deleted from the history DB but still exist in the sync DB).
-TEST_F(TypedURLSyncBridgeTest, MergeUrlsAfterExpiration) {
- // First, create a history row that has two visits, with timestamps 2 and 3.
- std::vector<VisitRow>(history_visits);
- history_visits.push_back(
- VisitRow(0, SinceEpoch(2), 0, ui::PAGE_TRANSITION_TYPED, 0, true, 0));
- URLRow history_url(
- MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &history_visits));
-
- // Now, create a sync node with visits at timestamps 1, 2, 3, 4.
- sync_pb::TypedUrlSpecifics node(
- MakeTypedUrlSpecifics(kURL, kTitle, 1, false));
- node.add_visits(2);
- node.add_visits(3);
- node.add_visits(4);
- node.add_visit_transitions(2);
- node.add_visit_transitions(3);
- node.add_visit_transitions(4);
- URLRow new_history_url(history_url.url());
- std::vector<VisitInfo> new_visits;
- EXPECT_EQ(
- TypedURLSyncBridgeTest::DIFF_NONE |
- TypedURLSyncBridgeTest::DIFF_LOCAL_VISITS_ADDED,
- TypedURLSyncBridgeTest::MergeUrls(node, history_url, &history_visits,
- &new_history_url, &new_visits));
- EXPECT_TRUE(URLsEqual(history_url, new_history_url));
- EXPECT_EQ(1U, new_visits.size());
- EXPECT_EQ(4U, new_visits[0].first.ToInternalValue());
- // We should not sync the visit with timestamp #1 since it is earlier than
- // any other visit for this URL in the history DB. But we should sync visit
- // #4.
- EXPECT_EQ(3U, history_visits.size());
- EXPECT_EQ(2U, history_visits[0].visit_time.ToInternalValue());
- EXPECT_EQ(3U, history_visits[1].visit_time.ToInternalValue());
- EXPECT_EQ(4U, history_visits[2].visit_time.ToInternalValue());
-}
-
-// Create a local typed URL with one expired TYPED visit,
-// MergeFullSyncData should not pass it to sync. And then add a non
-// expired visit, OnURLsModified should only send the non expired visit to sync.
-TEST_F(TypedURLSyncBridgeTest, LocalExpiredTypedUrlDoNotSync) {
- URLRow row;
- std::vector<URLRow> changed_urls;
- std::vector<VisitRow> visits;
-
- // Add an expired typed URL to local.
- row = MakeTypedUrlRow(kURL, kTitle, 1, kExpiredVisit, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&row, visits);
-
- // Check change processor did not receive expired typed URL.
- EXPECT_CALL(mock_processor_, Put).Times(0);
- StartSyncing(std::vector<TypedUrlSpecifics>());
-
- // Add a non expired typed URL to local.
- row = MakeTypedUrlRow(kURL, kTitle, 2, 1, false, &visits);
- fake_history_backend_->SetVisitsForUrl(&row, visits);
-
- changed_urls.push_back(row);
- // Check change processor did not receive expired typed URL.
- EntityData entity_data;
- EXPECT_CALL(mock_processor_, Put(GetStorageKey(kURL), _, _))
- .WillOnce(SaveArgPointeeMove<1>(&entity_data));
- // Notify typed url sync service of the update.
- bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls,
- /*is_from_expiration=*/false);
-
- // Get typed url specifics. Verify only a non-expired visit received.
- const sync_pb::TypedUrlSpecifics& url_specifics =
- entity_data.specifics.typed_url();
-
- EXPECT_TRUE(URLsEqual(row, url_specifics));
- ASSERT_EQ(1, url_specifics.visits_size());
- ASSERT_EQ(static_cast<const int>(visits.size() - 1),
- url_specifics.visits_size());
- EXPECT_EQ(visits[1].visit_time.ToInternalValue(), url_specifics.visits(0));
- EXPECT_EQ(static_cast<const int>(visits[1].transition),
- url_specifics.visit_transitions(0));
-}
-
-// Tests that database error gets reported to processor as model type error.
-TEST_F(TypedURLSyncBridgeTest, DatabaseError) {
- EXPECT_CALL(mock_processor_, ReportError);
- bridge()->OnDatabaseError();
-}
-
-} // namespace history
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index b2db5f8..91eb5d8 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -21,7 +21,6 @@
#import "components/consent_auditor/consent_auditor.h"
#import "components/dom_distiller/core/dom_distiller_service.h"
#import "components/history/core/browser/history_service.h"
-#import "components/history/core/browser/sync/typed_url_sync_bridge.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/metrics/demographics/user_demographics.h"
#import "components/password_manager/core/browser/password_store_interface.h"