root/chrome/browser/sync/glue/synced_session_tracker.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SetLocalSessionTag
  2. LookupAllForeignSessions
  3. LookupSessionWindows
  4. LookupSessionTab
  5. LookupTabNodeIds
  6. LookupLocalSession
  7. GetSession
  8. DeleteSession
  9. ResetSessionTracking
  10. DeleteOldSessionWindowIfNecessary
  11. DeleteOldSessionTabIfNecessary
  12. CleanupSession
  13. PutWindowInSession
  14. PutTabInWindow
  15. GetTab
  16. GetTabImpl
  17. Clear

// 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/logging.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/sync/glue/synced_session_tracker.h"

namespace browser_sync {

SyncedSessionTracker::SyncedSessionTracker() {
}

SyncedSessionTracker::~SyncedSessionTracker() {
  Clear();
}

void SyncedSessionTracker::SetLocalSessionTag(
    const std::string& local_session_tag) {
  local_session_tag_ = local_session_tag;
}

bool SyncedSessionTracker::LookupAllForeignSessions(
    std::vector<const SyncedSession*>* sessions) const {
  DCHECK(sessions);
  sessions->clear();
  // Fill vector of sessions from our synced session map.
  for (SyncedSessionMap::const_iterator i =
    synced_session_map_.begin(); i != synced_session_map_.end(); ++i) {
    // Only include foreign sessions with open tabs.
    SyncedSession* foreign_session = i->second;
    if (i->first != local_session_tag_ && !foreign_session->windows.empty()) {
      bool found_tabs = false;
      for (SyncedSession::SyncedWindowMap::const_iterator iter =
               foreign_session->windows.begin();
           iter != foreign_session->windows.end(); ++iter) {
        if (!SessionWindowHasNoTabsToSync(*(iter->second))) {
          found_tabs = true;
          break;
        }
      }
      if (found_tabs)
        sessions->push_back(foreign_session);
    }
  }

  return !sessions->empty();
}

bool SyncedSessionTracker::LookupSessionWindows(
    const std::string& session_tag,
    std::vector<const SessionWindow*>* windows) const {
  DCHECK(windows);
  windows->clear();
  SyncedSessionMap::const_iterator iter = synced_session_map_.find(session_tag);
  if (iter == synced_session_map_.end())
    return false;
  windows->clear();
  for (SyncedSession::SyncedWindowMap::const_iterator window_iter =
           iter->second->windows.begin();
       window_iter != iter->second->windows.end(); window_iter++) {
    windows->push_back(window_iter->second);
  }
  return true;
}

bool SyncedSessionTracker::LookupSessionTab(
    const std::string& tag,
    SessionID::id_type tab_id,
    const SessionTab** tab) const {
  DCHECK(tab);
  SyncedTabMap::const_iterator tab_map_iter = synced_tab_map_.find(tag);
  if (tab_map_iter == synced_tab_map_.end()) {
    // We have no record of this session.
    *tab = NULL;
    return false;
  }
  IDToSessionTabMap::const_iterator tab_iter =
      tab_map_iter->second.find(tab_id);
  if (tab_iter == tab_map_iter->second.end()) {
    // We have no record of this tab.
    *tab = NULL;
    return false;
  }
  *tab = tab_iter->second.tab_ptr;
  return true;
}

bool SyncedSessionTracker::LookupTabNodeIds(
    const std::string& session_tag, std::set<int>* tab_node_ids) {
  tab_node_ids->clear();
  SyncedTabMap::const_iterator tab_map_iter =
      synced_tab_map_.find(session_tag);
  if (tab_map_iter == synced_tab_map_.end())
    return false;

  IDToSessionTabMap::const_iterator tab_iter = tab_map_iter->second.begin();
  while (tab_iter != tab_map_iter->second.end()) {
    if (tab_iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID)
      tab_node_ids->insert(tab_iter->second.tab_node_id);
    ++tab_iter;
  }
  return true;
}

bool SyncedSessionTracker::LookupLocalSession(const SyncedSession** output)
    const {
  SyncedSessionMap::const_iterator it =
      synced_session_map_.find(local_session_tag_);
  if (it != synced_session_map_.end()) {
    *output = it->second;
    return true;
  }
  return false;
}

SyncedSession* SyncedSessionTracker::GetSession(
    const std::string& session_tag) {
  SyncedSession* synced_session = NULL;
  if (synced_session_map_.find(session_tag) !=
      synced_session_map_.end()) {
    synced_session = synced_session_map_[session_tag];
  } else {
    synced_session = new SyncedSession;
    DVLOG(1) << "Creating new session with tag " << session_tag << " at "
             << synced_session;
    synced_session->session_tag = session_tag;
    synced_session_map_[session_tag] = synced_session;
  }
  DCHECK(synced_session);
  return synced_session;
}

bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
  bool found_session = false;
  SyncedSessionMap::iterator iter = synced_session_map_.find(session_tag);
  if (iter != synced_session_map_.end()) {
    SyncedSession* session = iter->second;
    synced_session_map_.erase(iter);
    delete session;  // Delete the SyncedSession object.
    found_session = true;
  }
  synced_window_map_.erase(session_tag);
  // It's possible there was no header node but there were tab nodes.
  if (synced_tab_map_.erase(session_tag) > 0) {
    found_session = true;
  }
  return found_session;
}

void SyncedSessionTracker::ResetSessionTracking(
    const std::string& session_tag) {
  // Reset window tracking.
  GetSession(session_tag)->windows.clear();
  SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
  if (window_iter != synced_window_map_.end()) {
    for (IDToSessionWindowMap::iterator window_map_iter =
             window_iter->second.begin();
         window_map_iter != window_iter->second.end(); ++window_map_iter) {
      window_map_iter->second.owned = false;
      // We clear out the tabs to prevent double referencing of the same tab.
      // All tabs that are in use will be added back as needed.
      window_map_iter->second.window_ptr->tabs.clear();
    }
  }

  // Reset tab tracking.
  SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
  if (tab_iter != synced_tab_map_.end()) {
    for (IDToSessionTabMap::iterator tab_map_iter =
             tab_iter->second.begin();
         tab_map_iter != tab_iter->second.end(); ++tab_map_iter) {
      tab_map_iter->second.owned = false;
    }
  }
}

bool SyncedSessionTracker::DeleteOldSessionWindowIfNecessary(
    SessionWindowWrapper window_wrapper) {
  // Clear the tabs first, since we don't want the destructor to destroy
  // them. Their deletion will be handled by DeleteOldSessionTab below.
  if (!window_wrapper.owned) {
    DVLOG(1) << "Deleting closed window "
             << window_wrapper.window_ptr->window_id.id();
    window_wrapper.window_ptr->tabs.clear();
    delete window_wrapper.window_ptr;
    return true;
  }
  return false;
}

bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
    SessionTabWrapper tab_wrapper) {
  if (!tab_wrapper.owned) {
    if (VLOG_IS_ON(1)) {
      SessionTab* tab_ptr = tab_wrapper.tab_ptr;
      std::string title;
      if (tab_ptr->navigations.size() > 0) {
        title = " (" + base::UTF16ToUTF8(
            tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
      }
      DVLOG(1) << "Deleting closed tab " << tab_ptr->tab_id.id() << title
               << " from window " << tab_ptr->window_id.id();
    }
    unmapped_tabs_.erase(tab_wrapper.tab_ptr);
    delete tab_wrapper.tab_ptr;
    return true;
  }
  return false;
}

void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
  // Go through and delete any windows or tabs without owners.
  SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
  if (window_iter != synced_window_map_.end()) {
    for (IDToSessionWindowMap::iterator iter = window_iter->second.begin();
         iter != window_iter->second.end();) {
      SessionWindowWrapper window_wrapper = iter->second;
      if (DeleteOldSessionWindowIfNecessary(window_wrapper))
        window_iter->second.erase(iter++);
      else
        ++iter;
    }
  }

  SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
  if (tab_iter != synced_tab_map_.end()) {
    for (IDToSessionTabMap::iterator iter = tab_iter->second.begin();
         iter != tab_iter->second.end();) {
      SessionTabWrapper tab_wrapper = iter->second;
      if (DeleteOldSessionTabIfNecessary(tab_wrapper))
        tab_iter->second.erase(iter++);
      else
        ++iter;
    }
  }
}

void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
                                              SessionID::id_type window_id) {
  SessionWindow* window_ptr = NULL;
  IDToSessionWindowMap::iterator iter =
      synced_window_map_[session_tag].find(window_id);
  if (iter != synced_window_map_[session_tag].end()) {
    iter->second.owned = true;
    window_ptr = iter->second.window_ptr;
    DVLOG(1) << "Putting seen window " << window_id  << " at " << window_ptr
             << "in " << (session_tag == local_session_tag_ ?
                          "local session" : session_tag);
  } else {
    // Create the window.
    window_ptr = new SessionWindow();
    window_ptr->window_id.set_id(window_id);
    synced_window_map_[session_tag][window_id] =
        SessionWindowWrapper(window_ptr, IS_OWNED);
    DVLOG(1) << "Putting new window " << window_id  << " at " << window_ptr
             << "in " << (session_tag == local_session_tag_ ?
                          "local session" : session_tag);
  }
  DCHECK(window_ptr);
  DCHECK_EQ(window_ptr->window_id.id(), window_id);
  DCHECK_EQ(reinterpret_cast<SessionWindow*>(NULL),
            GetSession(session_tag)->windows[window_id]);
  GetSession(session_tag)->windows[window_id] = window_ptr;
}

void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
                                          SessionID::id_type window_id,
                                          SessionID::id_type tab_id,
                                          size_t tab_index) {
  // We're called here for two reasons. 1) We've received an update to the
  // SessionWindow information of a SessionHeader node for a foreign session,
  // and 2) The SessionHeader node for our local session changed. In both cases
  // we need to update our tracking state to reflect the change.
  //
  // Because the SessionHeader nodes are separate from the individual tab nodes
  // and we don't store tab_node_ids in the header / SessionWindow specifics,
  // the tab_node_ids are not always available when processing headers.
  // We know that we will eventually process (via GetTab) every single tab node
  // in the system, so we permit ourselves to use kInvalidTabNodeID here and
  // rely on the later update to build the mapping (or a restart).
  // TODO(tim): Bug 98892. Update comment when Sync API conversion finishes to
  // mention that in the meantime, the only ill effect is that we may not be
  // able to fully clean up a stale foreign session, but it will get garbage
  // collected eventually.
  SessionTab* tab_ptr = GetTabImpl(
      session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
  unmapped_tabs_.erase(tab_ptr);
  synced_tab_map_[session_tag][tab_id].owned = true;
  tab_ptr->window_id.set_id(window_id);
  DVLOG(1) << "  - tab " << tab_id << " added to window "<< window_id;
  DCHECK(GetSession(session_tag)->windows.find(window_id) !=
         GetSession(session_tag)->windows.end());
  std::vector<SessionTab*>& window_tabs =
      GetSession(session_tag)->windows[window_id]->tabs;
  if (window_tabs.size() <= tab_index) {
    window_tabs.resize(tab_index+1, NULL);
  }
  DCHECK(!window_tabs[tab_index]);
  window_tabs[tab_index] = tab_ptr;
}

SessionTab* SyncedSessionTracker::GetTab(
    const std::string& session_tag,
    SessionID::id_type tab_id,
    int tab_node_id) {
  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
  return GetTabImpl(session_tag, tab_id, tab_node_id);
}

SessionTab* SyncedSessionTracker::GetTabImpl(
    const std::string& session_tag,
    SessionID::id_type tab_id,
    int tab_node_id) {
  SessionTab* tab_ptr = NULL;
  IDToSessionTabMap::iterator iter =
      synced_tab_map_[session_tag].find(tab_id);
  if (iter != synced_tab_map_[session_tag].end()) {
    tab_ptr = iter->second.tab_ptr;
    if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
        tab_id != TabNodePool::kInvalidTabID) {
      // TabIDs are not stable across restarts of a client. Consider this
      // example with two tabs:
      //
      // http://a.com  TabID1 --> NodeIDA
      // http://b.com  TabID2 --> NodeIDB
      //
      // After restart, tab ids are reallocated. e.g, one possibility:
      // http://a.com TabID2 --> NodeIDA
      // http://b.com TabID1 --> NodeIDB
      //
      // If that happend on a remote client, here we will see an update to
      // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
      // with tab_node_id changing from NodeIDB to NodeIDA.
      //
      // We can also wind up here if we created this tab as an out-of-order
      // update to the header node for this session before actually associating
      // the tab itself, so the tab node id wasn't available at the time and
      // is currenlty kInvalidTabNodeID.
      //
      // In both cases, we update the tab_node_id.
      iter->second.tab_node_id = tab_node_id;
    }

    if (VLOG_IS_ON(1)) {
      std::string title;
      if (tab_ptr->navigations.size() > 0) {
        title = " (" + base::UTF16ToUTF8(
            tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
      }
      DVLOG(1) << "Getting "
               << (session_tag == local_session_tag_ ?
                   "local session" : session_tag)
               << "'s seen tab " << tab_id  << " at " << tab_ptr << title;
    }
  } else {
    tab_ptr = new SessionTab();
    tab_ptr->tab_id.set_id(tab_id);
    synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
                                                             NOT_OWNED,
                                                             tab_node_id);
    unmapped_tabs_.insert(tab_ptr);
    DVLOG(1) << "Getting "
             << (session_tag == local_session_tag_ ?
                 "local session" : session_tag)
             << "'s new tab " << tab_id  << " at " << tab_ptr;
  }
  DCHECK(tab_ptr);
  DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
  return tab_ptr;
}

void SyncedSessionTracker::Clear() {
  // Delete SyncedSession objects (which also deletes all their windows/tabs).
  STLDeleteValues(&synced_session_map_);

  // Go through and delete any tabs we had allocated but had not yet placed into
  // a SyncedSessionobject.
  STLDeleteElements(&unmapped_tabs_);

  // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
  // themselves; they should have all been deleted above).
  synced_window_map_.clear();
  synced_tab_map_.clear();

  local_session_tag_.clear();
}

}  // namespace browser_sync

/* [<][>][^][v][top][bottom][index][help] */