diff options
author | gangwu <gangwu@chromium.org> | 2015-12-15 15:00:15 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-15 23:01:12 +0000 |
commit | a9fc84392b6dd9d1ae1801c4f6b2334d4d1fad30 (patch) | |
tree | 478f2825eee3588e439f66f99cd9d0be3fc71854 /components/history | |
parent | 3baa84abc53467cc667243d1a45edc0a9a94662a (diff) | |
download | chromium_src-a9fc84392b6dd9d1ae1801c4f6b2334d4d1fad30.zip chromium_src-a9fc84392b6dd9d1ae1801c4f6b2334d4d1fad30.tar.gz chromium_src-a9fc84392b6dd9d1ae1801c4f6b2334d4d1fad30.tar.bz2 |
Using TypedUrlSyncableService instead of
TypedUrlModelAssociator and TypedUrlChangeProcessor.
And also fix some test issues because of this changes.
BUG=77819,543722
Committed: https://crrev.com/0295776af08f6f03850492a70b60d52855a112dd
Cr-Commit-Position: refs/heads/master@{#365199}
Review URL: https://codereview.chromium.org/1454503002
Cr-Commit-Position: refs/heads/master@{#365368}
Diffstat (limited to 'components/history')
13 files changed, 90 insertions, 2120 deletions
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn index e894ec6..0d88839 100644 --- a/components/history/core/browser/BUILD.gn +++ b/components/history/core/browser/BUILD.gn @@ -62,12 +62,8 @@ static_library("browser") { "top_sites_impl.cc", "top_sites_impl.h", "top_sites_observer.h", - "typed_url_change_processor.cc", - "typed_url_change_processor.h", "typed_url_data_type_controller.cc", "typed_url_data_type_controller.h", - "typed_url_model_associator.cc", - "typed_url_model_associator.h", "typed_url_syncable_service.cc", "typed_url_syncable_service.h", "url_database.cc", @@ -154,7 +150,6 @@ source_set("unit_tests") { "top_sites_cache_unittest.cc", "top_sites_database_unittest.cc", "top_sites_impl_unittest.cc", - "typed_url_model_associator_unittest.cc", "typed_url_syncable_service_unittest.cc", "url_database_unittest.cc", "url_utils_unittest.cc", diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc index 0982fcd..0779add 100644 --- a/components/history/core/browser/history_backend.cc +++ b/components/history/core/browser/history_backend.cc @@ -2551,10 +2551,6 @@ void HistoryBackend::NotifyURLVisited(ui::PageTransition transition, const URLRow& row, const RedirectList& redirects, base::Time visit_time) { - if (typed_url_syncable_service_) - typed_url_syncable_service_->OnURLVisited(this, transition, row, redirects, - visit_time); - FOR_EACH_OBSERVER(HistoryBackendObserver, observers_, OnURLVisited(this, transition, row, redirects, visit_time)); @@ -2563,9 +2559,6 @@ void HistoryBackend::NotifyURLVisited(ui::PageTransition transition, } void HistoryBackend::NotifyURLsModified(const URLRows& rows) { - if (typed_url_syncable_service_) - typed_url_syncable_service_->OnURLsModified(this, rows); - FOR_EACH_OBSERVER(HistoryBackendObserver, observers_, OnURLsModified(this, rows)); @@ -2578,11 +2571,6 @@ void HistoryBackend::NotifyURLsDeleted(bool all_history, const URLRows& rows, const std::set<GURL>& favicon_urls) { URLRows copied_rows(rows); - if (typed_url_syncable_service_) { - typed_url_syncable_service_->OnURLsDeleted(this, all_history, expired, - copied_rows, favicon_urls); - } - FOR_EACH_OBSERVER( HistoryBackendObserver, observers_, OnURLsDeleted(this, all_history, expired, copied_rows, favicon_urls)); diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h index aa36e9e..c535158 100644 --- a/components/history/core/browser/history_service.h +++ b/components/history/core/browser/history_service.h @@ -146,7 +146,7 @@ class HistoryService : public syncer::SyncableService, public KeyedService { // Returns a pointer to the TypedUrlSyncableService owned by HistoryBackend. // This method should only be called from the history thread, because the // returned service is intended to be accessed only via the history thread. - TypedUrlSyncableService* GetTypedUrlSyncableService() const; + virtual TypedUrlSyncableService* GetTypedUrlSyncableService() const; // KeyedService: void Shutdown() override; diff --git a/components/history/core/browser/typed_url_change_processor.cc b/components/history/core/browser/typed_url_change_processor.cc deleted file mode 100644 index 4395e80..0000000 --- a/components/history/core/browser/typed_url_change_processor.cc +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/history/core/browser/typed_url_change_processor.h" - -#include "base/location.h" -#include "base/metrics/histogram.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/thread_task_runner_handle.h" -#include "components/history/core/browser/history_backend.h" -#include "components/history/core/browser/typed_url_model_associator.h" -#include "sync/internal_api/public/change_record.h" -#include "sync/internal_api/public/read_node.h" -#include "sync/internal_api/public/write_node.h" -#include "sync/internal_api/public/write_transaction.h" -#include "sync/protocol/typed_url_specifics.pb.h" -#include "sync/syncable/entry.h" // TODO(tim): Investigating bug 121587. - -namespace browser_sync { - -// 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; - -TypedUrlChangeProcessor::TypedUrlChangeProcessor( - TypedUrlModelAssociator* model_associator, - history::HistoryBackend* history_backend, - sync_driver::DataTypeErrorHandler* error_handler, - const scoped_refptr<base::SingleThreadTaskRunner> ui_thread) - : sync_driver::ChangeProcessor(error_handler), - model_associator_(model_associator), - history_backend_(history_backend), - ui_thread_(ui_thread), - backend_thread_(base::ThreadTaskRunnerHandle::Get()), - disconnected_(false), - history_backend_observer_(this) { - DCHECK(model_associator); - DCHECK(history_backend); - DCHECK(error_handler); -} - -TypedUrlChangeProcessor::~TypedUrlChangeProcessor() { - DCHECK(backend_thread_->BelongsToCurrentThread()); - DCHECK(history_backend_); - history_backend_->RemoveObserver(this); -} - -void TypedUrlChangeProcessor::OnURLVisited( - history::HistoryBackend* history_backend, - ui::PageTransition transition, - const history::URLRow& row, - const history::RedirectList& redirects, - base::Time visit_time) { - DCHECK(backend_thread_->BelongsToCurrentThread()); - - base::AutoLock al(disconnect_lock_); - if (disconnected_) - return; - - DVLOG(1) << "Observed typed_url change."; - if (ShouldSyncVisit(row.typed_count(), transition)) { - VisitsToSync visits_to_sync; - if (FixupURLAndGetVisitsToSync(row, &visits_to_sync)) { - syncer::WriteTransaction trans(FROM_HERE, share_handle()); - CreateOrUpdateSyncNode(&trans, visits_to_sync.row, visits_to_sync.visits); - } - } - UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlChangeProcessorErrors", - model_associator_->GetErrorPercentage()); -} - -void TypedUrlChangeProcessor::OnURLsModified( - history::HistoryBackend* history_backend, - const history::URLRows& changed_urls) { - DCHECK(backend_thread_->BelongsToCurrentThread()); - - base::AutoLock al(disconnect_lock_); - if (disconnected_) - return; - - std::vector<VisitsToSync> visits_to_sync_vector; - - DVLOG(1) << "Observed typed_url change."; - for (const auto& row : changed_urls) { - if (row.typed_count() >= 0) { - VisitsToSync visits_to_sync; - if (FixupURLAndGetVisitsToSync(row, &visits_to_sync)) { - visits_to_sync_vector.push_back(visits_to_sync); - } - } - } - - if (!visits_to_sync_vector.empty()) { - syncer::WriteTransaction trans(FROM_HERE, share_handle()); - for (const auto& visits_to_sync : visits_to_sync_vector) { - // If there were any errors updating the sync node, just ignore them and - // continue on to process the next URL. - CreateOrUpdateSyncNode(&trans, visits_to_sync.row, visits_to_sync.visits); - } - } - - UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlChangeProcessorErrors", - model_associator_->GetErrorPercentage()); -} - -void TypedUrlChangeProcessor::OnURLsDeleted( - history::HistoryBackend* history_backend, - bool all_history, - bool expired, - const history::URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { - DCHECK(backend_thread_->BelongsToCurrentThread()); - - base::AutoLock al(disconnect_lock_); - if (disconnected_) - return; - - DVLOG(1) << "Observed typed_url change."; - - syncer::WriteTransaction trans(FROM_HERE, share_handle()); - - // Ignore archivals (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 archival 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) - return; - - if (all_history) { - if (!model_associator_->DeleteAllNodes(&trans)) { - syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed to delete local nodes.", - syncer::TYPED_URLS); - error_handler()->OnSingleDataTypeUnrecoverableError(error); - return; - } - } else { - for (const auto& row : deleted_rows) { - syncer::WriteNode sync_node(&trans); - // The deleted URL could have been non-typed, so it might not be found - // in the sync DB. - if (sync_node.InitByClientTagLookup(syncer::TYPED_URLS, - row.url().spec()) == - syncer::BaseNode::INIT_OK) { - sync_node.Tombstone(); - } - } - } - UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlChangeProcessorErrors", - model_associator_->GetErrorPercentage()); -} - -TypedUrlChangeProcessor::VisitsToSync::VisitsToSync() {} - -TypedUrlChangeProcessor::VisitsToSync::~VisitsToSync() {} - -bool TypedUrlChangeProcessor::FixupURLAndGetVisitsToSync( - const history::URLRow& url, - VisitsToSync* visits_to_sync) { - DCHECK_GE(url.typed_count(), 0); - - // Get the visits for this node. - visits_to_sync->row = url; - if (!model_associator_->FixupURLAndGetVisits(&visits_to_sync->row, - &visits_to_sync->visits)) { - DLOG(ERROR) << "Could not load visits for url: " << url.url(); - return false; - } - - if (std::find_if(visits_to_sync->visits.begin(), visits_to_sync->visits.end(), - [](const history::VisitRow& visit) { - return ui::PageTransitionCoreTypeIs( - visit.transition, ui::PAGE_TRANSITION_TYPED); - }) == visits_to_sync->visits.end()) { - // This URL has no TYPED visits, don't sync it. - return false; - } - - if (model_associator_->ShouldIgnoreUrl(visits_to_sync->row.url())) - return false; - - return true; -} - -void TypedUrlChangeProcessor::CreateOrUpdateSyncNode( - syncer::WriteTransaction* trans, - const history::URLRow& url, - const history::VisitVector& visit_vector) { - DCHECK_GE(url.typed_count(), 0); - DCHECK(!visit_vector.empty()); - - std::string tag = url.url().spec(); - syncer::WriteNode update_node(trans); - syncer::BaseNode::InitByLookupResult result = - update_node.InitByClientTagLookup(syncer::TYPED_URLS, tag); - if (result == syncer::BaseNode::INIT_OK) { - model_associator_->WriteToSyncNode(url, visit_vector, &update_node); - } else if (result == syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY) { - syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed to decrypt.", syncer::TYPED_URLS); - error_handler()->OnSingleDataTypeUnrecoverableError(error); - return; - } else { - syncer::WriteNode create_node(trans); - syncer::WriteNode::InitUniqueByCreationResult result = - create_node.InitUniqueByCreation(syncer::TYPED_URLS, tag); - if (result != syncer::WriteNode::INIT_SUCCESS) { - syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed to create sync node", syncer::TYPED_URLS); - error_handler()->OnSingleDataTypeUnrecoverableError(error); - return; - } - - create_node.SetTitle(tag); - model_associator_->WriteToSyncNode(url, visit_vector, &create_node); - } -} - -bool TypedUrlChangeProcessor::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 >= 0 && - (typed_count < kTypedUrlVisitThrottleThreshold || - (typed_count % kTypedUrlVisitThrottleMultiple) == 0)); -} - -void TypedUrlChangeProcessor::ApplyChangesFromSyncModel( - const syncer::BaseTransaction* trans, - int64 model_version, - const syncer::ImmutableChangeRecordList& changes) { - DCHECK(backend_thread_->BelongsToCurrentThread()); - - base::AutoLock al(disconnect_lock_); - if (disconnected_) - return; - - syncer::ReadNode typed_url_root(trans); - if (typed_url_root.InitTypeRoot(syncer::TYPED_URLS) != - syncer::BaseNode::INIT_OK) { - syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed to init type root.", syncer::TYPED_URLS); - error_handler()->OnSingleDataTypeUnrecoverableError(error); - return; - } - - DCHECK(pending_new_urls_.empty() && pending_new_visits_.empty() && - pending_deleted_visits_.empty() && pending_updated_urls_.empty() && - pending_deleted_urls_.empty()); - - for (syncer::ChangeRecordList::const_iterator it = changes.Get().begin(); - it != changes.Get().end(); ++it) { - if (syncer::ChangeRecord::ACTION_DELETE == it->action) { - DCHECK(it->specifics.has_typed_url()) - << "Typed URL delete change does not have necessary specifics."; - GURL url(it->specifics.typed_url().url()); - pending_deleted_urls_.push_back(url); - continue; - } - - syncer::ReadNode sync_node(trans); - if (sync_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) { - syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed to init sync node.", syncer::TYPED_URLS); - error_handler()->OnSingleDataTypeUnrecoverableError(error); - return; - } - - DCHECK(syncer::TYPED_URLS == sync_node.GetModelType()); - - const sync_pb::TypedUrlSpecifics& typed_url( - sync_node.GetTypedUrlSpecifics()); - DCHECK(typed_url.visits_size()); - - if (model_associator_->ShouldIgnoreUrl(GURL(typed_url.url()))) - continue; - - sync_pb::TypedUrlSpecifics filtered_url = - model_associator_->FilterExpiredVisits(typed_url); - if (!filtered_url.visits_size()) { - continue; - } - - model_associator_->UpdateFromSyncDB( - filtered_url, &pending_new_visits_, &pending_deleted_visits_, - &pending_updated_urls_, &pending_new_urls_); - } -} - -void TypedUrlChangeProcessor::CommitChangesFromSyncModel() { - DCHECK(backend_thread_->BelongsToCurrentThread()); - - base::AutoLock al(disconnect_lock_); - if (disconnected_) - return; - - // Make sure we stop listening for changes while we're modifying the backend, - // so we don't try to re-apply these changes to the sync DB. - ScopedStopObserving<TypedUrlChangeProcessor> stop_observing(this); - if (!pending_deleted_urls_.empty()) - history_backend_->DeleteURLs(pending_deleted_urls_); - - model_associator_->WriteToHistoryBackend( - &pending_new_urls_, &pending_updated_urls_, &pending_new_visits_, - &pending_deleted_visits_); - - pending_new_urls_.clear(); - pending_updated_urls_.clear(); - pending_new_visits_.clear(); - pending_deleted_visits_.clear(); - pending_deleted_urls_.clear(); - UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlChangeProcessorErrors", - model_associator_->GetErrorPercentage()); -} - -void TypedUrlChangeProcessor::Disconnect() { - base::AutoLock al(disconnect_lock_); - disconnected_ = true; -} - -void TypedUrlChangeProcessor::StartImpl() { - DCHECK(history_backend_); - DCHECK(backend_thread_); - DCHECK(ui_thread_->BelongsToCurrentThread()); - backend_thread_->PostTask(FROM_HERE, - base::Bind(&TypedUrlChangeProcessor::StartObserving, - base::Unretained(this))); -} - -void TypedUrlChangeProcessor::StartObserving() { - DCHECK(backend_thread_->BelongsToCurrentThread()); - DCHECK(history_backend_); - history_backend_observer_.Add(history_backend_); -} - -void TypedUrlChangeProcessor::StopObserving() { - DCHECK(backend_thread_->BelongsToCurrentThread()); - DCHECK(history_backend_); - history_backend_observer_.RemoveAll(); -} - -} // namespace browser_sync diff --git a/components/history/core/browser/typed_url_change_processor.h b/components/history/core/browser/typed_url_change_processor.h deleted file mode 100644 index e397d30..0000000 --- a/components/history/core/browser/typed_url_change_processor.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_CHANGE_PROCESSOR_H_ -#define COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_CHANGE_PROCESSOR_H_ - -#include "components/sync_driver/change_processor.h" - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/scoped_observer.h" -#include "components/history/core/browser/history_backend_observer.h" -#include "components/history/core/browser/typed_url_model_associator.h" -#include "components/sync_driver/data_type_error_handler.h" - -namespace base { -class MessageLoop; -} - -namespace history { -class HistoryBackend; -class URLRow; -}; - -namespace browser_sync { - -class DataTypeErrorHandler; - -// This class is responsible for taking changes from the history backend and -// applying them to the sync API 'syncable' model, and vice versa. All -// operations and use of this class are from the UI thread. -class TypedUrlChangeProcessor : public sync_driver::ChangeProcessor, - public history::HistoryBackendObserver { - public: - TypedUrlChangeProcessor( - TypedUrlModelAssociator* model_associator, - history::HistoryBackend* history_backend, - sync_driver::DataTypeErrorHandler* error_handler, - const scoped_refptr<base::SingleThreadTaskRunner> ui_thread); - ~TypedUrlChangeProcessor() override; - - // sync API model -> WebDataService change application. - void ApplyChangesFromSyncModel( - const syncer::BaseTransaction* trans, - int64 model_version, - const syncer::ImmutableChangeRecordList& changes) override; - - // Commit changes here, after we've released the transaction lock to avoid - // jank. - void CommitChangesFromSyncModel() override; - - // Stop processing changes and wait for being destroyed. - void Disconnect(); - - protected: - void StartImpl() override; - - private: - friend class ScopedStopObserving<TypedUrlChangeProcessor>; - void StartObserving(); - void StopObserving(); - - // history::HistoryBackendObserver: - void OnURLVisited(history::HistoryBackend* history_backend, - ui::PageTransition transition, - const history::URLRow& row, - const history::RedirectList& redirects, - base::Time visit_time) override; - void OnURLsModified(history::HistoryBackend* history_backend, - const history::URLRows& changed_urls) override; - void OnURLsDeleted(history::HistoryBackend* history_backend, - bool all_history, - bool expired, - const history::URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override; - - // 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); - - // This struct is used to return result from FixupURLAndGetVisitsToSync - // function below. - struct VisitsToSync { - VisitsToSync(); - ~VisitsToSync(); - - history::URLRow row; - history::VisitVector visits; - }; - - // Utility function that fixes the URL and retrieves visits to be used to - // update sync node for the given |url|. Returns false if the data can't be - // retrieved from History backend or if there are no typed visits to sync. - bool FixupURLAndGetVisitsToSync(const history::URLRow& row, - VisitsToSync* visits_to_sync); - - // Utility routine that either updates an existing sync node or creates a - // new one for the passed |typed_url| if one does not already exist. Returns - // false and sets an unrecoverable error if the operation failed. - void CreateOrUpdateSyncNode(syncer::WriteTransaction* transaction, - const history::URLRow& typed_url, - const history::VisitVector& visit_vector); - - // The two models should be associated according to this ModelAssociator. - TypedUrlModelAssociator* model_associator_; - - // The model we are processing changes from. This is owned by the - // WebDataService which is kept alive by our data type controller - // holding a reference. - history::HistoryBackend* history_backend_; - scoped_refptr<base::SingleThreadTaskRunner> ui_thread_; - scoped_refptr<base::SingleThreadTaskRunner> backend_thread_; - - // The set of pending changes that will be written out on the next - // CommitChangesFromSyncModel() call. - history::URLRows pending_new_urls_; - history::URLRows pending_updated_urls_; - std::vector<GURL> pending_deleted_urls_; - TypedUrlModelAssociator::TypedUrlVisitVector pending_new_visits_; - history::VisitVector pending_deleted_visits_; - - bool disconnected_; - base::Lock disconnect_lock_; - - ScopedObserver<history::HistoryBackend, history::HistoryBackendObserver> - history_backend_observer_; - - DISALLOW_COPY_AND_ASSIGN(TypedUrlChangeProcessor); -}; - -} // namespace browser_sync - -#endif // COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_CHANGE_PROCESSOR_H_ diff --git a/components/history/core/browser/typed_url_data_type_controller.cc b/components/history/core/browser/typed_url_data_type_controller.cc index 717fbef..462569f 100644 --- a/components/history/core/browser/typed_url_data_type_controller.cc +++ b/components/history/core/browser/typed_url_data_type_controller.cc @@ -10,7 +10,6 @@ #include "base/prefs/pref_service.h" #include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service.h" -#include "components/history/core/browser/typed_url_change_processor.h" #include "components/sync_driver/sync_client.h" namespace browser_sync { @@ -57,9 +56,10 @@ TypedUrlDataTypeController::TypedUrlDataTypeController( const base::Closure& error_callback, sync_driver::SyncClient* sync_client, const char* history_disabled_pref_name) - : NonFrontendDataTypeController(ui_thread, error_callback, sync_client), + : NonUIDataTypeController(ui_thread, error_callback, sync_client), history_disabled_pref_name_(history_disabled_pref_name), - backend_(NULL) { + backend_(NULL), + sync_client_(sync_client) { pref_registrar_.Init(sync_client->GetPrefService()); pref_registrar_.Add( history_disabled_pref_name_, @@ -78,7 +78,7 @@ syncer::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() const { bool TypedUrlDataTypeController::ReadyForStart() const { DCHECK(ui_thread()->BelongsToCurrentThread()); - return !sync_client()->GetPrefService()->GetBoolean( + return !sync_client_->GetPrefService()->GetBoolean( history_disabled_pref_name_); } @@ -89,16 +89,18 @@ void TypedUrlDataTypeController::SetBackend(history::HistoryBackend* backend) { void TypedUrlDataTypeController::OnSavingBrowserHistoryDisabledChanged() { DCHECK(ui_thread()->BelongsToCurrentThread()); - if (sync_client()->GetPrefService()->GetBoolean( - history_disabled_pref_name_)) { + if (sync_client_->GetPrefService()->GetBoolean(history_disabled_pref_name_)) { // We've turned off history persistence, so if we are running, // generate an unrecoverable error. This can be fixed by restarting // Chrome (on restart, typed urls will not be a registered type). if (state() != NOT_RUNNING && state() != STOPPING) { - syncer::SyncError error( - FROM_HERE, syncer::SyncError::DATATYPE_POLICY_ERROR, - "History saving is now disabled by policy.", syncer::TYPED_URLS); - DisableImpl(error); + PostTaskOnBackendThread( + FROM_HERE, + base::Bind(&DataTypeController::OnSingleDataTypeUnrecoverableError, + this, + syncer::SyncError( + FROM_HERE, syncer::SyncError::DATATYPE_POLICY_ERROR, + "History saving is now disabled by policy.", type()))); } } } @@ -107,7 +109,7 @@ bool TypedUrlDataTypeController::PostTaskOnBackendThread( const tracked_objects::Location& from_here, const base::Closure& task) { DCHECK(ui_thread()->BelongsToCurrentThread()); - history::HistoryService* history = sync_client()->GetHistoryService(); + history::HistoryService* history = sync_client_->GetHistoryService(); if (history) { history->ScheduleDBTask(scoped_ptr<history::HistoryDBTask>( new RunTaskOnHistoryThread(task, this)), @@ -120,22 +122,6 @@ bool TypedUrlDataTypeController::PostTaskOnBackendThread( } } -sync_driver::SyncApiComponentFactory::SyncComponents -TypedUrlDataTypeController::CreateSyncComponents() { - DCHECK(!ui_thread()->BelongsToCurrentThread()); - DCHECK_EQ(state(), ASSOCIATING); - DCHECK(backend_); - return sync_client() - ->GetSyncApiComponentFactory() - ->CreateTypedUrlSyncComponents(sync_client()->GetSyncService(), backend_, - this); -} - -void TypedUrlDataTypeController::DisconnectProcessor( - sync_driver::ChangeProcessor* processor) { - static_cast<TypedUrlChangeProcessor*>(processor)->Disconnect(); -} - TypedUrlDataTypeController::~TypedUrlDataTypeController() {} } // namespace browser_sync diff --git a/components/history/core/browser/typed_url_data_type_controller.h b/components/history/core/browser/typed_url_data_type_controller.h index ab0e46b..58c4e8d 100644 --- a/components/history/core/browser/typed_url_data_type_controller.h +++ b/components/history/core/browser/typed_url_data_type_controller.h @@ -12,7 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/prefs/pref_change_registrar.h" #include "base/task/cancelable_task_tracker.h" -#include "components/sync_driver/non_frontend_data_type_controller.h" +#include "components/sync_driver/non_ui_data_type_controller.h" #include "components/sync_driver/sync_api_component_factory.h" namespace history { @@ -24,7 +24,7 @@ namespace browser_sync { class ControlTask; // A class that manages the startup and shutdown of typed_url sync. -class TypedUrlDataTypeController : public NonFrontendDataTypeController { +class TypedUrlDataTypeController : public sync_driver::NonUIDataTypeController { public: explicit TypedUrlDataTypeController( const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, @@ -32,7 +32,7 @@ class TypedUrlDataTypeController : public NonFrontendDataTypeController { sync_driver::SyncClient* sync_client, const char* history_disabled_pref_name); - // NonFrontendDataTypeController implementation + // NonUIDataTypeController implementation syncer::ModelType type() const override; syncer::ModelSafeGroup model_safe_group() const override; bool ReadyForStart() const override; @@ -42,12 +42,9 @@ class TypedUrlDataTypeController : public NonFrontendDataTypeController { void SetBackend(history::HistoryBackend* backend); protected: - // NonFrontendDataTypeController interface. + // NonUIDataTypeController interface. bool PostTaskOnBackendThread(const tracked_objects::Location& from_here, const base::Closure& task) override; - sync_driver::SyncApiComponentFactory::SyncComponents CreateSyncComponents() - override; - void DisconnectProcessor(sync_driver::ChangeProcessor* processor) override; private: ~TypedUrlDataTypeController() override; @@ -64,6 +61,8 @@ class TypedUrlDataTypeController : public NonFrontendDataTypeController { // thread. base::CancelableTaskTracker task_tracker_; + sync_driver::SyncClient* const sync_client_; + DISALLOW_COPY_AND_ASSIGN(TypedUrlDataTypeController); }; diff --git a/components/history/core/browser/typed_url_model_associator.cc b/components/history/core/browser/typed_url_model_associator.cc deleted file mode 100644 index 31a3979..0000000 --- a/components/history/core/browser/typed_url_model_associator.cc +++ /dev/null @@ -1,862 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/history/core/browser/typed_url_model_associator.h" - -#include <algorithm> -#include <set> - -#include "base/location.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/strings/utf_string_conversions.h" -#include "components/history/core/browser/history_backend.h" -#include "components/sync_driver/sync_service.h" -#include "net/base/net_util.h" -#include "sync/api/sync_error.h" -#include "sync/api/sync_merge_result.h" -#include "sync/internal_api/public/read_node.h" -#include "sync/internal_api/public/read_transaction.h" -#include "sync/internal_api/public/write_node.h" -#include "sync/internal_api/public/write_transaction.h" -#include "sync/protocol/typed_url_specifics.pb.h" - -namespace browser_sync { - -// 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; - -static bool CheckVisitOrdering(const history::VisitVector& visits) { - int64 previous_visit_time = 0; - for (history::VisitVector::const_iterator visit = visits.begin(); - visit != visits.end(); ++visit) { - if (visit != visits.begin()) { - // We allow duplicate visits here - they shouldn't really be allowed, but - // they still seem to show up sometimes and we haven't figured out the - // source, so we just log an error instead of failing an assertion. - // (http://crbug.com/91473). - if (previous_visit_time == visit->visit_time.ToInternalValue()) - DVLOG(1) << "Duplicate visit time encountered"; - else if (previous_visit_time > visit->visit_time.ToInternalValue()) - return false; - } - - previous_visit_time = visit->visit_time.ToInternalValue(); - } - return true; -} - -TypedUrlModelAssociator::TypedUrlModelAssociator( - sync_driver::SyncService* sync_service, - history::HistoryBackend* history_backend, - sync_driver::DataTypeErrorHandler* error_handler) - : sync_service_(sync_service), - history_backend_(history_backend), - expected_loop_(base::MessageLoop::current()), - abort_requested_(false), - error_handler_(error_handler), - num_db_accesses_(0), - num_db_errors_(0) { - DCHECK(sync_service_); - // history_backend_ may be null for unit tests (since it's not mockable). -} - -TypedUrlModelAssociator::~TypedUrlModelAssociator() {} - - -bool TypedUrlModelAssociator::FixupURLAndGetVisits( - history::URLRow* url, - history::VisitVector* visits) { - ++num_db_accesses_; - CHECK(history_backend_); - if (!history_backend_->GetMostRecentVisitsForURL( - url->id(), kMaxVisitsToFetch, visits)) { - ++num_db_errors_; - 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 false to stop further - // processing because sync expects valid visit time for modified entry. - return false; - } - - history::VisitRow visit( - url->id(), url->last_visit(), 0, ui::PAGE_TRANSITION_TYPED, 0); - visits->push_back(visit); - } - - // GetMostRecentVisitsForURL() returns the data in the opposite order that - // we need it, so reverse it. - std::reverse(visits->begin(), visits->end()); - - // 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(CheckVisitOrdering(*visits)); - return true; -} - -bool TypedUrlModelAssociator::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.host())) - return true; - - return false; -} - -bool TypedUrlModelAssociator::ShouldIgnoreVisits( - const history::VisitVector& visits) { - // We ignore URLs that were imported, but have never been visited by - // chromium. - static const int kLastImportedSource = history::SOURCE_EXTENSION; - history::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 (history::VisitVector::const_iterator it = visits.begin(); - it != visits.end(); ++it) { - if (map.count(it->visit_id) == 0 || - map[it->visit_id] <= kLastImportedSource) { - return false; - } - } - // We only saw imported visits, so tell the caller to ignore them. - return true; -} - -syncer::SyncError TypedUrlModelAssociator::AssociateModels( - syncer::SyncMergeResult* local_merge_result, - syncer::SyncMergeResult* syncer_merge_result) { - ClearErrorStats(); - syncer::SyncError error = - DoAssociateModels(local_merge_result, syncer_merge_result); - UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlModelAssociationErrors", - GetErrorPercentage()); - ClearErrorStats(); - return error; -} - -void TypedUrlModelAssociator::ClearErrorStats() { - num_db_accesses_ = 0; - num_db_errors_ = 0; -} - -int TypedUrlModelAssociator::GetErrorPercentage() const { - return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0; -} - -syncer::SyncError TypedUrlModelAssociator::DoAssociateModels( - syncer::SyncMergeResult* local_merge_result, - syncer::SyncMergeResult* syncer_merge_result) { - DVLOG(1) << "Associating TypedUrl Models"; - DCHECK(expected_loop_ == base::MessageLoop::current()); - - history::URLRows typed_urls; - ++num_db_accesses_; - bool query_succeeded = - history_backend_ && history_backend_->GetAllTypedURLs(&typed_urls); - - history::URLRows new_urls; - history::URLRows updated_urls; - TypedUrlVisitVector new_visits; - { - base::AutoLock au(abort_lock_); - if (abort_requested_) { - return syncer::SyncError(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Association was aborted.", - model_type()); - } - - // Must lock and check first to make sure |error_handler_| is valid. - if (!query_succeeded) { - ++num_db_errors_; - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Could not get the typed_url entries.", - model_type()); - } - local_merge_result->set_num_items_before_association(typed_urls.size()); - - // Get all the visits. - std::map<history::URLID, history::VisitVector> visit_vectors; - history::URLRows::iterator new_end = typed_urls.end(); - for (history::URLRows::iterator ix = typed_urls.begin(); - ix != new_end;) { - DCHECK_EQ(0U, visit_vectors.count(ix->id())); - if (!FixupURLAndGetVisits(&(*ix), &(visit_vectors[ix->id()])) || - ShouldIgnoreUrl(ix->url()) || - ShouldIgnoreVisits(visit_vectors[ix->id()])) { - // 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). - --new_end; - if (ix != new_end) - *ix = *new_end; - } else { - ++ix; - } - } - typed_urls.erase(new_end, typed_urls.end()); - - syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode typed_url_root(&trans); - if (typed_url_root.InitTypeRoot(syncer::TYPED_URLS) != - syncer::BaseNode::INIT_OK) { - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Server did not create the top-level typed_url node. We " - "might be running against an out-of-date server.", - model_type()); - } - syncer_merge_result->set_num_items_before_association( - typed_url_root.GetTotalNodeCount()); - - std::set<std::string> current_urls; - for (history::URLRows::iterator ix = typed_urls.begin(); - ix != typed_urls.end(); ++ix) { - std::string tag = ix->url().spec(); - // Empty URLs should be filtered out by ShouldIgnoreUrl() previously. - DCHECK(!tag.empty()); - history::VisitVector& visits = visit_vectors[ix->id()]; - - syncer::ReadNode node(&trans); - if (node.InitByClientTagLookup(syncer::TYPED_URLS, tag) == - syncer::BaseNode::INIT_OK) { - // Same URL exists in sync data and in history data - compare the - // entries to see if there's any difference. - sync_pb::TypedUrlSpecifics typed_url( - FilterExpiredVisits(node.GetTypedUrlSpecifics())); - DCHECK_EQ(tag, typed_url.url()); - - // 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 below in WriteToHistoryBackend(), but - // some of the values in that structure are not synced (like - // typed_count). - history::URLRow new_url(*ix); - - std::vector<history::VisitInfo> added_visits; - MergeResult difference = - MergeUrls(typed_url, *ix, &visits, &new_url, &added_visits); - if (difference & DIFF_UPDATE_NODE) { - syncer::WriteNode write_node(&trans); - if (write_node.InitByClientTagLookup(syncer::TYPED_URLS, tag) != - syncer::BaseNode::INIT_OK) { - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Failed to edit typed_url sync node.", - model_type()); - } - // 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. - if (typed_url.visits_size() > 0) { - base::Time earliest_visit = - base::Time::FromInternalValue(typed_url.visits(0)); - for (history::VisitVector::iterator it = visits.begin(); - it != visits.end() && it->visit_time < earliest_visit; ) { - it = visits.erase(it); - } - // Should never be possible to delete all the items, since the - // visit vector contains all the items in typed_url.visits. - DCHECK_GT(visits.size(), 0u); - } - DCHECK_EQ(new_url.last_visit().ToInternalValue(), - visits.back().visit_time.ToInternalValue()); - WriteToSyncNode(new_url, visits, &write_node); - syncer_merge_result->set_num_items_modified( - syncer_merge_result->num_items_modified() + 1); - } - if (difference & DIFF_LOCAL_ROW_CHANGED) { - DCHECK_EQ(ix->id(), new_url.id()); - updated_urls.push_back(new_url); - } - if (difference & DIFF_LOCAL_VISITS_ADDED) { - new_visits.push_back( - std::pair<GURL, std::vector<history::VisitInfo> >(ix->url(), - added_visits)); - } - } else { - // Sync has never seen this URL before. - syncer::WriteNode node(&trans); - syncer::WriteNode::InitUniqueByCreationResult result = - node.InitUniqueByCreation(syncer::TYPED_URLS, tag); - if (result != syncer::WriteNode::INIT_SUCCESS) { - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Failed to create typed_url sync node: " + tag, - model_type()); - } - - node.SetTitle(tag); - WriteToSyncNode(*ix, visits, &node); - syncer_merge_result->set_num_items_added( - syncer_merge_result->num_items_added() + 1); - } - - current_urls.insert(tag); - } - - // Now walk the sync nodes and detect any URLs that exist there, but not in - // the history DB, so we can add them to our local history DB. - std::vector<int64> obsolete_nodes; - std::vector<int64> sync_ids; - typed_url_root.GetChildIds(&sync_ids); - - for (std::vector<int64>::const_iterator it = sync_ids.begin(); - it != sync_ids.end(); ++it) { - syncer::ReadNode sync_child_node(&trans); - if (sync_child_node.InitByIdLookup(*it) != syncer::BaseNode::INIT_OK) { - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Failed to fetch child node.", - model_type()); - } - const sync_pb::TypedUrlSpecifics& typed_url( - sync_child_node.GetTypedUrlSpecifics()); - - // Ignore old sync nodes that don't have any transition data stored with - // them, or transition data that does not match the visit data (will be - // deleted below). - if (typed_url.visit_transitions_size() == 0 || - typed_url.visit_transitions_size() != typed_url.visits_size()) { - // Generate a debug assertion to help track down http://crbug.com/91473, - // even though we gracefully handle this case by throwing away this - // node. - DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size()); - DVLOG(1) << "Deleting obsolete sync node with no visit " - << "transition info."; - obsolete_nodes.push_back(sync_child_node.GetId()); - continue; - } - - if (typed_url.url().empty()) { - DVLOG(1) << "Ignoring empty URL in sync DB"; - continue; - } - - // Now, get rid of the expired visits, and if there are no un-expired - // visits left, just ignore this node. - sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(typed_url); - if (filtered_url.visits_size() == 0) { - DVLOG(1) << "Ignoring expired URL in sync DB: " << filtered_url.url(); - continue; - } - - if (current_urls.find(filtered_url.url()) == current_urls.end()) { - // Update the local DB from the sync DB. Since we are doing our - // initial model association, we don't want to remove any of the - // existing visits (pass NULL as |visits_to_remove|). - UpdateFromSyncDB(filtered_url, - &new_visits, - NULL, - &updated_urls, - &new_urls); - local_merge_result->set_num_items_added( - local_merge_result->num_items_added() + 1); - } - } - - // If we encountered any obsolete nodes, remove them so they don't hang - // around and confuse people looking at the sync node browser. - if (!obsolete_nodes.empty()) { - for (std::vector<int64>::const_iterator it = obsolete_nodes.begin(); - it != obsolete_nodes.end(); - ++it) { - syncer::WriteNode sync_node(&trans); - if (sync_node.InitByIdLookup(*it) != syncer::BaseNode::INIT_OK) { - return error_handler_->CreateAndUploadError( - FROM_HERE, - "Failed to fetch obsolete node.", - model_type()); - } - sync_node.Tombstone(); - } - } - syncer_merge_result->set_num_items_after_association( - typed_url_root.GetTotalNodeCount()); - } - - // Since we're on the history thread, we don't have to worry about updating - // the history database after closing the write transaction, since - // this is the only thread that writes to the database. We also don't have - // to worry about the sync model getting out of sync, because changes are - // propagated to the ChangeProcessor on this thread. - WriteToHistoryBackend(&new_urls, &updated_urls, &new_visits, NULL); - local_merge_result->set_num_items_modified(updated_urls.size()); - local_merge_result->set_num_items_after_association( - local_merge_result->num_items_before_association() + - local_merge_result->num_items_added()); - return syncer::SyncError(); -} - -void TypedUrlModelAssociator::UpdateFromSyncDB( - const sync_pb::TypedUrlSpecifics& typed_url, - TypedUrlVisitVector* visits_to_add, - history::VisitVector* visits_to_remove, - history::URLRows* updated_urls, - history::URLRows* new_urls) { - history::URLRow new_url(GURL(typed_url.url())); - history::VisitVector 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. - if (!FixupURLAndGetVisits(&new_url, &existing_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: " << new_url.url(); - return; - } - } - visits_to_add->push_back(std::pair<GURL, std::vector<history::VisitInfo> >( - new_url.url(), std::vector<history::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().second, - visits_to_remove); - - if (existing_url) { - updated_urls->push_back(new_url); - } else { - new_urls->push_back(new_url); - } -} - -sync_pb::TypedUrlSpecifics TypedUrlModelAssociator::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 TypedUrlModelAssociator::DeleteAllNodes( - syncer::WriteTransaction* trans) { - DCHECK(expected_loop_ == base::MessageLoop::current()); - - // Just walk through all our child nodes and delete them. - syncer::ReadNode typed_url_root(trans); - if (typed_url_root.InitTypeRoot(syncer::TYPED_URLS) != - syncer::BaseNode::INIT_OK) { - LOG(ERROR) << "Could not lookup root node"; - return false; - } - - std::vector<int64> sync_ids; - typed_url_root.GetChildIds(&sync_ids); - - for (std::vector<int64>::const_iterator it = sync_ids.begin(); - it != sync_ids.end(); ++it) { - syncer::WriteNode sync_child_node(trans); - if (sync_child_node.InitByIdLookup(*it) != syncer::BaseNode::INIT_OK) { - LOG(ERROR) << "Typed url node lookup failed."; - return false; - } - sync_child_node.Tombstone(); - } - return true; -} - -syncer::SyncError TypedUrlModelAssociator::DisassociateModels() { - return syncer::SyncError(); -} - -void TypedUrlModelAssociator::AbortAssociation() { - base::AutoLock lock(abort_lock_); - abort_requested_ = true; -} - -bool TypedUrlModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { - DCHECK(has_nodes); - *has_nodes = false; - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode sync_node(&trans); - if (sync_node.InitTypeRoot(syncer::TYPED_URLS) != syncer::BaseNode::INIT_OK) { - LOG(ERROR) << "Server did not create the top-level typed_url node. We " - << "might be running against an out-of-date server."; - return false; - } - - // The sync model has user created nodes if the typed_url folder has any - // children. - *has_nodes = sync_node.HasChildren(); - return true; -} - -void TypedUrlModelAssociator::WriteToHistoryBackend( - const history::URLRows* new_urls, - const history::URLRows* updated_urls, - const TypedUrlVisitVector* new_visits, - const history::VisitVector* deleted_visits) { - if (new_urls) { - history_backend_->AddPagesWithDetails(*new_urls, history::SOURCE_SYNCED); - } - if (updated_urls) { - ++num_db_accesses_; - // These are existing entries 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, as we can try writing again on the next model association. - size_t num_successful_updates = history_backend_->UpdateURLs(*updated_urls); - num_db_errors_ += updated_urls->size() - num_successful_updates; - } - if (new_visits) { - for (TypedUrlVisitVector::const_iterator visits = new_visits->begin(); - visits != new_visits->end(); ++visits) { - // If there are no visits to add, just skip this. - if (visits->second.empty()) - continue; - ++num_db_accesses_; - if (!history_backend_->AddVisits(visits->first, visits->second, - history::SOURCE_SYNCED)) { - ++num_db_errors_; - DLOG(ERROR) << "Could not add visits."; - } - } - } - if (deleted_visits) { - ++num_db_accesses_; - if (!history_backend_->RemoveVisits(*deleted_visits)) { - ++num_db_errors_; - DLOG(ERROR) << "Could not remove visits."; - // 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. - } - } -} - -// static -TypedUrlModelAssociator::MergeResult TypedUrlModelAssociator::MergeUrls( - const sync_pb::TypedUrlSpecifics& node, - const history::URLRow& url, - history::VisitVector* visits, - history::URLRow* new_url, - std::vector<history::VisitInfo>* new_visits) { - DCHECK(new_url); - DCHECK(!node.url().compare(url.url().spec())); - DCHECK(!node.url().compare(new_url->url().spec())); - DCHECK(visits->size()); - CHECK_EQ(node.visits_size(), node.visit_transitions_size()); - - // If we have an old-format node (before we added the visits and - // visit_transitions arrays to the protobuf) or else the node only contained - // expired visits, so just overwrite it with our local history data. - if (node.visits_size() == 0) - return DIFF_UPDATE_NODE; - - // Convert these values only once. - base::string16 node_title(base::UTF8ToUTF16(node.title())); - base::Time node_last_visit = base::Time::FromInternalValue( - node.visits(node.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 ((node_title.compare(url.title()) != 0) || - (node.hidden() != url.hidden())) { - // Use the values from the most recent visit. - if (node_last_visit >= url.last_visit()) { - new_url->set_title(node_title); - new_url->set_hidden(node.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 node_num_visits = node.visits_size(); - size_t history_num_visits = visits->size(); - size_t node_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 (node_visit_index < node_num_visits || - history_visit_index < history_num_visits) { - // Time objects are initialized to "earliest possible time". - base::Time node_time, history_time; - if (node_visit_index < node_num_visits) - node_time = base::Time::FromInternalValue(node.visits(node_visit_index)); - if (history_visit_index < history_num_visits) - history_time = (*visits)[history_visit_index].visit_time; - if (node_visit_index >= node_num_visits || - (history_visit_index < history_num_visits && - node_time > history_time)) { - // We found a visit in the history DB that doesn't exist in the sync DB, - // so mark the node 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 || - node_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 node 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. - if (node_time > earliest_history_time) { - different |= DIFF_LOCAL_VISITS_ADDED; - new_visits->push_back(history::VisitInfo( - node_time, - ui::PageTransitionFromInt( - node.visit_transitions(node_visit_index)))); - } - // This visit is added to visits below. - ++node_visit_index; - } else { - // Same (already synced) entry found in both DBs - no need to do anything. - ++node_visit_index; - ++history_visit_index; - } - } - - DCHECK(CheckVisitOrdering(*visits)); - if (different & DIFF_LOCAL_VISITS_ADDED) { - // Insert new visits into the apropriate place in the visits vector. - history::VisitVector::iterator visit_ix = visits->begin(); - for (std::vector<history::VisitInfo>::iterator new_visit = - new_visits->begin(); - new_visit != new_visits->end(); ++new_visit) { - while (visit_ix != visits->end() && - new_visit->first > visit_ix->visit_time) { - ++visit_ix; - } - visit_ix = visits->insert(visit_ix, - history::VisitRow(url.id(), new_visit->first, - 0, new_visit->second, 0)); - ++visit_ix; - } - } - DCHECK(CheckVisitOrdering(*visits)); - - new_url->set_last_visit(visits->back().visit_time); - return different; -} - -// static -void TypedUrlModelAssociator::WriteToSyncNode( - const history::URLRow& url, - const history::VisitVector& visits, - syncer::WriteNode* node) { - sync_pb::TypedUrlSpecifics typed_url; - WriteToTypedUrlSpecifics(url, visits, &typed_url); - node->SetTypedUrlSpecifics(typed_url); -} - -void TypedUrlModelAssociator::WriteToTypedUrlSpecifics( - const history::URLRow& url, - const history::VisitVector& 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(CheckVisitOrdering(visits)); - - bool only_typed = false; - int skip_count = 0; - - 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 (history::VisitVector::const_iterator visit = visits.begin(); - visit != visits.end(); ++visit) { - ui::PageTransition transition = - ui::PageTransitionStripQualifier(visit->transition); - // We ignore reload visits. - if (transition == ui::PAGE_TRANSITION_RELOAD) - continue; - ++total; - if (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 (history::VisitVector::const_iterator visit = visits.begin(); - visit != visits.end(); ++visit) { - ui::PageTransition transition = - ui::PageTransitionStripQualifier(visit->transition); - // Skip reload visits. - if (transition == ui::PAGE_TRANSITION_RELOAD) - continue; - - // If we only have room for typed visits, then only add typed visits. - if (only_typed && 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 || 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); - - if (typed_url->visits_size() == 0) { - // If we get here, it's because we don't actually have any TYPED visits - // even though the visit's typed_count > 0 (corrupted typed_count). So - // let's go ahead and add a RELOAD visit at the most recent visit since - // it's not legal to have an empty visit array (yet another workaround - // for http://crbug.com/84258). - typed_url->add_visits(url.last_visit().ToInternalValue()); - typed_url->add_visit_transitions(ui::PAGE_TRANSITION_RELOAD); - } - 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()); -} - -// static -void TypedUrlModelAssociator::DiffVisits( - const history::VisitVector& old_visits, - const sync_pb::TypedUrlSpecifics& new_url, - std::vector<history::VisitInfo>* new_visits, - history::VisitVector* removed_visits) { - DCHECK(new_visits); - size_t old_visit_count = old_visits.size(); - size_t new_visit_count = new_url.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(new_url.visits(new_index)); - if (old_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(old_visits[old_index]); - } - ++old_index; - } else if (old_visits[old_index].visit_time > new_visit_time) { - new_visits->push_back(history::VisitInfo( - new_visit_time, - ui::PageTransitionFromInt( - new_url.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(old_visits[old_index]); - } - } - - for ( ; new_index < new_visit_count; ++new_index) { - new_visits->push_back(history::VisitInfo( - base::Time::FromInternalValue(new_url.visits(new_index)), - ui::PageTransitionFromInt(new_url.visit_transitions(new_index)))); - } -} - - -// static -void TypedUrlModelAssociator::UpdateURLRowFromTypedUrlSpecifics( - const sync_pb::TypedUrlSpecifics& typed_url, history::URLRow* new_url) { - DCHECK_GT(typed_url.visits_size(), 0); - CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size()); - 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))); - } -} - -bool TypedUrlModelAssociator::CryptoReadyIfNecessary() { - // We only access the cryptographer while holding a transaction. - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes(); - return !encrypted_types.Has(syncer::TYPED_URLS) || - sync_service_->IsCryptographerReady(&trans); -} - -} // namespace browser_sync diff --git a/components/history/core/browser/typed_url_model_associator.h b/components/history/core/browser/typed_url_model_associator.h deleted file mode 100644 index 35db2eb..0000000 --- a/components/history/core/browser/typed_url_model_associator.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_MODEL_ASSOCIATOR_H_ -#define COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_MODEL_ASSOCIATOR_H_ - -#include <map> -#include <string> -#include <utility> -#include <vector> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/strings/string16.h" -#include "components/history/core/browser/history_types.h" -#include "components/sync_driver/data_type_error_handler.h" -#include "components/sync_driver/model_associator.h" -#include "sync/protocol/typed_url_specifics.pb.h" - -class GURL; - -namespace base { -class MessageLoop; -} - -namespace history { -class HistoryBackend; -class URLRow; -}; - -namespace syncer { -class WriteNode; -class WriteTransaction; -}; - -namespace sync_driver { -class SyncService; -} - -namespace browser_sync { - -// Contains all model association related logic: -// * Algorithm to associate typed_url model and sync model. -// * Persisting model associations and loading them back. -// We do not check if we have local data before this run; we always -// merge and sync. -class TypedUrlModelAssociator : public sync_driver::AssociatorInterface { - public: - typedef std::vector<std::pair<GURL, std::vector<history::VisitInfo> > > - TypedUrlVisitVector; - - static syncer::ModelType model_type() { return syncer::TYPED_URLS; } - TypedUrlModelAssociator(sync_driver::SyncService* sync_service, - history::HistoryBackend* history_backend, - sync_driver::DataTypeErrorHandler* error_handler); - ~TypedUrlModelAssociator() override; - - // AssociatorInterface implementation. - // - // Iterates through the sync model looking for matched pairs of items. - syncer::SyncError AssociateModels( - syncer::SyncMergeResult* local_merge_result, - syncer::SyncMergeResult* syncer_merge_result) override; - - // Clears all associations. - syncer::SyncError DisassociateModels() override; - - // Called from the main thread, to abort the currently active model - // association (for example, if we are shutting down). - void AbortAssociation() override; - - // The has_nodes out param is true if the sync model has nodes other - // than the permanent tagged nodes. - bool SyncModelHasUserCreatedNodes(bool* has_nodes) override; - - bool CryptoReadyIfNecessary() override; - - // Delete all typed url nodes. - bool DeleteAllNodes(syncer::WriteTransaction* trans); - - void WriteToHistoryBackend(const history::URLRows* new_urls, - const history::URLRows* updated_urls, - const TypedUrlVisitVector* new_visits, - const history::VisitVector* deleted_visits); - - // 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 UpdateFromSyncDB(const sync_pb::TypedUrlSpecifics& typed_url, - TypedUrlVisitVector* visits_to_add, - history::VisitVector* visits_to_remove, - history::URLRows* updated_urls, - history::URLRows* new_urls); - - // 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); - - // Returns the percentage of DB accesses that have resulted in an error. - int GetErrorPercentage() const; - - // Bitfield returned from MergeUrls to specify the result of the merge. - typedef uint32 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 history::URLRow& url, - history::VisitVector* visits, - history::URLRow* new_url, - std::vector<history::VisitInfo>* new_visits); - static void WriteToSyncNode(const history::URLRow& url, - const history::VisitVector& visits, - syncer::WriteNode* node); - - // 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 history::VisitVector& old_visits, - const sync_pb::TypedUrlSpecifics& new_url, - std::vector<history::VisitInfo>* new_visits, - history::VisitVector* removed_visits); - - // Converts the passed URL information to a TypedUrlSpecifics structure for - // writing to the sync DB - static void WriteToTypedUrlSpecifics(const history::URLRow& url, - const history::VisitVector& visits, - sync_pb::TypedUrlSpecifics* specifics); - - // 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, - // etc) by modifying the passed |url| object and |visits| vector. - // Returns false if we could not fetch the visits for the passed URL, and - // tracks DB error statistics internally for reporting via UMA. - bool FixupURLAndGetVisits(history::URLRow* url, - history::VisitVector* visits); - - // Updates the passed |url_row| based on the values in |specifics|. Fields - // that are not contained in |specifics| (such as typed_count) are left - // unchanged. - static void UpdateURLRowFromTypedUrlSpecifics( - const sync_pb::TypedUrlSpecifics& specifics, history::URLRow* url_row); - - // 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); - - protected: - // Helper function that clears our error counters (used to reset stats after - // model association so we can track model association errors separately). - // Overridden by tests. - virtual void ClearErrorStats(); - - private: - // Helper routine that actually does the work of associating models. - syncer::SyncError DoAssociateModels( - syncer::SyncMergeResult* local_merge_result, - syncer::SyncMergeResult* syncer_merge_result); - - // 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 history::VisitVector& visits); - - sync_driver::SyncService* sync_service_; - history::HistoryBackend* history_backend_; - - base::MessageLoop* expected_loop_; - - bool abort_requested_; - base::Lock abort_lock_; - - // Guaranteed to outlive datatypes. - sync_driver::DataTypeErrorHandler* error_handler_; - - // Statistics for the purposes of tracking the percentage of DB accesses that - // fail for each client via UMA. - int num_db_accesses_; - int num_db_errors_; - - DISALLOW_COPY_AND_ASSIGN(TypedUrlModelAssociator); -}; - -} // namespace browser_sync - -#endif // COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_MODEL_ASSOCIATOR_H_ diff --git a/components/history/core/browser/typed_url_model_associator_unittest.cc b/components/history/core/browser/typed_url_model_associator_unittest.cc deleted file mode 100644 index ab8fec4..0000000 --- a/components/history/core/browser/typed_url_model_associator_unittest.cc +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/test_timeouts.h" -#include "base/threading/thread.h" -#include "base/time/time.h" -#include "components/history/core/browser/history_types.h" -#include "components/history/core/browser/typed_url_model_associator.h" -#include "components/sync_driver/fake_sync_service.h" -#include "sync/protocol/typed_url_specifics.pb.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -using browser_sync::TypedUrlModelAssociator; - -namespace { -class SyncTypedUrlModelAssociatorTest : public testing::Test { - public: - static history::URLRow MakeTypedUrlRow(const char* url, - const char* title, - int typed_count, - int64 last_visit, - bool hidden, - history::VisitVector* visits) { - GURL gurl(url); - history::URLRow history_url(gurl); - history_url.set_title(base::UTF8ToUTF16(title)); - history_url.set_typed_count(typed_count); - history_url.set_last_visit( - base::Time::FromInternalValue(last_visit)); - history_url.set_hidden(hidden); - visits->push_back(history::VisitRow( - history_url.id(), history_url.last_visit(), 0, - ui::PAGE_TRANSITION_RELOAD, 0)); - history_url.set_visit_count(visits->size()); - return history_url; - } - - static sync_pb::TypedUrlSpecifics MakeTypedUrlSpecifics(const char* url, - const char* title, - int64 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 bool URLsEqual(history::URLRow& lhs, history::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()); - } -}; - -static void CreateModelAssociatorAsync(base::WaitableEvent* startup, - base::WaitableEvent* aborted, - base::WaitableEvent* done, - TypedUrlModelAssociator** associator, - sync_driver::SyncService* service) { - // Grab the done lock - when we exit, this will be released and allow the - // test to finish. - *associator = new TypedUrlModelAssociator(service, NULL, NULL); - - // Signal frontend to call AbortAssociation and proceed after it's called. - startup->Signal(); - aborted->Wait(); - syncer::SyncError error = (*associator)->AssociateModels(NULL, NULL); - EXPECT_TRUE(error.IsSet()); - EXPECT_EQ("Association was aborted.", error.message()); - delete *associator; - done->Signal(); -} - -} // namespace - -TEST_F(SyncTypedUrlModelAssociatorTest, MergeUrls) { - history::VisitVector visits1; - history::URLRow row1(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &visits1)); - sync_pb::TypedUrlSpecifics specs1(MakeTypedUrlSpecifics("http://pie.com/", - "pie", - 3, false)); - history::URLRow new_row1(GURL("http://pie.com/")); - std::vector<history::VisitInfo> new_visits1; - EXPECT_TRUE(TypedUrlModelAssociator::MergeUrls(specs1, row1, &visits1, - &new_row1, &new_visits1) == TypedUrlModelAssociator::DIFF_NONE); - - history::VisitVector visits2; - history::URLRow row2(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &visits2)); - sync_pb::TypedUrlSpecifics specs2(MakeTypedUrlSpecifics("http://pie.com/", - "pie", - 3, true)); - history::VisitVector expected_visits2; - history::URLRow expected2(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, true, &expected_visits2)); - history::URLRow new_row2(GURL("http://pie.com/")); - std::vector<history::VisitInfo> new_visits2; - EXPECT_TRUE(TypedUrlModelAssociator::MergeUrls(specs2, row2, &visits2, - &new_row2, &new_visits2) == - TypedUrlModelAssociator::DIFF_LOCAL_ROW_CHANGED); - EXPECT_TRUE(URLsEqual(new_row2, expected2)); - - history::VisitVector visits3; - history::URLRow row3(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &visits3)); - sync_pb::TypedUrlSpecifics specs3(MakeTypedUrlSpecifics("http://pie.com/", - "pie2", - 3, true)); - history::VisitVector expected_visits3; - history::URLRow expected3(MakeTypedUrlRow("http://pie.com/", "pie2", - 2, 3, true, &expected_visits3)); - history::URLRow new_row3(GURL("http://pie.com/")); - std::vector<history::VisitInfo> new_visits3; - EXPECT_EQ(TypedUrlModelAssociator::DIFF_LOCAL_ROW_CHANGED | - TypedUrlModelAssociator::DIFF_NONE, - TypedUrlModelAssociator::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). - history::VisitVector visits4; - history::URLRow row4(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &visits4)); - sync_pb::TypedUrlSpecifics specs4(MakeTypedUrlSpecifics("http://pie.com/", - "pie2", - 4, false)); - history::VisitVector expected_visits4; - history::URLRow expected4(MakeTypedUrlRow("http://pie.com/", "pie2", - 2, 4, false, &expected_visits4)); - history::URLRow new_row4(GURL("http://pie.com/")); - std::vector<history::VisitInfo> new_visits4; - EXPECT_EQ(TypedUrlModelAssociator::DIFF_UPDATE_NODE | - TypedUrlModelAssociator::DIFF_LOCAL_ROW_CHANGED | - TypedUrlModelAssociator::DIFF_LOCAL_VISITS_ADDED, - TypedUrlModelAssociator::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()); - - history::VisitVector visits5; - history::URLRow row5(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 4, false, &visits5)); - sync_pb::TypedUrlSpecifics specs5(MakeTypedUrlSpecifics("http://pie.com/", - "pie", - 3, false)); - history::VisitVector expected_visits5; - history::URLRow expected5(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &expected_visits5)); - history::URLRow new_row5(GURL("http://pie.com/")); - std::vector<history::VisitInfo> new_visits5; - - // UPDATE_NODE should be set because row5 has a newer last_visit timestamp. - EXPECT_EQ(TypedUrlModelAssociator::DIFF_UPDATE_NODE | - TypedUrlModelAssociator::DIFF_NONE, - TypedUrlModelAssociator::MergeUrls(specs5, row5, &visits5, - &new_row5, &new_visits5)); - EXPECT_TRUE(URLsEqual(new_row5, expected5)); - EXPECT_EQ(0U, new_visits5.size()); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, MergeUrlsAfterExpiration) { - // 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). - - // First, create a history row that has two visits, with timestamps 2 and 3. - history::VisitVector(history_visits); - history_visits.push_back(history::VisitRow( - 0, base::Time::FromInternalValue(2), 0, ui::PAGE_TRANSITION_TYPED, - 0)); - history::URLRow history_url(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 3, false, &history_visits)); - - // Now, create a sync node with visits at timestamps 1, 2, 3, 4. - sync_pb::TypedUrlSpecifics node(MakeTypedUrlSpecifics("http://pie.com/", - "pie", 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); - history::URLRow new_history_url(history_url.url()); - std::vector<history::VisitInfo> new_visits; - EXPECT_EQ(TypedUrlModelAssociator::DIFF_NONE | - TypedUrlModelAssociator::DIFF_LOCAL_VISITS_ADDED, - TypedUrlModelAssociator::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()); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, DiffVisitsSame) { - history::VisitVector old_visits; - sync_pb::TypedUrlSpecifics new_url; - - const int64 visits[] = { 1024, 2065, 65534, 1237684 }; - - for (size_t c = 0; c < arraysize(visits); ++c) { - old_visits.push_back(history::VisitRow( - 0, base::Time::FromInternalValue(visits[c]), 0, - ui::PAGE_TRANSITION_TYPED, 0)); - new_url.add_visits(visits[c]); - new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED); - } - - std::vector<history::VisitInfo> new_visits; - history::VisitVector removed_visits; - - TypedUrlModelAssociator::DiffVisits(old_visits, new_url, - &new_visits, &removed_visits); - EXPECT_TRUE(new_visits.empty()); - EXPECT_TRUE(removed_visits.empty()); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, DiffVisitsRemove) { - history::VisitVector old_visits; - sync_pb::TypedUrlSpecifics new_url; - - const int64 visits_left[] = { 1, 2, 1024, 1500, 2065, 6000, - 65534, 1237684, 2237684 }; - const int64 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 visits_removed[] = { 1500, 6000, 2237684 }; - - for (size_t c = 0; c < arraysize(visits_left); ++c) { - old_visits.push_back(history::VisitRow( - 0, base::Time::FromInternalValue(visits_left[c]), 0, - ui::PAGE_TRANSITION_TYPED, 0)); - } - - for (size_t c = 0; c < arraysize(visits_right); ++c) { - new_url.add_visits(visits_right[c]); - new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED); - } - - std::vector<history::VisitInfo> new_visits; - history::VisitVector removed_visits; - - TypedUrlModelAssociator::DiffVisits(old_visits, new_url, - &new_visits, &removed_visits); - EXPECT_TRUE(new_visits.empty()); - ASSERT_EQ(removed_visits.size(), arraysize(visits_removed)); - for (size_t c = 0; c < arraysize(visits_removed); ++c) { - EXPECT_EQ(removed_visits[c].visit_time.ToInternalValue(), - visits_removed[c]); - } -} - -TEST_F(SyncTypedUrlModelAssociatorTest, DiffVisitsAdd) { - history::VisitVector old_visits; - sync_pb::TypedUrlSpecifics new_url; - - const int64 visits_left[] = { 1024, 2065, 65534, 1237684 }; - const int64 visits_right[] = { 1, 1024, 1500, 2065, 6000, - 65534, 1237684, 2237684 }; - - const int64 visits_added[] = { 1, 1500, 6000, 2237684 }; - - for (size_t c = 0; c < arraysize(visits_left); ++c) { - old_visits.push_back(history::VisitRow( - 0, base::Time::FromInternalValue(visits_left[c]), 0, - ui::PAGE_TRANSITION_TYPED, 0)); - } - - for (size_t c = 0; c < arraysize(visits_right); ++c) { - new_url.add_visits(visits_right[c]); - new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED); - } - - std::vector<history::VisitInfo> new_visits; - history::VisitVector removed_visits; - - TypedUrlModelAssociator::DiffVisits(old_visits, new_url, - &new_visits, &removed_visits); - EXPECT_TRUE(removed_visits.empty()); - ASSERT_TRUE(new_visits.size() == arraysize(visits_added)); - for (size_t c = 0; c < arraysize(visits_added); ++c) { - EXPECT_EQ(new_visits[c].first.ToInternalValue(), - visits_added[c]); - EXPECT_EQ(new_visits[c].second, ui::PAGE_TRANSITION_TYPED); - } -} - -static history::VisitRow CreateVisit(ui::PageTransition type, - int64 timestamp) { - return history::VisitRow(0, base::Time::FromInternalValue(timestamp), 0, - type, 0); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, WriteTypedUrlSpecifics) { - history::VisitVector 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)); - - history::URLRow url(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 100, false, &visits)); - sync_pb::TypedUrlSpecifics typed_url; - TypedUrlModelAssociator::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(ui::PAGE_TRANSITION_TYPED, - ui::PageTransitionFromInt(typed_url.visit_transitions(0))); - EXPECT_EQ(ui::PAGE_TRANSITION_LINK, - ui::PageTransitionFromInt(typed_url.visit_transitions(1))); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, TooManyVisits) { - history::VisitVector visits; - int64 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++)); - } - history::URLRow url(MakeTypedUrlRow("http://pie.com/", "pie", - 1, timestamp++, false, &visits)); - sync_pb::TypedUrlSpecifics typed_url; - TypedUrlModelAssociator::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(ui::PAGE_TRANSITION_TYPED, - ui::PageTransitionFromInt(typed_url.visit_transitions(0))); - EXPECT_EQ(ui::PAGE_TRANSITION_LINK, - ui::PageTransitionFromInt(typed_url.visit_transitions(1))); -} - -TEST_F(SyncTypedUrlModelAssociatorTest, TooManyTypedVisits) { - history::VisitVector visits; - int64 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++)); - } - history::URLRow url(MakeTypedUrlRow("http://pie.com/", "pie", - 1, timestamp++, false, &visits)); - sync_pb::TypedUrlSpecifics typed_url; - TypedUrlModelAssociator::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(ui::PAGE_TRANSITION_TYPED, - ui::PageTransitionFromInt(typed_url.visit_transitions(i))); - } -} - -TEST_F(SyncTypedUrlModelAssociatorTest, NoTypedVisits) { - history::VisitVector visits; - history::URLRow url(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 1000, false, &visits)); - sync_pb::TypedUrlSpecifics typed_url; - TypedUrlModelAssociator::WriteToTypedUrlSpecifics(url, visits, &typed_url); - // URLs with no typed URL visits should be translated to a URL with one - // reload visit. - EXPECT_EQ(1, typed_url.visits_size()); - EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size()); - // First two typed visits should be skipped. - EXPECT_EQ(1000, typed_url.visits(0)); - EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD, - ui::PageTransitionFromInt(typed_url.visit_transitions(0))); -} - -// This test verifies that we can abort model association from the UI thread. -// We start up the model associator on the DB thread, block until we abort the -// association on the UI thread, then ensure that AssociateModels() returns -// false. -TEST_F(SyncTypedUrlModelAssociatorTest, TestAbort) { - base::Thread db_thread("DB_Thread"); - db_thread.Start(); - - base::WaitableEvent startup(false, false); - base::WaitableEvent aborted(false, false); - base::WaitableEvent done(false, false); - sync_driver::FakeSyncService service; - TypedUrlModelAssociator* associator(NULL); - // Fire off to the DB thread to create the model associator and start - // model association. - base::Closure callback = base::Bind( - &CreateModelAssociatorAsync, &startup, &aborted, &done, &associator, - &service); - db_thread.task_runner()->PostTask(FROM_HERE, callback); - // Wait for the model associator to get created and start assocation. - ASSERT_TRUE(startup.TimedWait(TestTimeouts::action_timeout())); - // Abort the model assocation - this should be callable from any thread. - associator->AbortAssociation(); - // Tell the remote thread to continue. - aborted.Signal(); - // Block until CreateModelAssociator() exits. - ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout())); - db_thread.Stop(); -} diff --git a/components/history/core/browser/typed_url_syncable_service.cc b/components/history/core/browser/typed_url_syncable_service.cc index 7fb5410..1aaaeec 100644 --- a/components/history/core/browser/typed_url_syncable_service.cc +++ b/components/history/core/browser/typed_url_syncable_service.cc @@ -61,7 +61,11 @@ static bool CheckVisitOrdering(const VisitVector& visits) { TypedUrlSyncableService::TypedUrlSyncableService( HistoryBackend* history_backend) - : history_backend_(history_backend), processing_syncer_changes_(false) { + : history_backend_(history_backend), + processing_syncer_changes_(false), + num_db_accesses_(0), + num_db_errors_(0), + history_backend_observer_(this) { DCHECK(history_backend_); DCHECK(thread_checker_.CalledOnValidThread()); } @@ -88,16 +92,6 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing( DVLOG(1) << "Associating TypedUrl: MergeDataAndStartSyncing"; - // Get all the typed urls from the history db. - history::URLRows typed_urls; - ++num_db_accesses_; - if (!history_backend_->GetAllTypedURLs(&typed_urls)) { - ++num_db_errors_; - sync_error_handler_->CreateAndUploadError( - FROM_HERE, "Could not get the typed_url entries."); - return merge_result; - } - // Create a mapping of all local data by URLID. These will be narrowed down // by CreateOrUpdateUrl() to include only the entries different from sync // server data. @@ -105,20 +99,33 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing( // Get all the visits and map the URLRows by URL. UrlVisitVectorMap visit_vectors; - for (history::URLRows::iterator iter = typed_urls.begin(); - iter != typed_urls.end();) { - DCHECK_EQ(0U, visit_vectors.count(iter->url())); - if (!FixupURLAndGetVisits(&(*iter), &(visit_vectors[iter->url()])) || - ShouldIgnoreUrl(iter->url()) || - ShouldIgnoreVisits(visit_vectors[iter->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). - iter = typed_urls.erase(iter); - } else { - // Add url to map. - new_db_urls[iter->url()] = - std::make_pair(syncer::SyncChange::ACTION_ADD, iter); - ++iter; + + { + // Get all the typed urls from the history db. + history::URLRows typed_urls; + ++num_db_accesses_; + if (!history_backend_->GetAllTypedURLs(&typed_urls)) { + ++num_db_errors_; + merge_result.set_error(sync_error_handler_->CreateAndUploadError( + FROM_HERE, "Could not get the typed_url entries.")); + return merge_result; + } + + for (history::URLRows::iterator iter = typed_urls.begin(); + iter != typed_urls.end();) { + DCHECK_EQ(0U, visit_vectors.count(iter->url())); + if (!FixupURLAndGetVisits(&(*iter), &(visit_vectors[iter->url()])) || + ShouldIgnoreUrl(iter->url()) || + ShouldIgnoreVisits(visit_vectors[iter->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). + iter = typed_urls.erase(iter); + } else { + // Add url to map. + new_db_urls[iter->url()] = + std::make_pair(syncer::SyncChange::ACTION_ADD, *iter); + ++iter; + } } } @@ -164,15 +171,14 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing( continue; } - CreateOrUpdateUrl(typed_url, &typed_urls, &new_db_urls, &visit_vectors, - &new_synced_urls, &new_synced_visits, - &updated_synced_urls); + CreateOrUpdateUrl(typed_url, &new_db_urls, &visit_vectors, &new_synced_urls, + &new_synced_visits, &updated_synced_urls); } for (TypedUrlMap::iterator i = new_db_urls.begin(); i != new_db_urls.end(); ++i) { std::string tag = i->first.spec(); - AddTypedUrlToChangeList(i->second.first, *(i->second.second), + AddTypedUrlToChangeList(i->second.first, i->second.second, visit_vectors[i->first], tag, &new_changes); // Add url to cache of sync state, if not already cached @@ -188,6 +194,8 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing( &new_synced_visits, NULL); } + history_backend_observer_.Add(history_backend_); + UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlMergeAndStartSyncingErrors", GetErrorPercentage()); ClearErrorStats(); @@ -202,6 +210,8 @@ void TypedUrlSyncableService::StopSyncing(syncer::ModelType type) { // Clear cache of server state. synced_typed_urls_.clear(); + history_backend_observer_.RemoveAll(); + ClearErrorStats(); sync_processor_.reset(); @@ -370,7 +380,6 @@ void TypedUrlSyncableService::OnURLsDeleted( void TypedUrlSyncableService::CreateOrUpdateUrl( const sync_pb::TypedUrlSpecifics& typed_url, - history::URLRows* typed_urls, TypedUrlMap* loaded_data, UrlVisitVectorMap* visit_vectors, history::URLRows* new_synced_urls, @@ -405,10 +414,6 @@ void TypedUrlSyncableService::CreateOrUpdateUrl( bool is_existing_url = history_backend_->GetURL(untyped_url.url(), &untyped_url); if (is_existing_url) { - // This URL already exists locally, but was not grabbed earlier - // because |typed_count| is 0 - DCHECK_EQ(untyped_url.typed_count(), 0); - // Add a new entry to |loaded_data|, and set the iterator to it. history::VisitVector untyped_visits; if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits)) { @@ -421,12 +426,9 @@ void TypedUrlSyncableService::CreateOrUpdateUrl( (*visit_vectors)[untyped_url.url()] = untyped_visits; // Store row info that will be used to update sync's visits. - history::URLRows::iterator ri = - typed_urls->insert(typed_urls->end(), untyped_url); (*loaded_data)[untyped_url.url()] = - std::pair<syncer::SyncChange::SyncChangeType, - history::URLRows::iterator>( - syncer::SyncChange::ACTION_UPDATE, ri); + std::pair<syncer::SyncChange::SyncChangeType, history::URLRow>( + syncer::SyncChange::ACTION_UPDATE, untyped_url); // Set iterator |it| to point to this entry. it = loaded_data->find(untyped_url.url()); @@ -463,21 +465,21 @@ void TypedUrlSyncableService::CreateOrUpdateUrl( std::vector<history::VisitInfo> added_visits; // Empty URLs should be filtered out by ShouldIgnoreUrl() previously. - DCHECK(!it->second.second->url().spec().empty()); + DCHECK(!it->second.second.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). - history::URLRow new_url(*(it->second.second)); + history::URLRow new_url(it->second.second); - MergeResult difference = MergeUrls(sync_url, *(it->second.second), &visits, - &new_url, &added_visits); + MergeResult difference = + MergeUrls(sync_url, it->second.second, &visits, &new_url, &added_visits); if (difference != DIFF_NONE) { + it->second.second = new_url; if (difference & DIFF_UPDATE_NODE) { // Edit map entry to reflect update to sync. - *(it->second.second) = new_url; it->second.first = syncer::SyncChange::ACTION_UPDATE; // 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 @@ -504,7 +506,7 @@ void TypedUrlSyncableService::CreateOrUpdateUrl( } if (difference & DIFF_LOCAL_ROW_CHANGED) { // Add entry to updated_synced_urls to update the local db. - DCHECK_EQ(it->second.second->id(), new_url.id()); + DCHECK_EQ(it->second.second.id(), new_url.id()); updated_synced_urls->push_back(new_url); } if (difference & DIFF_LOCAL_VISITS_ADDED) { @@ -525,23 +527,14 @@ sync_pb::TypedUrlSpecifics TypedUrlSyncableService::FilterExpiredVisits( sync_pb::TypedUrlSpecifics specifics(source); specifics.clear_visits(); specifics.clear_visit_transitions(); - int typed_count = 0; 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)); - if (source.visit_transitions(i) == ui::PAGE_TRANSITION_TYPED) - ++typed_count; } } DCHECK(specifics.visits_size() == specifics.visit_transitions_size()); - // Treat specifics with no non-expired typed visits as though they have no - // non-expired visits of any kind - if (typed_count == 0) { - specifics.clear_visits(); - specifics.clear_visit_transitions(); - } return specifics; } diff --git a/components/history/core/browser/typed_url_syncable_service.h b/components/history/core/browser/typed_url_syncable_service.h index ed7d492..da396dc 100644 --- a/components/history/core/browser/typed_url_syncable_service.h +++ b/components/history/core/browser/typed_url_syncable_service.h @@ -8,6 +8,7 @@ #include <set> #include <vector> +#include "base/scoped_observer.h" #include "base/threading/thread_checker.h" #include "components/history/core/browser/history_backend_observer.h" #include "components/history/core/browser/history_types.h" @@ -68,6 +69,15 @@ class TypedUrlSyncableService : public syncer::SyncableService, const history::URLRows& deleted_rows, const std::set<GURL>& favicon_urls) override; + // Returns the percentage of DB accesses that have resulted in an error. + int GetErrorPercentage() const; + + // Converts the passed URL information to a TypedUrlSpecifics structure for + // writing to the sync DB. + static void WriteToTypedUrlSpecifics(const URLRow& url, + const VisitVector& visits, + sync_pb::TypedUrlSpecifics* specifics); + private: friend class TypedUrlSyncableServiceTest; @@ -77,9 +87,8 @@ class TypedUrlSyncableService : public syncer::SyncableService, // This is a helper map used only in Merge/Process* functions. The lifetime // of the iterator is longer than the map object. - typedef std::map<GURL, - std::pair<syncer::SyncChange::SyncChangeType, - URLRows::iterator>> TypedUrlMap; + typedef std::map<GURL, std::pair<syncer::SyncChange::SyncChangeType, URLRow>> + TypedUrlMap; // This is a helper map used to associate visit vectors from the history db // to the typed urls in the above map. @@ -93,17 +102,17 @@ class TypedUrlSyncableService : public syncer::SyncableService, static const MergeResult DIFF_LOCAL_VISITS_ADDED = 1 << 2; // Helper method for getting the set of synced urls. + // Set it as virtual for testing. void GetSyncedUrls(std::set<GURL>* urls) const; // Helper function that clears our error counters (used to reset stats after // model association so we can track model association errors separately). - void ClearErrorStats(); + virtual void ClearErrorStats(); // Compares |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 CreateOrUpdateUrl(const sync_pb::TypedUrlSpecifics& typed_url, - history::URLRows* typed_urls, TypedUrlMap* loaded_data, UrlVisitVectorMap* visit_vectors, history::URLRows* new_synced_urls, @@ -138,9 +147,6 @@ class TypedUrlSyncableService : public syncer::SyncableService, const TypedUrlVisitVector* new_visits, const history::VisitVector* deleted_visits); - // Returns the percentage of DB accesses that have resulted in an error. - int GetErrorPercentage() const; - // 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); @@ -168,12 +174,6 @@ class TypedUrlSyncableService : public syncer::SyncableService, std::string title, syncer::SyncChangeList* change_list); - // Converts the passed URL information to a TypedUrlSpecifics structure for - // writing to the sync DB. - static void WriteToTypedUrlSpecifics(const URLRow& url, - const VisitVector& visits, - sync_pb::TypedUrlSpecifics* specifics); - // Fills |new_url| with formatted data from |typed_url|. static void UpdateURLRowFromTypedUrlSpecifics( const sync_pb::TypedUrlSpecifics& typed_url, @@ -234,6 +234,9 @@ class TypedUrlSyncableService : public syncer::SyncableService, base::ThreadChecker thread_checker_; + ScopedObserver<history::HistoryBackend, history::HistoryBackendObserver> + history_backend_observer_; + DISALLOW_COPY_AND_ASSIGN(TypedUrlSyncableService); }; diff --git a/components/history/core/browser/typed_url_syncable_service_unittest.cc b/components/history/core/browser/typed_url_syncable_service_unittest.cc index fdd7fce..a3405da 100644 --- a/components/history/core/browser/typed_url_syncable_service_unittest.cc +++ b/components/history/core/browser/typed_url_syncable_service_unittest.cc @@ -210,7 +210,7 @@ class TestHistoryBackend : public HistoryBackend { class TypedUrlSyncableServiceTest : public testing::Test { public: - TypedUrlSyncableServiceTest() {} + TypedUrlSyncableServiceTest() : typed_url_sync_service_(NULL) {} ~TypedUrlSyncableServiceTest() override {} void SetUp() override { @@ -219,8 +219,8 @@ class TypedUrlSyncableServiceTest : public testing::Test { fake_history_backend_->Init( std::string(), false, TestHistoryDatabaseParamsForPath(test_dir_.path())); - typed_url_sync_service_.reset( - new TypedUrlSyncableService(fake_history_backend_.get())); + typed_url_sync_service_ = + fake_history_backend_->GetTypedUrlSyncableService(); fake_change_processor_.reset(new syncer::FakeSyncChangeProcessor); } @@ -288,7 +288,7 @@ class TypedUrlSyncableServiceTest : public testing::Test { base::MessageLoop message_loop_; base::ScopedTempDir test_dir_; scoped_refptr<TestHistoryBackend> fake_history_backend_; - scoped_ptr<TypedUrlSyncableService> typed_url_sync_service_; + TypedUrlSyncableService* typed_url_sync_service_; scoped_ptr<syncer::FakeSyncChangeProcessor> fake_change_processor_; }; @@ -305,6 +305,7 @@ void TypedUrlSyncableServiceTest::StartSyncing( fake_change_processor_.get())), scoped_ptr<syncer::SyncErrorFactory>( new syncer::SyncErrorFactoryMock())); + typed_url_sync_service_->history_backend_observer_.RemoveAll(); EXPECT_FALSE(result.error().IsSet()) << result.error().message(); } @@ -316,7 +317,7 @@ bool TypedUrlSyncableServiceTest::BuildAndPushLocalChanges( std::vector<VisitVector>* visit_vectors) { unsigned int total_urls = num_typed_urls + num_reload_urls; DCHECK(urls.size() >= total_urls); - if (!typed_url_sync_service_.get()) + if (!typed_url_sync_service_) return false; if (total_urls) { |