Home | History | Annotate | Download | only in glue
      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