[email protected] | 132c8565 | 2009-08-05 01:18:27 | [diff] [blame^] | 1 | // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifdef CHROME_PERSONALIZATION |
| 6 | |
| 7 | #ifndef CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_H_ |
| 8 | #define CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_H_ |
| 9 | |
| 10 | #include <string> |
| 11 | #include <map> |
| 12 | #include <vector> |
| 13 | |
| 14 | #include "base/basictypes.h" |
| 15 | #include "base/file_path.h" |
| 16 | #include "base/observer_list.h" |
| 17 | #include "base/scoped_ptr.h" |
| 18 | #include "chrome/browser/bookmarks/bookmark_model.h" |
| 19 | #include "chrome/browser/profile.h" |
| 20 | #include "chrome/browser/sync/glue/model_associator.h" |
| 21 | #include "chrome/browser/sync/glue/sync_backend_host.h" |
| 22 | #include "chrome/browser/views/sync/sync_setup_wizard.h" |
| 23 | #include "googleurl/src/gurl.h" |
| 24 | |
| 25 | class CommandLine; |
| 26 | class MessageLoop; |
| 27 | class Profile; |
| 28 | |
| 29 | namespace browser_sync { |
| 30 | class ModelAssociator; |
| 31 | } |
| 32 | |
| 33 | // Various UI components such as the New Tab page can be driven by observing |
| 34 | // the ProfileSyncService through this interface. |
| 35 | class ProfileSyncServiceObserver { |
| 36 | public: |
| 37 | // When one of the following events occurs, OnStateChanged() is called. |
| 38 | // Observers should query the service to determine what happened. |
| 39 | // - We initialized successfully. |
| 40 | // - There was an authentication error and the user needs to reauthenticate. |
| 41 | // - The sync servers are unavailable at this time. |
| 42 | // - Credentials are now in flight for authentication. |
| 43 | virtual void OnStateChanged() = 0; |
| 44 | protected: |
| 45 | virtual ~ProfileSyncServiceObserver() { } |
| 46 | }; |
| 47 | |
| 48 | // ProfileSyncService is the layer between browser subsystems like bookmarks, |
| 49 | // and the sync backend. |
| 50 | class ProfileSyncService : public BookmarkModelObserver, |
| 51 | public browser_sync::SyncFrontend { |
| 52 | public: |
| 53 | typedef ProfileSyncServiceObserver Observer; |
| 54 | typedef browser_sync::SyncBackendHost::Status Status; |
| 55 | |
| 56 | explicit ProfileSyncService(Profile* profile); |
| 57 | virtual ~ProfileSyncService(); |
| 58 | |
| 59 | // Initializes the object. This should be called every time an object of this |
| 60 | // class is constructed. |
| 61 | void Initialize(); |
| 62 | |
| 63 | // Enables/disables sync for user. |
| 64 | virtual void EnableForUser(); |
| 65 | virtual void DisableForUser(); |
| 66 | |
| 67 | // Whether sync is enabled by user or not. |
| 68 | bool IsSyncEnabledByUser() const; |
| 69 | |
| 70 | // BookmarkModelObserver implementation. |
| 71 | virtual void Loaded(BookmarkModel* model); |
| 72 | virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {} |
| 73 | virtual void BookmarkNodeMoved(BookmarkModel* model, |
| 74 | const BookmarkNode* old_parent, |
| 75 | int old_index, |
| 76 | const BookmarkNode* new_parent, |
| 77 | int new_index); |
| 78 | virtual void BookmarkNodeAdded(BookmarkModel* model, |
| 79 | const BookmarkNode* parent, |
| 80 | int index); |
| 81 | virtual void BookmarkNodeRemoved(BookmarkModel* model, |
| 82 | const BookmarkNode* parent, |
| 83 | int index, |
| 84 | const BookmarkNode* node); |
| 85 | virtual void BookmarkNodeChanged(BookmarkModel* model, |
| 86 | const BookmarkNode* node); |
| 87 | virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, |
| 88 | const BookmarkNode* node); |
| 89 | virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, |
| 90 | const BookmarkNode* node); |
| 91 | |
| 92 | // SyncFrontend implementation. |
| 93 | virtual void OnBackendInitialized(); |
| 94 | virtual void OnSyncCycleCompleted(); |
| 95 | virtual void OnAuthError(); |
| 96 | virtual void ApplyModelChanges( |
| 97 | const sync_api::BaseTransaction* trans, |
| 98 | const sync_api::SyncManager::ChangeRecord* changes, |
| 99 | int change_count); |
| 100 | |
| 101 | // Called when a user enters credentials through UI. |
| 102 | virtual void OnUserSubmittedAuth(const std::string& username, |
| 103 | const std::string& password); |
| 104 | |
| 105 | // Called when a user decides whether to merge and sync or abort. |
| 106 | virtual void OnUserAcceptedMergeAndSync(); |
| 107 | |
| 108 | // Called when a user cancels any setup dialog (login, merge and sync, etc). |
| 109 | virtual void OnUserCancelledDialog(); |
| 110 | |
| 111 | // Get various information for displaying in the user interface. |
| 112 | browser_sync::SyncBackendHost::StatusSummary QuerySyncStatusSummary(); |
| 113 | browser_sync::SyncBackendHost::Status QueryDetailedSyncStatus(); |
| 114 | |
| 115 | AuthErrorState GetAuthErrorState() const { |
| 116 | return last_auth_error_; |
| 117 | } |
| 118 | |
| 119 | // Displays a dialog for the user to enter GAIA credentials and attempt |
| 120 | // re-authentication, and returns true if it actually opened the dialog. |
| 121 | // Returns false if a dialog is already showing, an auth attempt is in |
| 122 | // progress, the sync system is already authenticated, or some error |
| 123 | // occurred preventing the action. We make it the duty of ProfileSyncService |
| 124 | // to open the dialog to easily ensure only one is ever showing. |
| 125 | bool SetupInProgress() const { |
| 126 | return !IsSyncEnabledByUser() && WizardIsVisible(); |
| 127 | } |
| 128 | bool WizardIsVisible() const { return wizard_.IsVisible(); } |
| 129 | void ShowLoginDialog(); |
| 130 | |
| 131 | // Pretty-printed strings for a given StatusSummary. |
| 132 | static std::wstring BuildSyncStatusSummaryText( |
| 133 | const browser_sync::SyncBackendHost::StatusSummary& summary); |
| 134 | |
| 135 | // Returns true if the SyncBackendHost has told us it's ready to accept |
| 136 | // changes. |
| 137 | // TODO(timsteele): What happens if the bookmark model is loaded, a change |
| 138 | // takes place, and the backend isn't initialized yet? |
| 139 | bool sync_initialized() const { return backend_initialized_; } |
| 140 | |
| 141 | bool UIShouldDepictAuthInProgress() const { |
| 142 | return is_auth_in_progress_; |
| 143 | } |
| 144 | |
| 145 | // A timestamp marking the last time the service observed a transition from |
| 146 | // the SYNCING state to the READY state. Note that this does not reflect the |
| 147 | // last time we polled the server to see if there were any changes; the |
| 148 | // timestamp is only snapped when syncing takes place and we download or |
| 149 | // upload some bookmark entity. |
| 150 | const base::Time& last_synced_time() const { return last_synced_time_; } |
| 151 | |
| 152 | // Returns a user-friendly string form of last synced time (in minutes). |
| 153 | std::wstring GetLastSyncedTimeString() const; |
| 154 | |
| 155 | // Returns the authenticated username of the sync user, or empty if none |
| 156 | // exists. It will only exist if the authentication service provider (e.g |
| 157 | // GAIA) has confirmed the username is authentic. |
| 158 | virtual string16 GetAuthenticatedUsername() const; |
| 159 | |
| 160 | const std::string& last_attempted_user_email() const { |
| 161 | return last_attempted_user_email_; |
| 162 | } |
| 163 | |
| 164 | // The profile we are syncing for. |
| 165 | Profile* profile() { return profile_; } |
| 166 | |
| 167 | // Adds/removes an observer. ProfileSyncService does not take ownership of |
| 168 | // the observer. |
| 169 | void AddObserver(Observer* observer); |
| 170 | void RemoveObserver(Observer* observer); |
| 171 | |
| 172 | protected: |
| 173 | // Call this after any of the subsystems being synced (the bookmark |
| 174 | // model and the sync backend) finishes its initialization. When everything |
| 175 | // is ready, this function will bootstrap the subsystems so that they are |
| 176 | // initially in sync, and start forwarding changes between the two models. |
| 177 | void StartProcessingChangesIfReady(); |
| 178 | |
| 179 | // Various member accessors needed by unit tests. |
| 180 | browser_sync::SyncBackendHost* backend() { return backend_.get(); } |
| 181 | |
| 182 | // Call this when normal operation detects that the bookmark model and the |
| 183 | // syncer model are inconsistent, or similar. The ProfileSyncService will |
| 184 | // try to avoid doing any work to avoid crashing or corrupting things |
| 185 | // further, and will report an error status if queried. |
| 186 | void SetUnrecoverableError(); |
| 187 | |
| 188 | // Returns whether processing changes is allowed. Check this before doing |
| 189 | // any model-modifying operations. |
| 190 | bool ShouldPushChanges(); |
| 191 | |
| 192 | // Starts up the backend sync components. |
| 193 | void StartUp(); |
| 194 | // Shuts down the backend sync components. |
| 195 | // |sync_disabled| indicates if syncing is being disabled or not. |
| 196 | void Shutdown(bool sync_disabled); |
| 197 | |
| 198 | // Tests need to override this. |
| 199 | virtual void InitializeBackend(); |
| 200 | |
| 201 | // Tests need this. |
| 202 | void set_model_associator(browser_sync::ModelAssociator* manager) { |
| 203 | model_associator_ = manager; |
| 204 | } |
| 205 | |
| 206 | // We keep track of the last auth error observed so we can cover up the first |
| 207 | // "expected" auth failure from observers. |
| 208 | // TODO(timsteele): Same as expecting_first_run_auth_needed_event_. Remove |
| 209 | // this! |
| 210 | AuthErrorState last_auth_error_; |
| 211 | |
| 212 | // Cache of the last name the client attempted to authenticate. |
| 213 | std::string last_attempted_user_email_; |
| 214 | |
| 215 | private: |
| 216 | friend class browser_sync::ModelAssociator; |
| 217 | friend class ProfileSyncServiceTest; |
| 218 | friend class ProfileSyncServiceTestHarness; |
| 219 | friend class TestModelAssociator; |
| 220 | FRIEND_TEST(ProfileSyncServiceTest, UnrecoverableErrorSuspendsService); |
| 221 | |
| 222 | enum MoveOrCreate { |
| 223 | MOVE, |
| 224 | CREATE, |
| 225 | }; |
| 226 | |
| 227 | // Initializes the various settings from the command line. |
| 228 | void InitSettings(); |
| 229 | |
| 230 | // Methods to register, load and remove preferences. |
| 231 | void RegisterPreferences(); |
| 232 | void LoadPreferences(); |
| 233 | void ClearPreferences(); |
| 234 | |
| 235 | // Treat the |index|th child of |parent| as a newly added node, and create a |
| 236 | // corresponding node in the sync domain using |trans|. All properties |
| 237 | // will be transferred to the new node. A node corresponding to |parent| |
| 238 | // must already exist and be associated for this call to succeed. Returns |
| 239 | // the ID of the just-created node, or if creation fails, kInvalidID. |
| 240 | int64 CreateSyncNode(const BookmarkNode* parent, |
| 241 | int index, |
| 242 | sync_api::WriteTransaction* trans); |
| 243 | |
| 244 | // Create a bookmark node corresponding to |src| if one is not already |
| 245 | // associated with |src|. Returns the node that was created or updated. |
| 246 | const BookmarkNode* CreateOrUpdateBookmarkNode( |
| 247 | sync_api::BaseNode* src, |
| 248 | BookmarkModel* model); |
| 249 | |
| 250 | // Creates a bookmark node under the given parent node from the given sync |
| 251 | // node. Returns the newly created node. |
| 252 | const BookmarkNode* CreateBookmarkNode( |
| 253 | sync_api::BaseNode* sync_node, |
| 254 | const BookmarkNode* parent, |
| 255 | int index) const; |
| 256 | |
| 257 | // Sets the favicon of the given bookmark node from the given sync node. |
| 258 | // Returns whether the favicon was set in the bookmark node. |
| 259 | bool SetBookmarkFavicon(sync_api::BaseNode* sync_node, |
| 260 | const BookmarkNode* bookmark_node) const; |
| 261 | |
| 262 | // Sets the favicon of the given sync node from the given bookmark node. |
| 263 | void SetSyncNodeFavicon(const BookmarkNode* bookmark_node, |
| 264 | sync_api::WriteNode* sync_node) const; |
| 265 | |
| 266 | // Helper function to determine the appropriate insertion index of sync node |
| 267 | // |node| under the Bookmark model node |parent|, to make the positions |
| 268 | // match up between the two models. This presumes that the predecessor of the |
| 269 | // item (in the bookmark model) has already been moved into its appropriate |
| 270 | // position. |
| 271 | int CalculateBookmarkModelInsertionIndex( |
| 272 | const BookmarkNode* parent, |
| 273 | const sync_api::BaseNode* node) const; |
| 274 | |
| 275 | // Helper function used to fix the position of a sync node so that it matches |
| 276 | // the position of a corresponding bookmark model node. |parent| and |
| 277 | // |index| identify the bookmark model position. |dst| is the node whose |
| 278 | // position is to be fixed. If |operation| is CREATE, treat |dst| as an |
| 279 | // uncreated node and set its position via InitByCreation(); otherwise, |
| 280 | // |dst| is treated as an existing node, and its position will be set via |
| 281 | // SetPosition(). |trans| is the transaction to which |dst| belongs. Returns |
| 282 | // false on failure. |
| 283 | bool PlaceSyncNode(MoveOrCreate operation, |
| 284 | const BookmarkNode* parent, |
| 285 | int index, |
| 286 | sync_api::WriteTransaction* trans, |
| 287 | sync_api::WriteNode* dst); |
| 288 | |
| 289 | // Copy properties (but not position) from |src| to |dst|. |
| 290 | void UpdateSyncNodeProperties(const BookmarkNode* src, |
| 291 | sync_api::WriteNode* dst); |
| 292 | |
| 293 | // Helper function to encode a bookmark's favicon into a PNG byte vector. |
| 294 | void EncodeFavicon(const BookmarkNode* src, |
| 295 | std::vector<unsigned char>* dst) const; |
| 296 | |
| 297 | // Remove the sync node corresponding to |node|. It shouldn't have |
| 298 | // any children. |
| 299 | void RemoveOneSyncNode(sync_api::WriteTransaction* trans, |
| 300 | const BookmarkNode* node); |
| 301 | |
| 302 | // Remove all the sync nodes associated with |node| and its children. |
| 303 | void RemoveSyncNodeHierarchy(const BookmarkNode* node); |
| 304 | |
| 305 | // Whether the sync merge warning should be shown. |
| 306 | bool MergeAndSyncAcceptanceNeeded() const; |
| 307 | |
| 308 | // Sets the last synced time to the current time. |
| 309 | void UpdateLastSyncedTime(); |
| 310 | |
| 311 | // The profile whose data we are synchronizing. |
| 312 | Profile* profile_; |
| 313 | |
| 314 | // TODO(ncarter): Put this in a profile, once there is UI for it. |
| 315 | // This specifies where to find the sync server. |
| 316 | GURL sync_service_url_; |
| 317 | |
| 318 | // Model assocation manager instance. |
| 319 | scoped_refptr<browser_sync::ModelAssociator> model_associator_; |
| 320 | |
| 321 | // The last time we detected a successful transition from SYNCING state. |
| 322 | // Our backend notifies us whenever we should take a new snapshot. |
| 323 | base::Time last_synced_time_; |
| 324 | |
| 325 | // Our asynchronous backend to communicate with sync components living on |
| 326 | // other threads. |
| 327 | scoped_ptr<browser_sync::SyncBackendHost> backend_; |
| 328 | |
| 329 | // Whether the SyncBackendHost has been initialized. |
| 330 | bool backend_initialized_; |
| 331 | |
| 332 | // Set to true when the user first enables sync, and we are waiting for |
| 333 | // syncapi to give us the green light on providing credentials for the first |
| 334 | // time. It is set back to false as soon as we get this message, and is |
| 335 | // false all other times so we don't have to persist this value as it will |
| 336 | // get initialized to false. |
| 337 | // TODO(timsteele): Remove this by way of starting the wizard when enabling |
| 338 | // sync *before* initializing the backend. syncapi will need to change, but |
| 339 | // it means we don't have to wait for the first AuthError; if we ever get |
| 340 | // one, it is actually an error and this bool isn't needed. |
| 341 | bool expecting_first_run_auth_needed_event_; |
| 342 | |
| 343 | // Various pieces of UI query this value to determine if they should show |
| 344 | // an "Authenticating.." type of message. We are the only central place |
| 345 | // all auth attempts funnel through, so it makes sense to provide this. |
| 346 | // As its name suggests, this should NOT be used for anything other than UI. |
| 347 | bool is_auth_in_progress_; |
| 348 | |
| 349 | // True only after all bootstrapping has succeeded: the bookmark model is |
| 350 | // loaded, the sync backend is initialized, and the two domains are |
| 351 | // consistent with one another. |
| 352 | bool ready_to_process_changes_; |
| 353 | |
| 354 | // True if an unrecoverable error (e.g. violation of an assumed invariant) |
| 355 | // occurred during syncer operation. This value should be checked before |
| 356 | // doing any work that might corrupt things further. |
| 357 | bool unrecoverable_error_detected_; |
| 358 | |
| 359 | SyncSetupWizard wizard_; |
| 360 | |
| 361 | ObserverList<Observer> observers_; |
| 362 | |
| 363 | DISALLOW_COPY_AND_ASSIGN(ProfileSyncService); |
| 364 | }; |
| 365 | |
| 366 | #endif // CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_H_ |
| 367 | |
| 368 | #endif // CHROME_PERSONALIZATION |