1 // Copyright (c) 2011 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 #ifndef CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_ 6 #define CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_ 7 #pragma once 8 9 #include <map> 10 #include <string> 11 #include <vector> 12 13 #include "base/basictypes.h" 14 #include "base/format_macros.h" 15 #include "base/gtest_prod_util.h" 16 #include "base/memory/scoped_vector.h" 17 #include "base/observer_list.h" 18 #include "base/string_util.h" 19 #include "base/threading/non_thread_safe.h" 20 #include "base/utf_string_conversions.h" 21 #include "chrome/browser/sessions/session_id.h" 22 #include "chrome/browser/sessions/session_service.h" 23 #include "chrome/browser/sessions/session_types.h" 24 #include "chrome/browser/sync/engine/syncapi.h" 25 #include "chrome/browser/sync/glue/foreign_session_tracker.h" 26 #include "chrome/browser/sync/glue/model_associator.h" 27 #include "chrome/browser/sync/protocol/session_specifics.pb.h" 28 #include "chrome/browser/sync/syncable/model_type.h" 29 #include "chrome/browser/ui/browser_window.h" 30 #include "content/browser/tab_contents/tab_contents.h" 31 32 class Profile; 33 class ProfileSyncService; 34 35 namespace sync_api { 36 class ReadNode; 37 class WriteNode; 38 class WriteTransaction; 39 } // namespace sync_api 40 41 namespace sync_pb { 42 class SessionSpecifics; 43 } // namespace sync_pb 44 45 namespace browser_sync { 46 47 static const char kSessionsTag[] = "google_chrome_sessions"; 48 49 // Contains all logic for associating the Chrome sessions model and 50 // the sync sessions model. 51 class SessionModelAssociator 52 : public PerDataTypeAssociatorInterface<TabContents, size_t>, 53 public base::NonThreadSafe { 54 public: 55 // Does not take ownership of sync_service. 56 explicit SessionModelAssociator(ProfileSyncService* sync_service); 57 SessionModelAssociator(ProfileSyncService* sync_service, 58 bool setup_for_test); 59 virtual ~SessionModelAssociator(); 60 61 // The has_nodes out parameter is set to true if the sync model has 62 // nodes other than the permanent tagged nodes. The method may 63 // return false if an error occurred. 64 virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes); 65 66 // AssociatorInterface and PerDataTypeAssociator Interface implementation. 67 virtual void AbortAssociation() { 68 // No implementation needed, this associator runs on the main thread. 69 } 70 71 // See ModelAssociator interface. 72 virtual bool CryptoReadyIfNecessary(); 73 74 // Returns sync id for the given chrome model id. 75 // Returns sync_api::kInvalidId if the sync node is not found for the given 76 // chrome id. 77 virtual int64 GetSyncIdFromChromeId(const size_t& id); 78 79 // Returns sync id for the given session tag. 80 // Returns sync_api::kInvalidId if the sync node is not found for the given 81 // tag 82 virtual int64 GetSyncIdFromSessionTag(const std::string& tag); 83 84 // Not used. 85 virtual const TabContents* GetChromeNodeFromSyncId(int64 sync_id); 86 87 // Not used. 88 virtual bool InitSyncNodeFromChromeId(const size_t& id, 89 sync_api::BaseNode* sync_node); 90 91 // Resync local window information. Updates the local sessions header node 92 // with the status of open windows and the order of tabs they contain. Should 93 // only be called for changes that affect a window, not a change within a 94 // single tab. 95 // 96 // If |reload_tabs| is true, will also resync all tabs (same as calling 97 // ReassociateTabs with a vector of all tabs). 98 void ReassociateWindows(bool reload_tabs); 99 100 // Loads and reassociates the local tabs referenced in |tabs|. 101 void ReassociateTabs(const std::vector<TabContents*>& tabs); 102 103 // Reassociates a single tab with the sync model. Will check if the tab 104 // already is associated with a sync node and allocate one if necessary. 105 void ReassociateTab(const TabContents& tab); 106 107 // Associate a local tab and it's sync node. Will overwrite the contents of 108 // the sync node with new specifics built from the tab. 109 virtual void Associate(const TabContents* tab, int64 sync_id); 110 111 // Looks up the specified sync node, and marks that tab as closed, then marks 112 // the node as free and deletes association. 113 virtual void Disassociate(int64 sync_id); 114 115 // Load any foreign session info stored in sync db and update the sync db 116 // with local client data. Processes/reuses any sync nodes owned by this 117 // client and creates any further sync nodes needed to store local header and 118 // tab info. 119 virtual bool AssociateModels(); 120 121 // Initializes the given sync node from the given chrome node id. 122 // Returns false if no sync node was found for the given chrome node id or 123 // if the initialization of sync node fails. 124 virtual bool InitSyncNodeFromChromeId(const std::string& id, 125 sync_api::BaseNode* sync_node); 126 127 // Clear local sync data buffers. Does not delete sync nodes to avoid 128 // tombstones. TODO(zea): way to eventually delete orphaned nodes. 129 virtual bool DisassociateModels(); 130 131 // Returns the tag used to uniquely identify this machine's session in the 132 // sync model. 133 inline const std::string& GetCurrentMachineTag() { 134 DCHECK(!current_machine_tag_.empty()); 135 return current_machine_tag_; 136 } 137 138 // Load and associate window and tab data for a foreign session 139 bool AssociateForeignSpecifics(const sync_pb::SessionSpecifics& specifics, 140 int64 modification_time); 141 142 // Removes a foreign session from our internal bookkeeping. 143 void DisassociateForeignSession(const std::string& foreign_session_tag); 144 145 // Builds a list of all foreign sessions. 146 // Caller does NOT own ForeignSession objects. 147 bool GetAllForeignSessions(std::vector<const ForeignSession*>* sessions); 148 149 // Loads all windows for foreign session with session tag |tag|. 150 // Caller does NOT own ForeignSession objects. 151 bool GetForeignSession(const std::string& tag, 152 std::vector<SessionWindow*>* windows); 153 154 // Looks up the foreign tab identified by |tab_id| and belonging to foreign 155 // session |tag|. 156 // Caller does NOT own the SessionTab object. 157 bool GetForeignTab(const std::string& tag, 158 const SessionID::id_type tab_id, 159 const SessionTab** tab); 160 161 // Specifies whether the window has tabs to sync. The new tab page does not 162 // count. If no tabs to sync, it returns true, otherwise false; 163 static bool SessionWindowHasNoTabsToSync(const SessionWindow& window); 164 165 // Control which local tabs we're interested in syncing. 166 // Ensures the profile matches sync's profile and that the tab has at least 167 // one navigation entry and is not an empty tab. 168 bool IsValidTab(const TabContents& tab); 169 170 // Control which foreign tabs we're interested in displaying. 171 // Checks that the tab has navigations and is not a new tab. 172 // Note: a new tab page with back/forward history is valid. 173 static bool IsValidSessionTab(const SessionTab& tab); 174 175 // Returns the syncable model type. 176 static syncable::ModelType model_type() { return syncable::SESSIONS; } 177 178 private: 179 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteSessionToNode); 180 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, 181 WriteFilledSessionToNode); 182 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, 183 WriteForeignSessionToNode); 184 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolEmpty); 185 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty); 186 FRIEND_TEST_ALL_PREFIXES(SessionModelAssociatorTest, PopulateSessionWindow); 187 FRIEND_TEST_ALL_PREFIXES(SessionModelAssociatorTest, PopulateSessionTab); 188 189 // Keep all the links to local tab data in one place. 190 class TabLinks { 191 public: 192 // To support usage as second value in maps we need default and copy 193 // constructors. 194 TabLinks() 195 : sync_id_(0), 196 session_tab_(NULL), 197 tab_(NULL) {} 198 199 // We only ever have either a SessionTab (for foreign tabs), or a 200 // TabContents (for local tabs). 201 TabLinks(int64 sync_id, const TabContents* tab) 202 : sync_id_(sync_id), 203 session_tab_(NULL) { 204 tab_ = const_cast<TabContents*>(tab); 205 } 206 TabLinks(int64 sync_id, const SessionTab* session_tab) 207 : sync_id_(sync_id), 208 tab_(NULL) { 209 session_tab_ = const_cast<SessionTab*>(session_tab); 210 } 211 212 inline int64 sync_id() const { return sync_id_; } 213 inline const SessionTab* session_tab() const { return session_tab_; } 214 inline const TabContents* tab() const { return tab_; } 215 private: 216 int64 sync_id_; 217 SessionTab* session_tab_; 218 TabContents* tab_; 219 }; 220 221 // A pool for managing free/used tab sync nodes. Performs lazy creation 222 // of sync nodes when necessary. 223 class TabNodePool { 224 public: 225 explicit TabNodePool(ProfileSyncService* sync_service); 226 ~TabNodePool(); 227 228 // Add a previously allocated tab sync node to our pool. Increases the size 229 // of tab_syncid_pool_ by one and marks the new tab node as free. 230 // Note: this should only be called when we discover tab sync nodes from 231 // previous sessions, not for freeing tab nodes we created through 232 // GetFreeTabNode (use FreeTabNode below for that). 233 void AddTabNode(int64 sync_id); 234 235 // Returns the sync_id for the next free tab node. If none are available, 236 // creates a new tab node. 237 // Note: We make use of the following "id's" 238 // - a sync_id: an int64 used in |sync_api::InitByIdLookup| 239 // - a tab_id: created by session service, unique to this client 240 // - a tab_node_id: the id for a particular sync tab node. This is used 241 // to generate the sync tab node tag through: 242 // tab_tag = StringPrintf("%s_%ui", local_session_tag, tab_node_id); 243 // tab_node_id and sync_id are both unique to a particular sync node. The 244 // difference is that tab_node_id is controlled by the model associator and 245 // is used when creating a new sync node, which returns the sync_id, created 246 // by the sync db. 247 int64 GetFreeTabNode(); 248 249 // Return a tab node to our free pool. 250 // Note: the difference between FreeTabNode and AddTabNode is that 251 // FreeTabNode does not modify the size of |tab_syncid_pool_|, while 252 // AddTabNode increases it by one. In the case of FreeTabNode, the size of 253 // the |tab_syncid_pool_| should always be equal to the amount of tab nodes 254 // associated with this machine. 255 void FreeTabNode(int64 sync_id); 256 257 // Clear tab pool. 258 inline void clear() { tab_syncid_pool_.clear(); } 259 260 // Return the number of tab nodes this client currently has allocated 261 // (including both free and used nodes) 262 inline size_t capacity() const { return tab_syncid_pool_.size(); } 263 264 // Return empty status (all tab nodes are in use). 265 inline bool empty() const { return tab_pool_fp_ == -1; } 266 267 // Return full status (no tab nodes are in use). 268 inline bool full() { 269 return tab_pool_fp_ == static_cast<int64>(tab_syncid_pool_.size())-1; 270 } 271 272 inline void set_machine_tag(const std::string& machine_tag) { 273 machine_tag_ = machine_tag; 274 } 275 private: 276 // Pool of all available syncid's for tab's we have created. 277 std::vector<int64> tab_syncid_pool_; 278 279 // Free pointer for tab pool. Only those node id's, up to and including the 280 // one indexed by the free pointer, are valid and free. The rest of the 281 // |tab_syncid_pool_| is invalid because the nodes are in use. 282 // To get the next free node, use tab_syncid_pool_[tab_pool_fp_--]. 283 int64 tab_pool_fp_; 284 285 // The machiine tag associated with this tab pool. Used in the title of new 286 // sync nodes. 287 std::string machine_tag_; 288 289 // Our sync service profile (for making changes to the sync db) 290 ProfileSyncService* sync_service_; 291 292 DISALLOW_COPY_AND_ASSIGN(TabNodePool); 293 }; 294 295 // Datatypes for accessing local tab data. 296 typedef std::map<SessionID::id_type, TabLinks> TabLinksMap; 297 298 // Delete all foreign session/window/tab objects allocated dynamically. 299 // This is comprised of ForeignSession*, IDToSessionTabMap*, and any orphaned 300 // SessionTab*'s. 301 void DeleteForeignSessions(); 302 303 // Determine if a window is of a type we're interested in syncing. 304 static bool ShouldSyncWindowType(const Browser::Type& type); 305 306 // Build a sync tag from tab_node_id. 307 static inline std::string TabIdToTag( 308 const std::string machine_tag, 309 size_t tab_node_id) { 310 return StringPrintf("%s %"PRIuS"", 311 machine_tag.c_str(), tab_node_id); 312 } 313 314 // Initializes the tag corresponding to this machine. 315 void InitializeCurrentMachineTag(sync_api::WriteTransaction* trans); 316 317 // Updates the server data based upon the current client session. If no node 318 // corresponding to this machine exists in the sync model, one is created. 319 void UpdateSyncModelDataFromClient(); 320 321 // Pulls the current sync model from the sync database and returns true upon 322 // update of the client model. Will associate any foreign sessions as well as 323 // keep track of any local tab nodes, adding them to our free tab node pool. 324 bool UpdateAssociationsFromSyncModel(const sync_api::ReadNode& root, 325 const sync_api::BaseTransaction* trans); 326 327 // Fills a tab sync node with data from a TabContents object. 328 // (from a local navigation event) 329 bool WriteTabContentsToSyncModel(const Browser& browser, 330 const TabContents& tab, 331 const int64 sync_id, 332 sync_api::WriteTransaction* trans); 333 334 // Used to populate a session window from the session specifics window 335 // provided. Tracks any foreign session data created through |tracker|. 336 static void PopulateSessionWindowFromSpecifics( 337 const std::string& foreign_session_tag, 338 const sync_pb::SessionWindow& window, 339 const int64 mtime, 340 SessionWindow* session_window, 341 ForeignSessionTracker* tracker); 342 343 // Used to populate a session tab from the session specifics tab provided. 344 static void PopulateSessionTabFromSpecifics(const sync_pb::SessionTab& tab, 345 const int64 mtime, 346 SessionTab* session_tab); 347 348 // Used to populate a session tab from the session specifics tab provided. 349 static void AppendSessionTabNavigation( 350 const sync_pb::TabNavigation& navigation, 351 std::vector<TabNavigation>* navigations); 352 353 // Populates the navigation portion of the session specifics. 354 static void PopulateSessionSpecificsNavigation( 355 const TabNavigation* navigation, 356 sync_pb::TabNavigation* tab_navigation); 357 358 // Returns the session service from |sync_service_|. 359 SessionService* GetSessionService(); 360 361 // Internal method used in the callback to obtain the current session. 362 // We don't own |windows|. 363 void OnGotSession(int handle, std::vector<SessionWindow*>* windows); 364 365 // Populate a session specifics header from a list of SessionWindows 366 void PopulateSessionSpecificsHeader( 367 const std::vector<SessionWindow*>& windows, 368 sync_pb::SessionHeader* header_s); 369 370 // Populates the window portion of the session specifics. 371 void PopulateSessionSpecificsWindow(const SessionWindow& window, 372 sync_pb::SessionWindow* session_window); 373 374 // Syncs all the tabs in |window| with the local sync db. Will allocate tab 375 // nodes if needed. 376 bool SyncLocalWindowToSyncModel(const SessionWindow& window); 377 378 // Fills a tab sync node with data from a SessionTab object. 379 // (from ReadCurrentSessions) 380 bool WriteSessionTabToSyncModel(const SessionTab& tab, 381 const int64 sync_id, 382 sync_api::WriteTransaction* trans); 383 384 // Populates the tab portion of the session specifics. 385 void PopulateSessionSpecificsTab(const SessionTab& tab, 386 sync_pb::SessionTab* session_tab); 387 388 // Local client name. 389 std::string current_machine_tag_; 390 391 // Pool of all used/available sync nodes associated with tabs. 392 TabNodePool tab_pool_; 393 394 // SyncID for the sync node containing all the window information for this 395 // client. 396 int64 local_session_syncid_; 397 398 // Mapping of current open (local) tabs to their sync identifiers. 399 TabLinksMap tab_map_; 400 401 ForeignSessionTracker foreign_session_tracker_; 402 403 // Weak pointer. 404 ProfileSyncService* sync_service_; 405 406 // Consumer used to obtain the current session. 407 CancelableRequestConsumer consumer_; 408 409 // To avoid certain checks not applicable to tests. 410 bool setup_for_test_; 411 412 DISALLOW_COPY_AND_ASSIGN(SessionModelAssociator); 413 }; 414 415 } // namespace browser_sync 416 417 #endif // CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_ 418