| Index: chrome/browser/sync/glue/session_model_associator.cc
|
| ===================================================================
|
| --- chrome/browser/sync/glue/session_model_associator.cc (revision 70604)
|
| +++ 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,15 +24,18 @@
|
| 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_(sync_service),
|
| + local_session_syncid_(sync_api::kInvalidId),
|
| + sync_service_(sync_service) {
|
| DCHECK(CalledOnValidThread());
|
| DCHECK(sync_service_);
|
| }
|
| @@ -37,80 +44,10 @@
|
| DCHECK(CalledOnValidThread());
|
| }
|
|
|
| -bool SessionModelAssociator::AssociateModels() {
|
| - DCHECK(CalledOnValidThread());
|
| -
|
| - // Make sure we have a machine tag.
|
| - if (current_machine_tag_.empty())
|
| - InitializeCurrentMachineTag(); // Creates a syncable::BaseTransaction.
|
| -
|
| - {
|
| - // 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;
|
| -}
|
| -
|
| -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::InitSyncNodeFromChromeId(
|
| const std::string& id,
|
| sync_api::BaseNode* sync_node) {
|
| + NOTREACHED();
|
| return false;
|
| }
|
|
|
| @@ -131,138 +68,212 @@
|
| return true;
|
| }
|
|
|
| -std::string SessionModelAssociator::GetCurrentMachineTag() {
|
| - DCHECK(!current_machine_tag_.empty());
|
| - return current_machine_tag_;
|
| +int64 SessionModelAssociator::GetSyncIdFromChromeId(const size_t& id) {
|
| + DCHECK(CalledOnValidThread());
|
| + return GetSyncIdFromSessionTag(TabIdToTag(GetCurrentMachineTag(), id));
|
| }
|
|
|
| -void SessionModelAssociator::UpdateSyncModelDataFromClient() {
|
| +int64 SessionModelAssociator::GetSyncIdFromSessionTag(const std::string& tag) {
|
| 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);
|
| + 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();
|
| }
|
| -
|
| -// 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) {
|
| +void SessionModelAssociator::ReassociateWindows(bool reload_tabs) {
|
| DCHECK(CalledOnValidThread());
|
| + sync_pb::SessionSpecifics specifics;
|
| + specifics.set_session_tag(GetCurrentMachineTag());
|
| + sync_pb::SessionHeader* header_s = specifics.mutable_header();
|
|
|
| - // Rebuild specifics_ vector
|
| - specifics_.clear();
|
| - if (!QuerySyncModel(trans, specifics_)) {
|
| - LOG(ERROR) << "SessionModelAssociator failed to updated from sync model";
|
| - return false;
|
| + 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);
|
| + }
|
| +
|
| + // 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) {
|
| +// Static.
|
| +bool SessionModelAssociator::ShouldSyncWindowType(const 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;
|
| + }
|
| +}
|
| +
|
| +void SessionModelAssociator::ReassociateTabs(
|
| + const std::vector<TabContents*>& tabs) {
|
| DCHECK(CalledOnValidThread());
|
| - sync_api::ReadNode root(trans);
|
| - if (!root.InitByTagLookup(kSessionsTag)) {
|
| - LOG(ERROR) << kNoSessionsFolderError;
|
| - return false;
|
| + for (std::vector<TabContents*>::const_iterator i = tabs.begin();
|
| + i != tabs.end();
|
| + ++i) {
|
| + ReassociateTab(**i);
|
| }
|
| - 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;
|
| +void SessionModelAssociator::ReassociateTab(const TabContents& tab) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (!IsValidTab(tab))
|
| + return;
|
| +
|
| + int64 sync_id;
|
| + SessionID::id_type id = tab.controller().session_id().id();
|
| + if (tab.is_being_destroyed()) {
|
| + // This tab is closing.
|
| + TabLinksMap::iterator tab_iter = tab_map_.find(id);
|
| + if (tab_iter == tab_map_.end()) {
|
| + // We aren't tracking this tab (for example, sync setting page).
|
| + return;
|
| }
|
| - if (id != current_id) {
|
| - specifics.insert(specifics.end(), &sync_node.GetSessionSpecifics());
|
| - }
|
| - id = sync_node.GetSuccessorId();
|
| + tab_pool_.FreeTabNode(tab_iter->second.sync_id());
|
| + tab_map_.erase(tab_iter);
|
| + return;
|
| }
|
| - return true;
|
| +
|
| + 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 = tab_pool_.GetFreeTabNode();
|
| + } else {
|
| + // This tab is already associated with a sync node, reuse it.
|
| + sync_id = tablink->second.sync_id();
|
| + }
|
| + Associate(&tab, sync_id);
|
| }
|
|
|
| -bool SessionModelAssociator::GetSessionData(
|
| - std::vector<ForeignSession*>* sessions) {
|
| +void SessionModelAssociator::Associate(const TabContents* tab, int64 sync_id) {
|
| DCHECK(CalledOnValidThread());
|
| + SessionID::id_type session_id = tab->controller().session_id().id();
|
|
|
| - // 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);
|
| - }
|
| + TabLinks t(sync_id, tab);
|
| + tab_map_[session_id] = t;
|
|
|
| - return true;
|
| + 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) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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.
|
| +// Static
|
| +// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
|
| +// See http://crbug.com/67068.
|
| 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());
|
| @@ -334,125 +345,280 @@
|
| }
|
| }
|
|
|
| -// 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) {
|
| + DCHECK(CalledOnValidThread());
|
| + NOTIMPLEMENTED();
|
| + // TODO(zea): we will need this once we support deleting foreign sessions.
|
| }
|
|
|
| -// 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::AssociateModels() {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + // Ensure that we disassociated properly, otherwise memory might leak.
|
| + DCHECK(foreign_session_tracker_.empty());
|
| + DCHECK_EQ(0U, tab_pool_.capacity());
|
| +
|
| + 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;
|
| }
|
| - 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;
|
| +
|
| + // Make sure we have a machine tag.
|
| + if (current_machine_tag_.empty())
|
| + InitializeCurrentMachineTag(&trans);
|
| +
|
| + 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;
|
| }
|
| - sync_pb::SessionTab* session_tab = session_window->add_session_tab();
|
| - PopulateSessionSpecificsTab(tab, session_tab);
|
| + 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;
|
| }
|
|
|
| -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::DisassociateModels() {
|
| + DCHECK(CalledOnValidThread());
|
| + foreign_session_tracker_.clear();
|
| + tab_map_.clear();
|
| + tab_pool_.clear();
|
| + 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) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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 at http://crbug.com/59672
|
| + current_machine_tag_ = "session_sync";
|
| + current_machine_tag_.append(dir->cache_guid());
|
| + VLOG(1) << "Creating machine tag: " << current_machine_tag_;
|
| + tab_pool_.set_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 foreign 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;
|
| }
|
| - num_populated++;
|
| +
|
| + 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;
|
| + }
|
| + } else if (id != local_session_syncid_) {
|
| + // This is previously stored local session information.
|
| + if (specifics.has_header()) {
|
| + DCHECK_EQ(sync_api::kInvalidId, local_session_syncid_);
|
| +
|
| + // 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_pool_.AddTabNode(id);
|
| + }
|
| + }
|
| +
|
| + id = sync_node.GetSuccessorId();
|
| }
|
| - if (num_populated == 0)
|
| - return true;
|
| - return false;
|
| +
|
| + // After updating from sync model all tabid's should be free.
|
| + DCHECK(tab_pool_.full());
|
| +
|
| + 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;
|
| +bool SessionModelAssociator::AssociateForeignSpecifics(
|
| + const sync_pb::SessionSpecifics& specifics,
|
| + const int64 modification_time) {
|
| + DCHECK(CalledOnValidThread());
|
| + std::string foreign_session_tag = specifics.session_tag();
|
| + DCHECK(foreign_session_tag != GetCurrentMachineTag() ||
|
| + sync_service_->cros_user() == "test user"); // For tests.
|
| +
|
| + 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.
|
| + ForeignSession* foreign_session =
|
| + foreign_session_tracker_.GetForeignSession(foreign_session_tag);
|
| +
|
| + 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 < static_cast<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],
|
| + &foreign_session_tracker_);
|
| + }
|
| + // 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());
|
| + } else if (specifics.has_tab()) {
|
| + const sync_pb::SessionTab& tab_s = specifics.tab();
|
| + SessionID::id_type tab_id = tab_s.tab_id();
|
| + SessionTab* tab =
|
| + foreign_session_tracker_.GetSessionTab(foreign_session_tag,
|
| + tab_id,
|
| + false);
|
| + PopulateSessionTabFromSpecifics(tab_s, modification_time, tab);
|
| + } else {
|
| + NOTREACHED();
|
| + return false;
|
| }
|
| - UpdateSyncModel(&specifics, &trans, &root);
|
| +
|
| + return true;
|
| }
|
|
|
| -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::DisassociateForeignSession(
|
| + const std::string& foreign_session_tag) {
|
| + DCHECK(CalledOnValidThread());
|
| + foreign_session_tracker_.DeleteForeignSession(foreign_session_tag);
|
| +}
|
| +
|
| +// Static
|
| +void SessionModelAssociator::PopulateSessionWindowFromSpecifics(
|
| + std::string foreign_session_tag,
|
| + const sync_pb::SessionWindow& specifics,
|
| + int64 mtime,
|
| + SessionWindow* session_window,
|
| + ForeignSessionTracker* tracker) {
|
| + 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::id_type tab_id = specifics.tab(i);
|
| + session_window->tabs[i] =
|
| + tracker->GetSessionTab(foreign_session_tag, tab_id, true);
|
| + }
|
| }
|
|
|
| +// Static
|
| +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);
|
| + }
|
| +}
|
| +
|
| +// Static
|
| 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;
|
| @@ -493,7 +659,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;
|
| @@ -512,67 +678,291 @@
|
| 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);
|
| +}
|
| +
|
| +SessionModelAssociator::TabNodePool::TabNodePool(
|
| + ProfileSyncService* sync_service)
|
| + : tab_pool_fp_(-1),
|
| + sync_service_(sync_service) {
|
| +}
|
| +
|
| +void SessionModelAssociator::TabNodePool::AddTabNode(int64 sync_id) {
|
| + tab_syncid_pool_.resize(tab_syncid_pool_.size() + 1);
|
| + tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id;
|
| +}
|
| +
|
| +int64 SessionModelAssociator::TabNodePool::GetFreeTabNode() {
|
| + DCHECK_GT(machine_tag_.length(), 0U);
|
| + 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(machine_tag_, tab_node_id);
|
| + sync_api::WriteNode tab_node(&trans);
|
| + if (!tab_node.InitUniqueByCreation(syncable::SESSIONS, root,
|
| + tab_node_tag)) {
|
| + LOG(ERROR) << "Could not create new node!";
|
| + 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::TabNodePool::FreeTabNode(int64 sync_id) {
|
| + // Pool size should always match # of free tab nodes.
|
| + DCHECK_LT(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());
|
| + return foreign_session_tracker_.LookupAllForeignSessions(sessions);
|
| +}
|
| +
|
| +bool SessionModelAssociator::GetForeignSession(
|
| + const std::string& tag,
|
| + std::vector<SessionWindow*>* windows) {
|
| + DCHECK(CalledOnValidThread());
|
| + return foreign_session_tracker_.LookupSessionWindows(tag, windows);
|
| +}
|
| +
|
| +bool SessionModelAssociator::GetForeignTab(
|
| + const std::string& tag,
|
| + const SessionID::id_type tab_id,
|
| + const SessionTab** tab) {
|
| + DCHECK(CalledOnValidThread());
|
| + return foreign_session_tracker_.LookupSessionTab(tag, tab_id, tab);
|
| +}
|
| +
|
| +// Static
|
| +bool SessionModelAssociator::SessionWindowHasNoTabsToSync(
|
| + 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;
|
| }
|
|
|
| -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;
|
| +// Valid local tab?
|
| +bool SessionModelAssociator::IsValidTab(const TabContents& tab) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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;
|
| +}
|
| +
|
| +// Static
|
| +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.
|
| +
|
| +SessionService* SessionModelAssociator::GetSessionService() {
|
| + DCHECK(CalledOnValidThread());
|
| + 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(CalledOnValidThread());
|
| + 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) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + // 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 (SessionWindowHasNoTabsToSync(*(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) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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(CalledOnValidThread());
|
| + DCHECK(tab_map_.empty());
|
| + for (size_t i = 0; i < window.tabs.size(); ++i) {
|
| + SessionTab* tab = window.tabs[i];
|
| + int64 id = tab_pool_.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.id()] = t;
|
| }
|
| - write_node.SetSessionSpecifics(*session_data);
|
| return true;
|
| }
|
|
|
| +bool SessionModelAssociator::WriteSessionTabToSyncModel(
|
| + const SessionTab& tab,
|
| + const int64 sync_id,
|
| + sync_api::WriteTransaction* trans) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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) {
|
| + DCHECK(CalledOnValidThread());
|
| + 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
|
|
|