// 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 CHROME_BROWSER_SYNC_GLUE_TYPED_URL_MODEL_ASSOCIATOR_H_ #define CHROME_BROWSER_SYNC_GLUE_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 "chrome/browser/history/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; class ProfileSyncService; namespace base { class MessageLoop; } namespace history { class HistoryBackend; class URLRow; }; namespace syncer { class WriteNode; class WriteTransaction; }; namespace browser_sync { extern const char kTypedUrlTag[]; // 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 AssociatorInterface { public: typedef std::vector<std::pair<history::URLID, history::URLRow> > TypedUrlUpdateVector; typedef std::vector<std::pair<GURL, std::vector<history::VisitInfo> > > TypedUrlVisitVector; static syncer::ModelType model_type() { return syncer::TYPED_URLS; } TypedUrlModelAssociator(ProfileSyncService* sync_service, history::HistoryBackend* history_backend, DataTypeErrorHandler* error_handler); virtual ~TypedUrlModelAssociator(); // AssociatorInterface implementation. // // Iterates through the sync model looking for matched pairs of items. virtual syncer::SyncError AssociateModels( syncer::SyncMergeResult* local_merge_result, syncer::SyncMergeResult* syncer_merge_result) OVERRIDE; // Clears all associations. virtual syncer::SyncError DisassociateModels() OVERRIDE; // Called from the main thread, to abort the currently active model // association (for example, if we are shutting down). virtual void AbortAssociation() OVERRIDE; // The has_nodes out param is true if the sync model has nodes other // than the permanent tagged nodes. virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes) OVERRIDE; virtual bool CryptoReadyIfNecessary() OVERRIDE; // Delete all typed url nodes. bool DeleteAllNodes(syncer::WriteTransaction* trans); void WriteToHistoryBackend(const history::URLRows* new_urls, const TypedUrlUpdateVector* 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, TypedUrlUpdateVector* 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(); // 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); ProfileSyncService* sync_service_; history::HistoryBackend* history_backend_; base::MessageLoop* expected_loop_; bool abort_requested_; base::Lock abort_lock_; DataTypeErrorHandler* error_handler_; // Guaranteed to outlive datatypes. // 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 // CHROME_BROWSER_SYNC_GLUE_TYPED_URL_MODEL_ASSOCIATOR_H_