Chromium Code Reviews| Index: chrome/browser/sync/glue/session_model_associator.cc |
| =================================================================== |
| --- chrome/browser/sync/glue/session_model_associator.cc (revision 68844) |
| +++ chrome/browser/sync/glue/session_model_associator.cc (working copy) |
| @@ -4,15 +4,19 @@ |
| #include "chrome/browser/sync/glue/session_model_associator.h" |
| +#include <algorithm> |
| #include <utility> |
| #include "base/logging.h" |
| -#include "base/string_util.h" |
| -#include "base/utf_string_conversions.h" |
| +#include "chrome/browser/browser_list.h" |
| +#include "chrome/browser/browser_window.h" |
| #include "chrome/browser/profiles/profile.h" |
| -#include "chrome/browser/sessions/session_id.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/syncable/syncable.h" |
| +#include "chrome/browser/tab_contents/navigation_controller.h" |
| +#include "chrome/browser/tab_contents/navigation_entry.h" |
| +#include "chrome/browser/tabs/tab_strip_model.h" |
| +#include "chrome/common/extensions/extension.h" |
| #include "chrome/common/notification_details.h" |
| #include "chrome/common/notification_service.h" |
| #include "chrome/common/url_constants.h" |
| @@ -20,94 +24,45 @@ |
| namespace browser_sync { |
| namespace { |
| - |
| static const char kNoSessionsFolderError[] = |
| "Server did not create the top-level sessions node. We " |
| "might be running against an out-of-date server."; |
| +// The maximum number of navigations in each direction we care to sync. |
| +static const int max_sync_navigation_count = 6; |
| } // namespace |
| -SessionModelAssociator::SessionModelAssociator( |
| - ProfileSyncService* sync_service) : sync_service_(sync_service) { |
| +SessionModelAssociator::SessionModelAssociator(ProfileSyncService* sync_service) |
| + : tab_pool_fp_(-1), |
| + local_session_syncid_(sync_api::kInvalidId), |
| + sync_service_(sync_service) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(sync_service_); |
| } |
| SessionModelAssociator::~SessionModelAssociator() { |
| DCHECK(CalledOnValidThread()); |
| + DeleteForeignSessions(); |
| } |
| -bool SessionModelAssociator::AssociateModels() { |
| - DCHECK(CalledOnValidThread()); |
| +void SessionModelAssociator::DeleteForeignSessions() { |
|
tim (not reviewing)
2010/12/15 21:14:00
should we slap CalledOnValidThread in most of thes
Nicolas Zea
2010/12/16 18:09:18
Why not.
On 2010/12/15 21:14:00, timsteele wrote:
|
| + // Delete ForeignSession objects (which also deletes all their windows/tabs). |
| + STLDeleteContainerPairSecondPointers(foreign_session_map_.begin(), |
| + foreign_session_map_.end()); |
| + foreign_session_map_.clear(); |
| - // Make sure we have a machine tag. |
| - if (current_machine_tag_.empty()) |
| - InitializeCurrentMachineTag(); // Creates a syncable::BaseTransaction. |
| + // Delete IDToSessTab maps. Does not delete the SessionTab objects, because |
| + // they should already be referenced through foreign_session_map_. |
| + STLDeleteContainerPairSecondPointers(foreign_tab_map_.begin(), |
| + foreign_tab_map_.end()); |
| + foreign_tab_map_.clear(); |
| - { |
| - // Do an initial update from sync model (in case we just re-enabled and |
| - // already had data). |
| - sync_api::ReadTransaction trans( |
| - sync_service_->backend()->GetUserShareHandle()); |
| - UpdateFromSyncModel(&trans); |
| - } |
| - |
| - // Check if anything has changed on the client side. |
| - UpdateSyncModelDataFromClient(); |
| - return true; |
| + // Go through and delete any tabs we had allocated but had not yet placed into |
| + // a ForeignSessionobject. |
| + STLDeleteContainerPointers(unmapped_tabs_.begin(), unmapped_tabs_.end()); |
| + unmapped_tabs_.clear(); |
| } |
| -bool SessionModelAssociator::ChromeModelHasUserCreatedNodes( |
| - bool* has_nodes) { |
| - DCHECK(CalledOnValidThread()); |
| - CHECK(has_nodes); |
| - // This is wrong, but this function is unused, anyway. |
| - *has_nodes = true; |
| - return true; |
| -} |
| - |
| -bool SessionModelAssociator::DisassociateModels() { |
| - specifics_.clear(); |
| - |
| - // There is no local model stored with which to disassociate, just notify |
| - // foreign session handlers. |
| - NotificationService::current()->Notify( |
| - NotificationType::FOREIGN_SESSION_DISABLED, |
| - NotificationService::AllSources(), |
| - NotificationService::NoDetails()); |
| - return true; |
| -} |
| - |
| -const sync_pb::SessionSpecifics* SessionModelAssociator:: |
| - GetChromeNodeFromSyncId(int64 sync_id) { |
| - sync_api::ReadTransaction trans( |
| - sync_service_->backend()->GetUserShareHandle()); |
| - sync_api::ReadNode node(&trans); |
| - if (!node.InitByIdLookup(sync_id)) |
| - return NULL; |
| - return new sync_pb::SessionSpecifics(node.GetSessionSpecifics()); |
| -} |
| - |
| -bool SessionModelAssociator::GetSyncIdForTaggedNode(const std::string* tag, |
| - int64* sync_id) { |
| - sync_api::ReadTransaction trans( |
| - sync_service_->backend()->GetUserShareHandle()); |
| - sync_api::ReadNode node(&trans); |
| - if (!node.InitByClientTagLookup(syncable::SESSIONS, *tag)) |
| - return false; |
| - *sync_id = node.GetId(); |
| - return true; |
| -} |
| - |
| -int64 SessionModelAssociator::GetSyncIdFromChromeId(const std::string& id) { |
| - sync_api::ReadTransaction trans( |
| - sync_service_->backend()->GetUserShareHandle()); |
| - sync_api::ReadNode node(&trans); |
| - if (!node.InitByClientTagLookup(syncable::SESSIONS, id)) |
| - return sync_api::kInvalidId; |
| - return node.GetId(); |
| -} |
| - |
| bool SessionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { |
| DCHECK(CalledOnValidThread()); |
| CHECK(has_nodes); |
| @@ -125,138 +80,190 @@ |
| return true; |
| } |
| -std::string SessionModelAssociator::GetCurrentMachineTag() { |
| - DCHECK(!current_machine_tag_.empty()); |
| - return current_machine_tag_; |
| +int64 SessionModelAssociator::GetSyncIdFromChromeId(const size_t& id) { |
| + return GetSyncIdFromSessionTag(TabIdToTag(id)); |
| } |
| -void SessionModelAssociator::UpdateSyncModelDataFromClient() { |
| - DCHECK(CalledOnValidThread()); |
| - SessionService::SessionCallback* callback = |
| - NewCallback(this, &SessionModelAssociator::OnGotSession); |
| - // TODO(jerrica): Stop current race condition, possibly make new method in |
| - // session service, which only grabs the windows from memory. |
| - GetSessionService()->GetCurrentSession(&consumer_, callback); |
| +int64 SessionModelAssociator::GetSyncIdFromSessionTag(const std::string& tag) { |
| + sync_api::ReadTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + sync_api::ReadNode node(&trans); |
| + if (!node.InitByClientTagLookup(syncable::SESSIONS, tag)) |
| + return sync_api::kInvalidId; |
| + return node.GetId(); |
| } |
| +void SessionModelAssociator::ReassociateWindows(bool reload_tabs) { |
| + sync_pb::SessionSpecifics specifics; |
| + specifics.set_session_tag(GetCurrentMachineTag()); |
| + sync_pb::SessionHeader* header_s = specifics.mutable_header(); |
| -// TODO(zea): Don't recreate sessions_ vector from scratch each time. This |
| -// will involve knowing which sessions have been changed (a different data |
| -// structure will probably be better too). |
| -bool SessionModelAssociator::UpdateFromSyncModel( |
| - const sync_api::BaseTransaction* trans) { |
| - DCHECK(CalledOnValidThread()); |
| + for (BrowserList::const_iterator i = BrowserList::begin(); |
| + i != BrowserList::end(); ++i) { |
| + // Make sure the browser has tabs and a window. Browsers destructor |
| + // removes itself from the BrowserList. When a browser is closed the |
| + // destructor is not necessarily run immediately. This means its possible |
| + // for us to get a handle to a browser that is about to be removed. If |
| + // the tab count is 0 or the window is NULL, the browser is about to be |
| + // deleted, so we ignore it. |
| + if (ShouldSyncWindowType((*i)->type()) && (*i)->tab_count() && |
| + (*i)->window()) { |
| + sync_pb::SessionWindow window_s; |
| + SessionID::id_type window_id = (*i)->session_id().id(); |
| + VLOG(1) << "Reassociating window " << window_id << " with " << |
| + (*i)->tab_count() << " tabs."; |
| + window_s.set_window_id(window_id); |
| + window_s.set_selected_tab_index((*i)->selected_index()); |
| + if ((*i)->type() == |
| + Browser::TYPE_NORMAL) { |
| + window_s.set_browser_type( |
| + sync_pb::SessionWindow_BrowserType_TYPE_NORMAL); |
| + } else { |
| + window_s.set_browser_type( |
| + sync_pb::SessionWindow_BrowserType_TYPE_POPUP); |
| + } |
| - // Rebuild specifics_ vector |
| - specifics_.clear(); |
| - if (!QuerySyncModel(trans, specifics_)) { |
| - LOG(ERROR) << "SessionModelAssociator failed to updated from sync model"; |
| - return false; |
| + // Store the order of tabs. |
| + bool found_tabs = false; |
| + for (int j = 0; j < (*i)->tab_count(); ++j) { |
| + TabContents* tab = (*i)->GetTabContentsAt(j); |
| + DCHECK(tab); |
| + if (IsValidTab(*tab)) { |
| + found_tabs = true; |
| + window_s.add_tab(tab->controller().session_id().id()); |
| + if (reload_tabs) { |
| + ReassociateTab(*tab); |
| + } |
| + } |
| + } |
| + // Only add a window if it contains valid tabs. |
| + if (found_tabs) { |
| + sync_pb::SessionWindow* header_window = header_s->add_window(); |
| + *header_window = window_s; |
| + } |
| + } |
| } |
| - return true; |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + sync_api::WriteNode header_node(&trans); |
| + if (!header_node.InitByIdLookup(local_session_syncid_)) { |
| + LOG(ERROR) << "Failed to load local session header node."; |
| + return; |
| + } |
| + header_node.SetSessionSpecifics(specifics); |
| } |
| -bool SessionModelAssociator::QuerySyncModel( |
| - const sync_api::BaseTransaction* trans, |
| - std::vector<const sync_pb::SessionSpecifics*>& specifics) { |
| - DCHECK(CalledOnValidThread()); |
| - sync_api::ReadNode root(trans); |
| - if (!root.InitByTagLookup(kSessionsTag)) { |
| - LOG(ERROR) << kNoSessionsFolderError; |
| - return false; |
| +bool SessionModelAssociator::ShouldSyncWindowType(Browser::Type type) { |
| + switch (type) { |
| + case Browser::TYPE_POPUP: |
| + return true; |
| + case Browser::TYPE_APP: |
| + return false; |
| + case Browser::TYPE_APP_POPUP: |
| + return false; |
| + case Browser::TYPE_DEVTOOLS: |
| + return false; |
| + case Browser::TYPE_APP_PANEL: |
| + return false; |
| + case Browser::TYPE_NORMAL: |
| + default: |
| + return true; |
| } |
| - sync_api::ReadNode current_machine(trans); |
| - int64 current_id = (current_machine.InitByClientTagLookup(syncable::SESSIONS, |
| - GetCurrentMachineTag())) ? current_machine.GetId() : sync_api::kInvalidId; |
| +} |
| - // Iterate through the nodes and populate the session model. |
| - int64 id = root.GetFirstChildId(); |
| - while (id != sync_api::kInvalidId) { |
| - sync_api::ReadNode sync_node(trans); |
| - if (!sync_node.InitByIdLookup(id)) { |
| - LOG(ERROR) << "Failed to fetch sync node for id " << id; |
| - return false; |
| - } |
| - if (id != current_id) { |
| - specifics.insert(specifics.end(), &sync_node.GetSessionSpecifics()); |
| - } |
| - id = sync_node.GetSuccessorId(); |
| +void SessionModelAssociator::ReassociateTabs( |
| + const std::vector<TabContents*>& tabs) { |
| + for (std::vector<TabContents*>::const_iterator i = tabs.begin(); |
| + i != tabs.end(); |
| + ++i) { |
| + ReassociateTab(**i); |
| } |
| - return true; |
| } |
| -bool SessionModelAssociator::GetSessionData( |
| - std::vector<ForeignSession*>* sessions) { |
| - DCHECK(CalledOnValidThread()); |
| +void SessionModelAssociator::ReassociateTab(const TabContents& tab) { |
| + if (!IsValidTab(tab)) |
| + return; |
| - // Build vector of sessions from specifics data |
| - for (std::vector<const sync_pb::SessionSpecifics*>::const_iterator i = |
| - specifics_.begin(); i != specifics_.end(); ++i) { |
| - // Only include sessions with open windows. |
| - if ((*i)->session_window_size() > 0) |
| - AppendForeignSessionFromSpecifics(*i, sessions); |
| + int64 sync_id; |
| + SessionID id(tab.controller().session_id()); |
| + |
| + TabLinksMap::const_iterator tablink = tab_map_.find(id); |
| + if (tablink == tab_map_.end()) { |
| + // This is a new tab, get a sync node for it. |
| + sync_id = GetFreeTabNode(); |
| + } else { |
| + // This tab is already associated with a sync node, reuse it. |
| + sync_id = tablink->second.sync_id(); |
| } |
| + Associate(&tab, sync_id); |
| +} |
| - return true; |
| +void SessionModelAssociator::Associate(const TabContents* tab, int64 sync_id) { |
| + TabLinks t(sync_id, tab); |
| + SessionID session_id = tab->controller().session_id(); |
| + tab_map_[session_id] = t; |
| + |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + WriteTabContentsToSyncModel(*tab, sync_id, &trans); |
| } |
| -void SessionModelAssociator::AppendForeignSessionFromSpecifics( |
| - const sync_pb::SessionSpecifics* specifics, |
| - std::vector<ForeignSession*>* session) { |
| - ForeignSession* foreign_session = new ForeignSession(); |
| - foreign_session->foreign_session_tag = specifics->session_tag(); |
| - session->insert(session->end(), foreign_session); |
| - for (int i = 0; i < specifics->session_window_size(); i++) { |
| - const sync_pb::SessionWindow* window = &specifics->session_window(i); |
| - SessionWindow* session_window = new SessionWindow(); |
| - PopulateSessionWindowFromSpecifics(session_window, window); |
| - foreign_session->windows.insert( |
| - foreign_session->windows.end(), session_window); |
| +bool SessionModelAssociator::WriteTabContentsToSyncModel( |
| + const TabContents& tab, |
| + int64 sync_id, |
| + sync_api::WriteTransaction* trans) { |
| + sync_api::WriteNode tab_node(trans); |
| + if (!tab_node.InitByIdLookup(sync_id)) { |
| + LOG(ERROR) << "Failed to look up tab node " << sync_id; |
| + return false; |
| } |
| -} |
| -// Fills the given vector with foreign session windows to restore. |
| -void SessionModelAssociator::AppendForeignSessionWithID(int64 id, |
| - std::vector<ForeignSession*>* session, sync_api::BaseTransaction* trans) { |
| - if (id == sync_api::kInvalidId) |
| - return; |
| - sync_api::ReadNode node(trans); |
| - if (!node.InitByIdLookup(id)) |
| - return; |
| - const sync_pb::SessionSpecifics* ref = &node.GetSessionSpecifics(); |
| - AppendForeignSessionFromSpecifics(ref, session); |
| -} |
| + sync_pb::SessionSpecifics session_s; |
| + session_s.set_session_tag(GetCurrentMachineTag()); |
| + sync_pb::SessionTab* tab_s = session_s.mutable_tab(); |
| -SessionService* SessionModelAssociator::GetSessionService() { |
| - DCHECK(sync_service_); |
| - Profile* profile = sync_service_->profile(); |
| - DCHECK(profile); |
| - SessionService* sessions_service = profile->GetSessionService(); |
| - DCHECK(sessions_service); |
| - return sessions_service; |
| -} |
| + SessionID::id_type tab_id = tab.controller().session_id().id(); |
| + tab_s->set_tab_id(tab_id); |
| + tab_s->set_window_id(tab.controller().window_id().id()); |
| + const int current_index = tab.controller().GetCurrentEntryIndex(); |
| + const int min_index = std::max(0, |
| + current_index - max_sync_navigation_count); |
| + const int max_index = std::min(current_index + max_sync_navigation_count, |
| + tab.controller().entry_count()); |
| + const int pending_index = tab.controller().pending_entry_index(); |
| + Browser* browser = BrowserList::FindBrowserWithID( |
| + tab.controller().window_id().id()); |
| + DCHECK(browser); |
| + int index_in_window = browser->tabstrip_model()->GetWrapperIndex(&tab); |
| + DCHECK(index_in_window != TabStripModel::kNoTab); |
| + tab_s->set_pinned(browser->tabstrip_model()->IsTabPinned(index_in_window)); |
| + if (tab.extension_app()) |
| + tab_s->set_extension_app_id(tab.extension_app()->id()); |
| + for (int i = min_index; i < max_index; ++i) { |
| + const NavigationEntry* entry = (i == pending_index) ? |
| + tab.controller().pending_entry() : tab.controller().GetEntryAtIndex(i); |
| + DCHECK(entry); |
| + if (entry->virtual_url().is_valid()) { |
| + if (i == max_index-1) { |
| + VLOG(1) << "Associating tab " << tab_id << " with sync id " << sync_id |
| + << " and url " << entry->virtual_url().possibly_invalid_spec(); |
| + } |
| + TabNavigation tab_nav; |
| + tab_nav.SetFromNavigationEntry(*entry); |
| + sync_pb::TabNavigation* nav_s = tab_s->add_navigation(); |
| + PopulateSessionSpecificsNavigation(&tab_nav, nav_s); |
| + } |
| + } |
| + tab_s->set_current_navigation_index(current_index); |
| -void SessionModelAssociator::InitializeCurrentMachineTag() { |
| - sync_api::WriteTransaction trans(sync_service_->backend()-> |
| - GetUserShareHandle()); |
| - syncable::Directory* dir = |
| - trans.GetWrappedWriteTrans()->directory(); |
| - |
| - // TODO(zea): We need a better way of creating a machine tag. The directory |
| - // kernel's cache_guid changes every time syncing is turned on and off. This |
| - // will result in session's associated with stale machine tags persisting on |
| - // the server since that tag will not be reused. Eventually this should |
| - // become some string identifiable to the user. (Home, Work, Laptop, etc.) |
| - // See issue 59672 |
| - current_machine_tag_ = "session_sync"; |
| - current_machine_tag_.append(dir->cache_guid()); |
| - VLOG(1) << "Creating machine tag: " << current_machine_tag_; |
| + tab_node.SetSessionSpecifics(session_s); |
| + return true; |
| } |
| -// See PopulateSessionSpecificsTab for use. May add functionality that includes |
| -// the state later. |
| +// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well? |
|
tim (not reviewing)
2010/12/15 21:14:00
so far we explicitly didn't want to do this but I
Nicolas Zea
2010/12/16 18:09:18
Done.
|
| void SessionModelAssociator::PopulateSessionSpecificsNavigation( |
| - const TabNavigation* navigation, sync_pb::TabNavigation* tab_navigation) { |
| + const TabNavigation* navigation, |
| + sync_pb::TabNavigation* tab_navigation) { |
| tab_navigation->set_index(navigation->index()); |
| tab_navigation->set_virtual_url(navigation->virtual_url().spec()); |
| tab_navigation->set_referrer(navigation->referrer().spec()); |
| @@ -328,125 +335,319 @@ |
| } |
| } |
| -// See PopulateSessionSpecificsWindow for use. |
| -void SessionModelAssociator::PopulateSessionSpecificsTab( |
| - const SessionTab* tab, sync_pb::SessionTab* session_tab) { |
| - session_tab->set_tab_visual_index(tab->tab_visual_index); |
| - session_tab->set_current_navigation_index( |
| - tab->current_navigation_index); |
| - session_tab->set_pinned(tab->pinned); |
| - session_tab->set_extension_app_id(tab->extension_app_id); |
| - for (std::vector<TabNavigation>::const_iterator i3 = |
| - tab->navigations.begin(); i3 != tab->navigations.end(); ++i3) { |
| - const TabNavigation navigation = *i3; |
| - sync_pb::TabNavigation* tab_navigation = |
| - session_tab->add_navigation(); |
| - PopulateSessionSpecificsNavigation(&navigation, tab_navigation); |
| +void SessionModelAssociator::Disassociate(int64 sync_id) { |
| + // TODO(zea): we will need this once we support deleting foreign sessions. |
|
tim (not reviewing)
2010/12/15 21:14:00
is it called anywhere?
add a NOTIMPLEMENTED() ?
Nicolas Zea
2010/12/16 18:09:18
Done.
|
| +} |
| + |
| +bool SessionModelAssociator::AssociateModels() { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + // Ensure that we disassociated properly, otherwise memory might leak. |
| + DCHECK(foreign_tab_map_.empty()); |
| + DCHECK(foreign_session_map_.empty()); |
| + |
| + // Make sure we have a machine tag. |
| + if (current_machine_tag_.empty()) |
| + InitializeCurrentMachineTag(); // Creates a syncable::BaseTransaction. |
| + |
| + tab_pool_fp_ = -1; |
| + local_session_syncid_ = sync_api::kInvalidId; |
| + |
| + // Read any available foreign sessions and load any session data we may have. |
| + // If we don't have any local session data in the db, create a header node. |
| + { |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + |
| + sync_api::ReadNode root(&trans); |
| + if (!root.InitByTagLookup(kSessionsTag)) { |
| + LOG(ERROR) << kNoSessionsFolderError; |
| + return false; |
| + } |
| + |
| + UpdateAssociationsFromSyncModel(root, &trans); |
| + |
| + if (local_session_syncid_ == sync_api::kInvalidId) { |
| + // The sync db didn't have a header node for us, we need to create one. |
| + sync_api::WriteNode write_node(&trans); |
| + if (!write_node.InitUniqueByCreation(syncable::SESSIONS, root, |
| + current_machine_tag_)) { |
| + LOG(ERROR) << "Failed to create sessions header sync node."; |
| + return false; |
| + } |
| + write_node.SetTitle(UTF8ToWide(current_machine_tag_)); |
| + local_session_syncid_ = write_node.GetId(); |
| + } |
| } |
| + |
| + // Check if anything has changed on the client side. |
| + UpdateSyncModelDataFromClient(); |
| + |
| + VLOG(1) << "Session models associated."; |
| + |
| + return true; |
| } |
| -// Called when populating session specifics to send to the sync model, called |
| -// when associating models, or updating the sync model. |
| -void SessionModelAssociator::PopulateSessionSpecificsWindow( |
| - const SessionWindow* window, sync_pb::SessionWindow* session_window) { |
| - session_window->set_selected_tab_index(window->selected_tab_index); |
| - if (window->type == 1) { |
| - session_window->set_browser_type( |
| - sync_pb::SessionWindow_BrowserType_TYPE_NORMAL); |
| - } else { |
| - session_window->set_browser_type( |
| - sync_pb::SessionWindow_BrowserType_TYPE_POPUP); |
| +bool SessionModelAssociator::DisassociateModels() { |
| + DeleteForeignSessions(); |
| + tab_map_.clear(); |
| + tab_syncid_pool_.clear(); |
| + tab_pool_fp_ = -1; |
| + local_session_syncid_ = sync_api::kInvalidId; |
| + |
| + // There is no local model stored with which to disassociate, just notify |
| + // foreign session handlers. |
| + NotificationService::current()->Notify( |
| + NotificationType::FOREIGN_SESSION_DISABLED, |
| + NotificationService::AllSources(), |
| + NotificationService::NoDetails()); |
| + return true; |
| +} |
| + |
| +void SessionModelAssociator::InitializeCurrentMachineTag() { |
| + sync_api::WriteTransaction trans(sync_service_->backend()-> |
| + GetUserShareHandle()); |
| + syncable::Directory* dir = trans.GetWrappedWriteTrans()->directory(); |
| + |
| + // TODO(zea): We need a better way of creating a machine tag. The directory |
|
tim (not reviewing)
2010/12/15 21:14:00
reference bug #
Nicolas Zea
2010/12/16 18:09:18
See end of comment paragraph. I went ahead and tur
|
| + // kernel's cache_guid changes every time syncing is turned on and off. This |
| + // will result in session's associated with stale machine tags persisting on |
| + // the server since that tag will not be reused. Eventually this should |
| + // become some string identifiable to the user. (Home, Work, Laptop, etc.) |
| + // See issue 59672 |
| + current_machine_tag_ = "session_sync"; |
| + current_machine_tag_.append(dir->cache_guid()); |
| + VLOG(1) << "Creating machine tag: " << current_machine_tag_; |
| +} |
| + |
| +bool SessionModelAssociator::UpdateAssociationsFromSyncModel( |
| + const sync_api::ReadNode& root, |
| + const sync_api::BaseTransaction* trans) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + // Iterate through the nodes and associate any foriegn sessions. |
| + int64 id = root.GetFirstChildId(); |
| + while (id != sync_api::kInvalidId) { |
| + sync_api::ReadNode sync_node(trans); |
| + if (!sync_node.InitByIdLookup(id)) { |
| + LOG(ERROR) << "Failed to fetch sync node for id " << id; |
| + return false; |
| } |
| - for (std::vector<SessionTab*>::const_iterator i2 = window->tabs.begin(); |
| - i2 != window->tabs.end(); ++i2) { |
| - const SessionTab* tab = *i2; |
| - if (tab->navigations.at(tab->current_navigation_index).virtual_url() == |
| - GURL(chrome::kChromeUINewTabURL)) { |
| - continue; |
| + |
| + const sync_pb::SessionSpecifics& specifics = |
| + sync_node.GetSessionSpecifics(); |
| + const int64 modification_time = sync_node.GetModificationTime(); |
| + if (specifics.session_tag() != GetCurrentMachineTag()) { |
| + if (!AssociateForeignSpecifics(specifics, modification_time)) { |
| + return false; |
| } |
| - sync_pb::SessionTab* session_tab = session_window->add_session_tab(); |
| - PopulateSessionSpecificsTab(tab, session_tab); |
| + } else if (id != local_session_syncid_) { |
| + // This is previously stored local session information. |
| + if (specifics.has_header()) { |
| + DCHECK(local_session_syncid_ == sync_api::kInvalidId); |
| + |
| + // This is our previous header node, reuse it. |
| + local_session_syncid_ = id; |
| + } else { |
| + DCHECK(specifics.has_tab()); |
| + |
| + // This is a tab node. We want to track these to reuse them in our free |
| + // tab node pool. They will be overwritten eventually, so need to do |
| + // anything else. |
| + tab_syncid_pool_.push_back(id); |
| + tab_pool_fp_++; |
| + } |
| } |
| + |
| + id = sync_node.GetSuccessorId(); |
| + } |
| + |
| + // After updating from sync model all tabid's should be free. |
| + DCHECK(tab_pool_fp_ == static_cast<int64>(tab_syncid_pool_.size())-1); |
| + |
| + return true; |
| } |
| -bool SessionModelAssociator::WindowHasNoTabsToSync( |
| - const SessionWindow* window) { |
| - int num_populated = 0; |
| - for (std::vector<SessionTab*>::const_iterator i = window->tabs.begin(); |
| - i != window->tabs.end(); ++i) { |
| - const SessionTab* tab = *i; |
| - if (tab->navigations.at(tab->current_navigation_index).virtual_url() == |
| - GURL(chrome::kChromeUINewTabURL)) { |
| - continue; |
| +bool SessionModelAssociator::AssociateForeignSpecifics( |
| + const sync_pb::SessionSpecifics& specifics, |
| + const int64 modification_time) { |
| + std::string foreign_session_tag = specifics.session_tag(); |
| + DCHECK(foreign_session_tag != GetCurrentMachineTag() || |
| + sync_service_->cros_user() == "test user"); // For tests. |
| + |
| + if (foreign_tab_map_.find(foreign_session_tag) == foreign_tab_map_.end()) { |
| + foreign_tab_map_[foreign_session_tag] = new IDToSessTabMap(); |
| + } |
| + if (specifics.has_header()) { |
| + // Read in the header data for this foreign session. |
| + // Header data contains window information and ordered tab id's for each |
| + // window. |
| + |
| + // Load (or create) the ForeignSession object for this client. |
| + scoped_ptr<ForeignSession> foreign_session; |
| + if (foreign_session_map_.find(foreign_session_tag) != |
| + foreign_session_map_.end()) { |
| + foreign_session.reset(foreign_session_map_[foreign_session_tag]); |
| + } else { |
| + foreign_session.reset(new ForeignSession); |
| + foreign_session->foreign_session_tag = foreign_session_tag; |
| } |
| - num_populated++; |
| + |
| + const sync_pb::SessionHeader& header = specifics.header(); |
| + foreign_session->windows.reserve(header.window_size()); |
| + VLOG(1) << "Associating " << foreign_session_tag << " with " << |
| + header.window_size() << " windows."; |
| + size_t i; |
| + for (i = 0; i < (size_t)header.window_size(); ++i) { |
| + if (i >= foreign_session->windows.size()) { |
| + // This a new window, create it. |
| + foreign_session->windows.push_back(new SessionWindow()); |
| + } |
| + const sync_pb::SessionWindow& window_s = header.window(i); |
| + PopulateSessionWindowFromSpecifics(foreign_session_tag, |
| + window_s, |
| + modification_time, |
| + foreign_session->windows[i]); |
| + } |
| + // Remove any remaining windows (in case windows were closed) |
| + for (; i < foreign_session->windows.size(); ++i) { |
| + delete foreign_session->windows[i]; |
| + } |
| + foreign_session->windows.resize(header.window_size()); |
| + |
| + foreign_session_map_[foreign_session_tag] = foreign_session.release(); |
| + } else if (specifics.has_tab()) { |
| + const sync_pb::SessionTab& tab_s = specifics.tab(); |
| + SessionID tab_id(tab_s.tab_id()); |
| + scoped_ptr<SessionTab> tab; |
| + if (foreign_tab_map_[foreign_session_tag]->find(tab_id) != |
| + foreign_tab_map_[foreign_session_tag]->end()) { |
| + // We've already seen the header for this client, and created the |
| + // appropriate tab objects. Go ahead and fill it with the tab info. |
| + tab.reset((*foreign_tab_map_[foreign_session_tag])[tab_id]); |
| + unmapped_tabs_.erase(tab.get()); |
| + VLOG(1) << "Associating " << foreign_session_tag << "'s seen tab " << |
| + tab_id.id() << " at " << tab.get(); |
| + } else { |
| + // Create the tab object, and associate it with the tab id in |
| + // |foreign_tab_map_|. It will get associated with a window when we |
| + // associate the header node for this client. |
| + tab.reset(new SessionTab()); |
| + (*foreign_tab_map_[foreign_session_tag])[tab_id] = tab.get(); |
| + unmapped_tabs_.insert(tab.get()); |
| + VLOG(1) << "Associating " << foreign_session_tag << "'s new tab " << |
| + tab_id.id() << " at " << tab.get(); |
| + } |
| + PopulateSessionTabFromSpecifics(tab_s, modification_time, tab.release()); |
| + } else { |
| + NOTREACHED(); |
| + return false; |
| } |
| - if (num_populated == 0) |
| - return true; |
| - return false; |
| + |
| + return true; |
| } |
| -void SessionModelAssociator::OnGotSession(int handle, |
| - std::vector<SessionWindow*>* windows) { |
| - sync_pb::SessionSpecifics specifics; |
| - // Set the tag, then iterate through the vector of windows, extracting the |
| - // window data, along with the tabs data and tab navigation data to populate |
| - // the session specifics. |
| - specifics.set_session_tag(GetCurrentMachineTag()); |
| - FillSpecificsFromSessions(windows, &specifics); |
| - bool has_nodes = false; |
| - if (!SyncModelHasUserCreatedNodes(&has_nodes)) |
| - return; |
| - if (specifics.session_window_size() == 0 && has_nodes) |
| - return; |
| - sync_api::WriteTransaction trans( |
| - sync_service_->backend()->GetUserShareHandle()); |
| - sync_api::ReadNode root(&trans); |
| - if (!root.InitByTagLookup(kSessionsTag)) { |
| - LOG(ERROR) << kNoSessionsFolderError; |
| - return; |
| +void SessionModelAssociator::DisassociateForeignSession( |
| + const std::string& foreign_session_tag) { |
| + ForeignSessionMap::iterator iter = |
| + foreign_session_map_.find(foreign_session_tag); |
| + if (iter != foreign_session_map_.end()) { |
| + delete iter->second; // Delete the ForeignSession object. |
| + foreign_session_map_.erase(iter); |
| } |
| - UpdateSyncModel(&specifics, &trans, &root); |
| } |
| -void SessionModelAssociator::FillSpecificsFromSessions( |
| - std::vector<SessionWindow*>* windows, |
| - sync_pb::SessionSpecifics* session) { |
| - for (std::vector<SessionWindow*>::const_iterator i = windows->begin(); |
| - i != windows->end(); ++i) { |
| - const SessionWindow* window = *i; |
| - if (WindowHasNoTabsToSync(window)) { |
| - continue; |
| +void SessionModelAssociator::PopulateSessionWindowFromSpecifics( |
| + std::string foreign_session_tag, |
| + const sync_pb::SessionWindow& specifics, |
| + int64 mtime, |
| + SessionWindow* session_window) { |
| + if (specifics.has_window_id()) |
| + session_window->window_id.set_id(specifics.window_id()); |
| + if (specifics.has_selected_tab_index()) |
| + session_window->selected_tab_index = specifics.selected_tab_index(); |
| + if (specifics.has_browser_type()) { |
| + if (specifics.browser_type() == |
| + sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) { |
| + session_window->type = 1; |
| + } else { |
| + session_window->type = 2; |
| } |
| - sync_pb::SessionWindow* session_window = session->add_session_window(); |
| - PopulateSessionSpecificsWindow(window, session_window); |
| } |
| + session_window->timestamp = base::Time::FromInternalValue(mtime); |
| + session_window->tabs.resize(specifics.tab_size()); |
| + for (int i = 0; i < specifics.tab_size(); i++) { |
| + SessionID tab_id(specifics.tab(i)); |
| + scoped_ptr<SessionTab> tab; |
| + IDToSessTabMap::iterator iter = |
| + foreign_tab_map_[foreign_session_tag]->find(tab_id); |
| + if (iter != (*foreign_tab_map_[foreign_session_tag]).end()) { |
| + tab.reset(iter->second); |
| + unmapped_tabs_.erase(tab.get()); |
| + VLOG(1) << "Associating " << foreign_session_tag << "'s seen tab " << |
| + tab_id.id() << " at " << tab.get() << " with window " << |
| + specifics.window_id(); |
| + } else { |
| + tab.reset(new SessionTab()); |
| + (*foreign_tab_map_[foreign_session_tag])[tab_id] = tab.get(); |
| + unmapped_tabs_.insert(tab.get()); |
| + VLOG(1) << "Associating " << foreign_session_tag << "'s new tab " << |
| + tab_id.id() << " at " << tab.get() << " with window " << |
| + specifics.window_id(); |
| + } |
| + session_window->tabs[i] = tab.release(); |
| + } |
| } |
| +void SessionModelAssociator::PopulateSessionTabFromSpecifics( |
| + const sync_pb::SessionTab& specifics, |
| + const int64 mtime, |
| + SessionTab* tab) { |
| + if (specifics.has_tab_id()) |
| + tab->tab_id.set_id(specifics.tab_id()); |
| + if (specifics.has_window_id()) |
| + tab->window_id.set_id(specifics.window_id()); |
| + if (specifics.has_tab_visual_index()) |
| + tab->tab_visual_index = specifics.tab_visual_index(); |
| + if (specifics.has_current_navigation_index()) |
| + tab->current_navigation_index = specifics.current_navigation_index(); |
| + if (specifics.has_pinned()) |
| + tab->pinned = specifics.pinned(); |
| + if (specifics.has_extension_app_id()) |
| + tab->extension_app_id = specifics.extension_app_id(); |
| + tab->timestamp = base::Time::FromInternalValue(mtime); |
| + tab->navigations.clear(); // In case we are reusing a previous SessionTab. |
| + for (int i = 0; i < specifics.navigation_size(); i++) { |
| + AppendSessionTabNavigation(specifics.navigation(i), &tab->navigations); |
| + } |
| +} |
| + |
| void SessionModelAssociator::AppendSessionTabNavigation( |
| - std::vector<TabNavigation>* navigations, |
| - const sync_pb::TabNavigation* navigation) { |
| + const sync_pb::TabNavigation& specifics, |
| + std::vector<TabNavigation>* navigations) { |
| int index = 0; |
| GURL virtual_url; |
| GURL referrer; |
| string16 title; |
| std::string state; |
| PageTransition::Type transition(PageTransition::LINK); |
| - if (navigation->has_index()) |
| - index = navigation->index(); |
| - if (navigation->has_virtual_url()) { |
| - GURL gurl(navigation->virtual_url()); |
| + if (specifics.has_index()) |
| + index = specifics.index(); |
| + if (specifics.has_virtual_url()) { |
| + GURL gurl(specifics.virtual_url()); |
| virtual_url = gurl; |
| } |
| - if (navigation->has_referrer()) { |
| - GURL gurl(navigation->referrer()); |
| + if (specifics.has_referrer()) { |
| + GURL gurl(specifics.referrer()); |
| referrer = gurl; |
| } |
| - if (navigation->has_title()) |
| - title = UTF8ToUTF16(navigation->title()); |
| - if (navigation->has_page_transition() || |
| - navigation->has_navigation_qualifier()) { |
| - switch (navigation->page_transition()) { |
| + if (specifics.has_title()) |
| + title = UTF8ToUTF16(specifics.title()); |
| + if (specifics.has_state()) |
| + state = specifics.state(); |
| + if (specifics.has_page_transition() || |
| + specifics.has_navigation_qualifier()) { |
| + switch (specifics.page_transition()) { |
| case sync_pb::TabNavigation_PageTransition_LINK: |
| transition = PageTransition::LINK; |
| break; |
| @@ -487,7 +688,7 @@ |
| transition = PageTransition::CHAIN_END; |
| break; |
| default: |
| - switch (navigation->navigation_qualifier()) { |
| + switch (specifics.navigation_qualifier()) { |
| case sync_pb:: |
| TabNavigation_PageTransitionQualifier_CLIENT_REDIRECT: |
| transition = PageTransition::CLIENT_REDIRECT; |
| @@ -506,67 +707,293 @@ |
| navigations->insert(navigations->end(), tab_navigation); |
| } |
| -void SessionModelAssociator::PopulateSessionTabFromSpecifics( |
| - SessionTab* session_tab, |
| - const sync_pb::SessionTab* tab, SessionID id) { |
| - session_tab->window_id = id; |
| - SessionID tabID; |
| - session_tab->tab_id = tabID; |
| - if (tab->has_tab_visual_index()) |
| - session_tab->tab_visual_index = tab->tab_visual_index(); |
| - if (tab->has_current_navigation_index()) { |
| - session_tab->current_navigation_index = |
| - tab->current_navigation_index(); |
| +void SessionModelAssociator::UpdateSyncModelDataFromClient() { |
| + DCHECK(CalledOnValidThread()); |
| + // TODO(zea): the logic for determining if we want to sync and the loading of |
| + // the previous session should go here. We can probably reuse the code for |
| + // loading the current session from the old session implementation. |
| + // SessionService::SessionCallback* callback = |
| + // NewCallback(this, &SessionModelAssociator::OnGotSession); |
| + // GetSessionService()->GetCurrentSession(&consumer_, callback); |
| + |
| + // Associate all open windows and their tabs. |
| + ReassociateWindows(true); |
| +} |
| + |
| +int64 SessionModelAssociator::GetFreeTabNode() { |
| + if (tab_pool_fp_ == -1) { |
| + // Tab pool has no free nodes, allocate new one. |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + sync_api::ReadNode root(&trans); |
| + if (!root.InitByTagLookup(kSessionsTag)) { |
| + LOG(ERROR) << kNoSessionsFolderError; |
| + return 0; |
| + } |
| + size_t tab_node_id = tab_syncid_pool_.size(); |
| + std::string tab_node_tag = TabIdToTag(tab_node_id); |
| + sync_api::WriteNode tab_node(&trans); |
| + if (!tab_node.InitUniqueByCreation(syncable::SESSIONS, root, |
| + tab_node_tag)) { |
| + LOG(ERROR) << "Could not create node for session " << |
| + GetCurrentMachineTag(); |
| + return -1; |
| + } |
| + tab_node.SetTitle(UTF8ToWide(tab_node_tag)); |
| + |
| + // Grow the pool by 1 since we created a new node. We don't actually need |
| + // to put the node's id in the pool now, since the pool is still empty. |
| + // The id will be added when that tab is closed and the node is freed. |
| + tab_syncid_pool_.resize(tab_node_id + 1); |
| + VLOG(1) << "Adding sync node " << tab_node.GetId() << " to tab syncid pool"; |
| + return tab_node.GetId(); |
| + } else { |
| + // There are nodes available, grab next free and decrement free pointer. |
| + return tab_syncid_pool_[static_cast<size_t>(tab_pool_fp_--)]; |
| } |
| - if (tab->has_pinned()) |
| - session_tab->pinned = tab->pinned(); |
| - if (tab->has_extension_app_id()) |
| - session_tab->extension_app_id = tab->extension_app_id(); |
| - for (int i3 = 0; i3 < tab->navigation_size(); i3++) { |
| - const sync_pb::TabNavigation* navigation = &tab->navigation(i3); |
| - AppendSessionTabNavigation(&session_tab->navigations, navigation); |
| +} |
| + |
| +void SessionModelAssociator::FreeTabNode(int64 sync_id) { |
| + // Pool size should always match # of free tab nodes. |
| + DCHECK(tab_pool_fp_ < static_cast<int64>(tab_syncid_pool_.size())); |
| + tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id; |
| +} |
| + |
| +bool SessionModelAssociator::GetAllForeignSessions( |
| + std::vector<const ForeignSession*>* sessions) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + // Build vector of sessions from our foreign session map. |
| + for (ForeignSessionMap::const_iterator i = |
| + foreign_session_map_.begin(); i != foreign_session_map_.end(); ++i) { |
| + // Only include sessions with open tabs. |
| + ForeignSession* foreign_session = i->second; |
| + if (foreign_session->windows.size() > 0 && |
| + !WindowHasNoTabsToSync(*foreign_session->windows[0])) |
| + sessions->push_back(foreign_session); |
| } |
| + |
| + return true; |
| } |
| -void SessionModelAssociator::PopulateSessionWindowFromSpecifics( |
| - SessionWindow* session_window, const sync_pb::SessionWindow* window) { |
| - SessionID id; |
| - session_window->window_id = id; |
| - if (window->has_selected_tab_index()) |
| - session_window->selected_tab_index = window->selected_tab_index(); |
| - if (window->has_browser_type()) { |
| - if (window->browser_type() == |
| - sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) { |
| - session_window->type = 1; |
| - } else { |
| - session_window->type = 2; |
| +bool SessionModelAssociator::GetForeignSession( |
| + const std::string& tag, |
| + std::vector<SessionWindow*>* sessions) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + ForeignSessionMap::iterator iter = foreign_session_map_.find(tag); |
| + if (iter == foreign_session_map_.end()) |
| + return false; |
| + *sessions = iter->second->windows; |
| + |
| + return true; |
| +} |
| + |
| +bool SessionModelAssociator::GetForeignTab( |
| + const std::string& tag, |
| + const SessionID::id_type tab_id, |
| + const SessionTab** tab) { |
| + if (foreign_tab_map_.find(tag) == foreign_tab_map_.end()) |
| + return false; |
| + SessionID id(tab_id); |
| + if (foreign_tab_map_[tag]->find(id) == foreign_tab_map_[tag]->end()) |
| + return false; |
| + *tab = (*foreign_tab_map_[tag])[id]; |
| + return true; |
| +} |
| + |
| +bool SessionModelAssociator::WindowHasNoTabsToSync( |
| + const SessionWindow& window) { |
| + int num_populated = 0; |
| + for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin(); |
| + i != window.tabs.end(); ++i) { |
| + const SessionTab* tab = *i; |
| + if (IsValidSessionTab(*tab)) |
| + num_populated++; |
| + } |
| + if (num_populated == 0) |
| + return true; |
| + return false; |
| +} |
| + |
| +// Valid local tab? |
| +bool SessionModelAssociator::IsValidTab(const TabContents& tab) { |
| + if ((tab.profile() == sync_service_->profile() || |
| + sync_service_->profile() == NULL)) { |
| + const NavigationEntry* entry = tab.controller().GetActiveEntry(); |
| + if (!entry) |
| + return false; |
| + if (entry->virtual_url().is_valid() && |
| + (entry->virtual_url() != GURL(chrome::kChromeUINewTabURL) || |
| + tab.controller().entry_count() > 1)) { |
| + return true; |
| } |
| } |
| - for (int i = 0; i < window->session_tab_size(); i++) { |
| - const sync_pb::SessionTab& tab = window->session_tab(i); |
| - SessionTab* session_tab = new SessionTab(); |
| - PopulateSessionTabFromSpecifics(session_tab, &tab, id); |
| - session_window->tabs.insert(session_window->tabs.end(), session_tab); |
| + return false; |
| +} |
| + |
| +// Valid foreign tab? |
| +bool SessionModelAssociator::IsValidSessionTab(const SessionTab& tab) { |
| + if (tab.navigations.empty()) |
| + return false; |
| + int selected_index = tab.current_navigation_index; |
| + selected_index = std::max( |
| + 0, |
| + std::min(selected_index, |
| + static_cast<int>(tab.navigations.size() - 1))); |
| + if (selected_index == 0 && |
| + tab.navigations.size() == 1 && |
| + tab.navigations.at(selected_index).virtual_url() == |
| + GURL(chrome::kChromeUINewTabURL)) { |
| + // This is a new tab with no further history, skip. |
| + return false; |
| } |
| + return true; |
| } |
| -bool SessionModelAssociator::UpdateSyncModel( |
| - sync_pb::SessionSpecifics* session_data, |
| - sync_api::WriteTransaction* trans, |
| - const sync_api::ReadNode* root) { |
| - const std::string id = session_data->session_tag(); |
| - sync_api::WriteNode write_node(trans); |
| - if (!write_node.InitByClientTagLookup(syncable::SESSIONS, id)) { |
| - sync_api::WriteNode create_node(trans); |
| - if (!create_node.InitUniqueByCreation(syncable::SESSIONS, *root, id)) { |
| - LOG(ERROR) << "Could not create node for session " << id; |
| +// ========================================================================== |
| +// The following methods are not currently used but will likely become useful |
| +// if we choose to sync the previous browser session. |
|
tim (not reviewing)
2010/12/15 21:14:00
previous? is there a bug#?
Nicolas Zea
2010/12/16 18:09:18
No, should I make one? this refers to what Travis
|
| + |
| +SessionService* SessionModelAssociator::GetSessionService() { |
| + DCHECK(sync_service_); |
| + Profile* profile = sync_service_->profile(); |
| + DCHECK(profile); |
| + SessionService* sessions_service = profile->GetSessionService(); |
| + DCHECK(sessions_service); |
| + return sessions_service; |
| +} |
| + |
| +void SessionModelAssociator::OnGotSession( |
| + int handle, |
| + std::vector<SessionWindow*>* windows) { |
| + DCHECK(local_session_syncid_); |
| + |
| + sync_pb::SessionSpecifics specifics; |
| + specifics.set_session_tag(GetCurrentMachineTag()); |
| + sync_pb::SessionHeader* header_s = specifics.mutable_header(); |
| + PopulateSessionSpecificsHeader(*windows, header_s); |
| + |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + sync_api::ReadNode root(&trans); |
| + if (!root.InitByTagLookup(kSessionsTag)) { |
| + LOG(ERROR) << kNoSessionsFolderError; |
| + return; |
| + } |
| + |
| + sync_api::WriteNode header_node(&trans); |
| + if (!header_node.InitByIdLookup(local_session_syncid_)) { |
| + LOG(ERROR) << "Failed to load local session header node."; |
| + return; |
| + } |
| + |
| + header_node.SetSessionSpecifics(specifics); |
| +} |
| + |
| +void SessionModelAssociator::PopulateSessionSpecificsHeader( |
| + const std::vector<SessionWindow*>& windows, |
| + sync_pb::SessionHeader* header_s) { |
| + |
| + // Iterate through the vector of windows, extracting the window data, along |
| + // with the tab data to populate the session specifics. |
| + for (size_t i = 0; i < windows.size(); ++i) { |
| + if (WindowHasNoTabsToSync(*(windows[i]))) |
| + continue; |
| + sync_pb::SessionWindow* window_s = header_s->add_window(); |
| + PopulateSessionSpecificsWindow(*(windows[i]), window_s); |
| + if (!SyncLocalWindowToSyncModel(*(windows[i]))) |
| + return; |
| + } |
| +} |
| + |
| +// Called when populating session specifics to send to the sync model, called |
| +// when associating models, or updating the sync model. |
| +void SessionModelAssociator::PopulateSessionSpecificsWindow( |
| + const SessionWindow& window, |
| + sync_pb::SessionWindow* session_window) { |
| + session_window->set_window_id(window.window_id.id()); |
| + session_window->set_selected_tab_index(window.selected_tab_index); |
| + if (window.type == Browser::TYPE_NORMAL) { |
| + session_window->set_browser_type( |
| + sync_pb::SessionWindow_BrowserType_TYPE_NORMAL); |
| + } else if (window.type == Browser::TYPE_POPUP) { |
| + session_window->set_browser_type( |
| + sync_pb::SessionWindow_BrowserType_TYPE_POPUP); |
| + } else { |
| + // ignore |
| + LOG(WARNING) << "Session Sync unable to handle windows of type" << |
| + window.type; |
| + return; |
| + } |
| + for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin(); |
| + i != window.tabs.end(); ++i) { |
| + const SessionTab* tab = *i; |
| + if (!IsValidSessionTab(*tab)) |
| + continue; |
| + session_window->add_tab(tab->tab_id.id()); |
| + } |
| +} |
| + |
| +bool SessionModelAssociator::SyncLocalWindowToSyncModel( |
| + const SessionWindow& window) { |
| + DCHECK(tab_map_.empty()); |
| + for (size_t i = 0; i < window.tabs.size(); ++i) { |
| + SessionTab* tab = window.tabs[i]; |
| + int64 id = GetFreeTabNode(); |
| + if (id == -1) { |
| + LOG(ERROR) << "Failed to find/generate free sync node for tab."; |
| return false; |
| } |
| - create_node.SetSessionSpecifics(*session_data); |
| - return true; |
| + |
| + sync_api::WriteTransaction trans( |
| + sync_service_->backend()->GetUserShareHandle()); |
| + if (!WriteSessionTabToSyncModel(*tab, id, &trans)) { |
| + return false; |
| + } |
| + |
| + TabLinks t(id, tab); |
| + tab_map_[tab->tab_id] = t; |
| } |
| - write_node.SetSessionSpecifics(*session_data); |
| return true; |
| } |
| +bool SessionModelAssociator::WriteSessionTabToSyncModel( |
| + const SessionTab& tab, |
| + const int64 sync_id, |
| + sync_api::WriteTransaction* trans) { |
| + sync_api::WriteNode tab_node(trans); |
| + if (!tab_node.InitByIdLookup(sync_id)) { |
| + LOG(ERROR) << "Failed to look up tab node " << sync_id; |
| + return false; |
| + } |
| + |
| + sync_pb::SessionSpecifics specifics; |
| + specifics.set_session_tag(GetCurrentMachineTag()); |
| + sync_pb::SessionTab* tab_s = specifics.mutable_tab(); |
| + PopulateSessionSpecificsTab(tab, tab_s); |
| + tab_node.SetSessionSpecifics(specifics); |
| + return true; |
| +} |
| + |
| +// See PopulateSessionSpecificsWindow for use. |
| +void SessionModelAssociator::PopulateSessionSpecificsTab( |
| + const SessionTab& tab, |
| + sync_pb::SessionTab* session_tab) { |
| + session_tab->set_tab_id(tab.tab_id.id()); |
| + session_tab->set_window_id(tab.window_id.id()); |
| + session_tab->set_tab_visual_index(tab.tab_visual_index); |
| + session_tab->set_current_navigation_index( |
| + tab.current_navigation_index); |
| + session_tab->set_pinned(tab.pinned); |
| + session_tab->set_extension_app_id(tab.extension_app_id); |
| + for (std::vector<TabNavigation>::const_iterator i = |
| + tab.navigations.begin(); i != tab.navigations.end(); ++i) { |
| + const TabNavigation navigation = *i; |
| + sync_pb::TabNavigation* tab_navigation = |
| + session_tab->add_navigation(); |
| + PopulateSessionSpecificsNavigation(&navigation, tab_navigation); |
| + } |
| +} |
| + |
| } // namespace browser_sync |