Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: chrome/browser/sync/glue/session_model_associator.cc

Issue 5705004: [SYNC] Sessions datatype refactor. Most things related to sessions under-the-... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Reviewer comments + self review Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/sync/glue/session_model_associator.h" 5 #include "chrome/browser/sync/glue/session_model_associator.h"
6 6
7 #include <algorithm>
7 #include <utility> 8 #include <utility>
8 9
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/string_util.h" 11 #include "chrome/browser/browser_list.h"
11 #include "base/utf_string_conversions.h" 12 #include "chrome/browser/browser_window.h"
12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/sessions/session_id.h"
14 #include "chrome/browser/sync/profile_sync_service.h" 14 #include "chrome/browser/sync/profile_sync_service.h"
15 #include "chrome/browser/sync/syncable/syncable.h" 15 #include "chrome/browser/sync/syncable/syncable.h"
16 #include "chrome/browser/tab_contents/navigation_controller.h"
17 #include "chrome/browser/tab_contents/navigation_entry.h"
18 #include "chrome/browser/tabs/tab_strip_model.h"
19 #include "chrome/common/extensions/extension.h"
16 #include "chrome/common/notification_details.h" 20 #include "chrome/common/notification_details.h"
17 #include "chrome/common/notification_service.h" 21 #include "chrome/common/notification_service.h"
18 #include "chrome/common/url_constants.h" 22 #include "chrome/common/url_constants.h"
19 23
20 namespace browser_sync { 24 namespace browser_sync {
21 25
22 namespace { 26 namespace {
23
24 static const char kNoSessionsFolderError[] = 27 static const char kNoSessionsFolderError[] =
25 "Server did not create the top-level sessions node. We " 28 "Server did not create the top-level sessions node. We "
26 "might be running against an out-of-date server."; 29 "might be running against an out-of-date server.";
27 30
31 // The maximum number of navigations in each direction we care to sync.
32 static const int max_sync_navigation_count = 6;
28 } // namespace 33 } // namespace
29 34
30 SessionModelAssociator::SessionModelAssociator( 35 SessionModelAssociator::SessionModelAssociator(ProfileSyncService* sync_service)
31 ProfileSyncService* sync_service) : sync_service_(sync_service) { 36 : tab_pool_(sync_service),
37 local_session_syncid_(sync_api::kInvalidId),
38 sync_service_(sync_service) {
32 DCHECK(CalledOnValidThread()); 39 DCHECK(CalledOnValidThread());
33 DCHECK(sync_service_); 40 DCHECK(sync_service_);
34 } 41 }
35 42
36 SessionModelAssociator::~SessionModelAssociator() { 43 SessionModelAssociator::~SessionModelAssociator() {
37 DCHECK(CalledOnValidThread()); 44 DCHECK(CalledOnValidThread());
45 DeleteForeignSessions();
38 } 46 }
39 47
40 bool SessionModelAssociator::AssociateModels() { 48 void SessionModelAssociator::DeleteForeignSessions() {
41 DCHECK(CalledOnValidThread()); 49 DCHECK(CalledOnValidThread());
50 // Delete ForeignSession objects (which also deletes all their windows/tabs).
51 STLDeleteContainerPairSecondPointers(foreign_session_map_.begin(),
52 foreign_session_map_.end());
53 foreign_session_map_.clear();
42 54
43 // Make sure we have a machine tag. 55 // Delete IDToSessTab maps. Does not delete the SessionTab objects, because
44 if (current_machine_tag_.empty()) 56 // they should already be referenced through foreign_session_map_.
45 InitializeCurrentMachineTag(); // Creates a syncable::BaseTransaction. 57 STLDeleteContainerPairSecondPointers(foreign_tab_map_.begin(),
58 foreign_tab_map_.end());
59 foreign_tab_map_.clear();
46 60
47 { 61 // Go through and delete any tabs we had allocated but had not yet placed into
48 // Do an initial update from sync model (in case we just re-enabled and 62 // a ForeignSessionobject.
49 // already had data). 63 STLDeleteContainerPointers(unmapped_tabs_.begin(), unmapped_tabs_.end());
50 sync_api::ReadTransaction trans( 64 unmapped_tabs_.clear();
51 sync_service_->backend()->GetUserShareHandle());
52 UpdateFromSyncModel(&trans);
53 }
54
55 // Check if anything has changed on the client side.
56 UpdateSyncModelDataFromClient();
57 return true;
58 }
59
60 bool SessionModelAssociator::ChromeModelHasUserCreatedNodes(
61 bool* has_nodes) {
62 DCHECK(CalledOnValidThread());
63 CHECK(has_nodes);
64 // This is wrong, but this function is unused, anyway.
65 *has_nodes = true;
66 return true;
67 }
68
69 bool SessionModelAssociator::DisassociateModels() {
70 specifics_.clear();
71
72 // There is no local model stored with which to disassociate, just notify
73 // foreign session handlers.
74 NotificationService::current()->Notify(
75 NotificationType::FOREIGN_SESSION_DISABLED,
76 NotificationService::AllSources(),
77 NotificationService::NoDetails());
78 return true;
79 }
80
81 const sync_pb::SessionSpecifics* SessionModelAssociator::
82 GetChromeNodeFromSyncId(int64 sync_id) {
83 sync_api::ReadTransaction trans(
84 sync_service_->backend()->GetUserShareHandle());
85 sync_api::ReadNode node(&trans);
86 if (!node.InitByIdLookup(sync_id))
87 return NULL;
88 return new sync_pb::SessionSpecifics(node.GetSessionSpecifics());
89 }
90
91 bool SessionModelAssociator::GetSyncIdForTaggedNode(const std::string* tag,
92 int64* sync_id) {
93 sync_api::ReadTransaction trans(
94 sync_service_->backend()->GetUserShareHandle());
95 sync_api::ReadNode node(&trans);
96 if (!node.InitByClientTagLookup(syncable::SESSIONS, *tag))
97 return false;
98 *sync_id = node.GetId();
99 return true;
100 }
101
102 int64 SessionModelAssociator::GetSyncIdFromChromeId(const std::string& id) {
103 sync_api::ReadTransaction trans(
104 sync_service_->backend()->GetUserShareHandle());
105 sync_api::ReadNode node(&trans);
106 if (!node.InitByClientTagLookup(syncable::SESSIONS, id))
107 return sync_api::kInvalidId;
108 return node.GetId();
109 } 65 }
110 66
111 bool SessionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 67 bool SessionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
112 DCHECK(CalledOnValidThread()); 68 DCHECK(CalledOnValidThread());
113 CHECK(has_nodes); 69 CHECK(has_nodes);
114 *has_nodes = false; 70 *has_nodes = false;
115 sync_api::ReadTransaction trans( 71 sync_api::ReadTransaction trans(
116 sync_service_->backend()->GetUserShareHandle()); 72 sync_service_->backend()->GetUserShareHandle());
117 sync_api::ReadNode root(&trans); 73 sync_api::ReadNode root(&trans);
118 if (!root.InitByTagLookup(kSessionsTag)) { 74 if (!root.InitByTagLookup(kSessionsTag)) {
119 LOG(ERROR) << kNoSessionsFolderError; 75 LOG(ERROR) << kNoSessionsFolderError;
120 return false; 76 return false;
121 } 77 }
122 // The sync model has user created nodes iff the sessions folder has 78 // The sync model has user created nodes iff the sessions folder has
123 // any children. 79 // any children.
124 *has_nodes = root.GetFirstChildId() != sync_api::kInvalidId; 80 *has_nodes = root.GetFirstChildId() != sync_api::kInvalidId;
125 return true; 81 return true;
126 } 82 }
127 83
128 std::string SessionModelAssociator::GetCurrentMachineTag() { 84 int64 SessionModelAssociator::GetSyncIdFromChromeId(const size_t& id) {
129 DCHECK(!current_machine_tag_.empty()); 85 DCHECK(CalledOnValidThread());
130 return current_machine_tag_; 86 return GetSyncIdFromSessionTag(TabIdToTag(GetCurrentMachineTag(), id));
131 } 87 }
132 88
133 void SessionModelAssociator::UpdateSyncModelDataFromClient() { 89 int64 SessionModelAssociator::GetSyncIdFromSessionTag(const std::string& tag) {
134 DCHECK(CalledOnValidThread()); 90 DCHECK(CalledOnValidThread());
135 SessionService::SessionCallback* callback = 91 sync_api::ReadTransaction trans(
136 NewCallback(this, &SessionModelAssociator::OnGotSession); 92 sync_service_->backend()->GetUserShareHandle());
137 // TODO(jerrica): Stop current race condition, possibly make new method in 93 sync_api::ReadNode node(&trans);
138 // session service, which only grabs the windows from memory. 94 if (!node.InitByClientTagLookup(syncable::SESSIONS, tag))
139 GetSessionService()->GetCurrentSession(&consumer_, callback); 95 return sync_api::kInvalidId;
140 } 96 return node.GetId();
141 97 }
142 // TODO(zea): Don't recreate sessions_ vector from scratch each time. This 98 void SessionModelAssociator::ReassociateWindows(bool reload_tabs) {
143 // will involve knowing which sessions have been changed (a different data 99 DCHECK(CalledOnValidThread());
144 // structure will probably be better too). 100 sync_pb::SessionSpecifics specifics;
145 bool SessionModelAssociator::UpdateFromSyncModel( 101 specifics.set_session_tag(GetCurrentMachineTag());
146 const sync_api::BaseTransaction* trans) { 102 sync_pb::SessionHeader* header_s = specifics.mutable_header();
147 DCHECK(CalledOnValidThread()); 103
148 104 for (BrowserList::const_iterator i = BrowserList::begin();
149 // Rebuild specifics_ vector 105 i != BrowserList::end(); ++i) {
150 specifics_.clear(); 106 // Make sure the browser has tabs and a window. Browsers destructor
151 if (!QuerySyncModel(trans, specifics_)) { 107 // removes itself from the BrowserList. When a browser is closed the
152 LOG(ERROR) << "SessionModelAssociator failed to updated from sync model"; 108 // destructor is not necessarily run immediately. This means its possible
109 // for us to get a handle to a browser that is about to be removed. If
110 // the tab count is 0 or the window is NULL, the browser is about to be
111 // deleted, so we ignore it.
112 if (ShouldSyncWindowType((*i)->type()) && (*i)->tab_count() &&
113 (*i)->window()) {
114 sync_pb::SessionWindow window_s;
115 SessionID::id_type window_id = (*i)->session_id().id();
116 VLOG(1) << "Reassociating window " << window_id << " with " <<
117 (*i)->tab_count() << " tabs.";
118 window_s.set_window_id(window_id);
119 window_s.set_selected_tab_index((*i)->selected_index());
120 if ((*i)->type() ==
121 Browser::TYPE_NORMAL) {
122 window_s.set_browser_type(
123 sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
124 } else {
125 window_s.set_browser_type(
126 sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
127 }
128
129 // Store the order of tabs.
130 bool found_tabs = false;
131 for (int j = 0; j < (*i)->tab_count(); ++j) {
132 TabContents* tab = (*i)->GetTabContentsAt(j);
133 DCHECK(tab);
134 if (IsValidTab(*tab)) {
135 found_tabs = true;
136 window_s.add_tab(tab->controller().session_id().id());
137 if (reload_tabs) {
138 ReassociateTab(*tab);
139 }
140 }
141 }
142 // Only add a window if it contains valid tabs.
143 if (found_tabs) {
144 sync_pb::SessionWindow* header_window = header_s->add_window();
145 *header_window = window_s;
146 }
147 }
148 }
149
150 sync_api::WriteTransaction trans(
151 sync_service_->backend()->GetUserShareHandle());
152 sync_api::WriteNode header_node(&trans);
153 if (!header_node.InitByIdLookup(local_session_syncid_)) {
154 LOG(ERROR) << "Failed to load local session header node.";
155 return;
156 }
157 header_node.SetSessionSpecifics(specifics);
158 }
159
160 // Static.
161 bool SessionModelAssociator::ShouldSyncWindowType(const Browser::Type& type) {
162 switch (type) {
163 case Browser::TYPE_POPUP:
164 return true;
165 case Browser::TYPE_APP:
166 return false;
167 case Browser::TYPE_APP_POPUP:
168 return false;
169 case Browser::TYPE_DEVTOOLS:
170 return false;
171 case Browser::TYPE_APP_PANEL:
172 return false;
173 case Browser::TYPE_NORMAL:
174 default:
175 return true;
176 }
177 }
178
179 void SessionModelAssociator::ReassociateTabs(
180 const std::vector<TabContents*>& tabs) {
181 DCHECK(CalledOnValidThread());
182 for (std::vector<TabContents*>::const_iterator i = tabs.begin();
183 i != tabs.end();
184 ++i) {
185 ReassociateTab(**i);
186 }
187 }
188
189 void SessionModelAssociator::ReassociateTab(const TabContents& tab) {
190 DCHECK(CalledOnValidThread());
191 if (!IsValidTab(tab))
192 return;
193
194 int64 sync_id;
195 SessionID id(tab.controller().session_id());
196 if (tab.is_being_destroyed()) {
197 // This tab is closing.
198 TabLinksMap::iterator tab_iter = tab_map_.find(id);
199 if (tab_iter == tab_map_.end()) {
200 // We aren't tracking this tab (for example, sync setting page).
201 return;
202 }
203 tab_pool_.FreeTabNode(tab_iter->second.sync_id());
204 tab_map_.erase(tab_iter);
205 return;
206 }
207
208 TabLinksMap::const_iterator tablink = tab_map_.find(id);
209 if (tablink == tab_map_.end()) {
210 // This is a new tab, get a sync node for it.
211 sync_id = tab_pool_.GetFreeTabNode();
212 } else {
213 // This tab is already associated with a sync node, reuse it.
214 sync_id = tablink->second.sync_id();
215 }
216 Associate(&tab, sync_id);
217 }
218
219 void SessionModelAssociator::Associate(const TabContents* tab, int64 sync_id) {
220 DCHECK(CalledOnValidThread());
221 SessionID session_id = tab->controller().session_id();
222
223 TabLinks t(sync_id, tab);
224 tab_map_[session_id] = t;
225
226 sync_api::WriteTransaction trans(
227 sync_service_->backend()->GetUserShareHandle());
228 WriteTabContentsToSyncModel(*tab, sync_id, &trans);
229 }
230
231 bool SessionModelAssociator::WriteTabContentsToSyncModel(
232 const TabContents& tab,
233 int64 sync_id,
234 sync_api::WriteTransaction* trans) {
235 DCHECK(CalledOnValidThread());
236 sync_api::WriteNode tab_node(trans);
237 if (!tab_node.InitByIdLookup(sync_id)) {
238 LOG(ERROR) << "Failed to look up tab node " << sync_id;
153 return false; 239 return false;
154 } 240 }
155 241
242 sync_pb::SessionSpecifics session_s;
243 session_s.set_session_tag(GetCurrentMachineTag());
244 sync_pb::SessionTab* tab_s = session_s.mutable_tab();
245
246 SessionID::id_type tab_id = tab.controller().session_id().id();
247 tab_s->set_tab_id(tab_id);
248 tab_s->set_window_id(tab.controller().window_id().id());
249 const int current_index = tab.controller().GetCurrentEntryIndex();
250 const int min_index = std::max(0,
251 current_index - max_sync_navigation_count);
252 const int max_index = std::min(current_index + max_sync_navigation_count,
253 tab.controller().entry_count());
254 const int pending_index = tab.controller().pending_entry_index();
255 Browser* browser = BrowserList::FindBrowserWithID(
256 tab.controller().window_id().id());
257 DCHECK(browser);
258 int index_in_window = browser->tabstrip_model()->GetWrapperIndex(&tab);
259 DCHECK(index_in_window != TabStripModel::kNoTab);
260 tab_s->set_pinned(browser->tabstrip_model()->IsTabPinned(index_in_window));
261 if (tab.extension_app())
262 tab_s->set_extension_app_id(tab.extension_app()->id());
263 for (int i = min_index; i < max_index; ++i) {
264 const NavigationEntry* entry = (i == pending_index) ?
265 tab.controller().pending_entry() : tab.controller().GetEntryAtIndex(i);
266 DCHECK(entry);
267 if (entry->virtual_url().is_valid()) {
268 if (i == max_index-1) {
269 VLOG(1) << "Associating tab " << tab_id << " with sync id " << sync_id
270 << " and url " << entry->virtual_url().possibly_invalid_spec();
271 }
272 TabNavigation tab_nav;
273 tab_nav.SetFromNavigationEntry(*entry);
274 sync_pb::TabNavigation* nav_s = tab_s->add_navigation();
275 PopulateSessionSpecificsNavigation(&tab_nav, nav_s);
276 }
277 }
278 tab_s->set_current_navigation_index(current_index);
279
280 tab_node.SetSessionSpecifics(session_s);
156 return true; 281 return true;
157 } 282 }
158 283
159 bool SessionModelAssociator::QuerySyncModel( 284 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
160 const sync_api::BaseTransaction* trans, 285 // See http://crbug.com/67068.
161 std::vector<const sync_pb::SessionSpecifics*>& specifics) {
162 DCHECK(CalledOnValidThread());
163 sync_api::ReadNode root(trans);
164 if (!root.InitByTagLookup(kSessionsTag)) {
165 LOG(ERROR) << kNoSessionsFolderError;
166 return false;
167 }
168 sync_api::ReadNode current_machine(trans);
169 int64 current_id = (current_machine.InitByClientTagLookup(syncable::SESSIONS,
170 GetCurrentMachineTag())) ? current_machine.GetId() : sync_api::kInvalidId;
171
172 // Iterate through the nodes and populate the session model.
173 int64 id = root.GetFirstChildId();
174 while (id != sync_api::kInvalidId) {
175 sync_api::ReadNode sync_node(trans);
176 if (!sync_node.InitByIdLookup(id)) {
177 LOG(ERROR) << "Failed to fetch sync node for id " << id;
178 return false;
179 }
180 if (id != current_id) {
181 specifics.insert(specifics.end(), &sync_node.GetSessionSpecifics());
182 }
183 id = sync_node.GetSuccessorId();
184 }
185 return true;
186 }
187
188 bool SessionModelAssociator::GetSessionData(
189 std::vector<ForeignSession*>* sessions) {
190 DCHECK(CalledOnValidThread());
191
192 // Build vector of sessions from specifics data
193 for (std::vector<const sync_pb::SessionSpecifics*>::const_iterator i =
194 specifics_.begin(); i != specifics_.end(); ++i) {
195 // Only include sessions with open windows.
196 if ((*i)->session_window_size() > 0)
197 AppendForeignSessionFromSpecifics(*i, sessions);
198 }
199
200 return true;
201 }
202
203 void SessionModelAssociator::AppendForeignSessionFromSpecifics(
204 const sync_pb::SessionSpecifics* specifics,
205 std::vector<ForeignSession*>* session) {
206 ForeignSession* foreign_session = new ForeignSession();
207 foreign_session->foreign_session_tag = specifics->session_tag();
208 session->insert(session->end(), foreign_session);
209 for (int i = 0; i < specifics->session_window_size(); i++) {
210 const sync_pb::SessionWindow* window = &specifics->session_window(i);
211 SessionWindow* session_window = new SessionWindow();
212 PopulateSessionWindowFromSpecifics(session_window, window);
213 foreign_session->windows.insert(
214 foreign_session->windows.end(), session_window);
215 }
216 }
217
218 // Fills the given vector with foreign session windows to restore.
219 void SessionModelAssociator::AppendForeignSessionWithID(int64 id,
220 std::vector<ForeignSession*>* session, sync_api::BaseTransaction* trans) {
221 if (id == sync_api::kInvalidId)
222 return;
223 sync_api::ReadNode node(trans);
224 if (!node.InitByIdLookup(id))
225 return;
226 const sync_pb::SessionSpecifics* ref = &node.GetSessionSpecifics();
227 AppendForeignSessionFromSpecifics(ref, session);
228 }
229
230 SessionService* SessionModelAssociator::GetSessionService() {
231 DCHECK(sync_service_);
232 Profile* profile = sync_service_->profile();
233 DCHECK(profile);
234 SessionService* sessions_service = profile->GetSessionService();
235 DCHECK(sessions_service);
236 return sessions_service;
237 }
238
239 void SessionModelAssociator::InitializeCurrentMachineTag() {
240 sync_api::WriteTransaction trans(sync_service_->backend()->
241 GetUserShareHandle());
242 syncable::Directory* dir =
243 trans.GetWrappedWriteTrans()->directory();
244
245 // TODO(zea): We need a better way of creating a machine tag. The directory
246 // kernel's cache_guid changes every time syncing is turned on and off. This
247 // will result in session's associated with stale machine tags persisting on
248 // the server since that tag will not be reused. Eventually this should
249 // become some string identifiable to the user. (Home, Work, Laptop, etc.)
250 // See issue 59672
251 current_machine_tag_ = "session_sync";
252 current_machine_tag_.append(dir->cache_guid());
253 VLOG(1) << "Creating machine tag: " << current_machine_tag_;
254 }
255
256 // See PopulateSessionSpecificsTab for use. May add functionality that includes
257 // the state later.
258 void SessionModelAssociator::PopulateSessionSpecificsNavigation( 286 void SessionModelAssociator::PopulateSessionSpecificsNavigation(
259 const TabNavigation* navigation, sync_pb::TabNavigation* tab_navigation) { 287 const TabNavigation* navigation,
288 sync_pb::TabNavigation* tab_navigation) {
289 DCHECK(CalledOnValidThread());
260 tab_navigation->set_index(navigation->index()); 290 tab_navigation->set_index(navigation->index());
261 tab_navigation->set_virtual_url(navigation->virtual_url().spec()); 291 tab_navigation->set_virtual_url(navigation->virtual_url().spec());
262 tab_navigation->set_referrer(navigation->referrer().spec()); 292 tab_navigation->set_referrer(navigation->referrer().spec());
263 tab_navigation->set_title(UTF16ToUTF8(navigation->title())); 293 tab_navigation->set_title(UTF16ToUTF8(navigation->title()));
264 switch (navigation->transition()) { 294 switch (navigation->transition()) {
265 case PageTransition::LINK: 295 case PageTransition::LINK:
266 tab_navigation->set_page_transition( 296 tab_navigation->set_page_transition(
267 sync_pb::TabNavigation_PageTransition_LINK); 297 sync_pb::TabNavigation_PageTransition_LINK);
268 break; 298 break;
269 case PageTransition::TYPED: 299 case PageTransition::TYPED:
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 case PageTransition::SERVER_REDIRECT: 351 case PageTransition::SERVER_REDIRECT:
322 tab_navigation->set_navigation_qualifier( 352 tab_navigation->set_navigation_qualifier(
323 sync_pb::TabNavigation_PageTransitionQualifier_SERVER_REDIRECT); 353 sync_pb::TabNavigation_PageTransitionQualifier_SERVER_REDIRECT);
324 break; 354 break;
325 default: 355 default:
326 tab_navigation->set_page_transition( 356 tab_navigation->set_page_transition(
327 sync_pb::TabNavigation_PageTransition_TYPED); 357 sync_pb::TabNavigation_PageTransition_TYPED);
328 } 358 }
329 } 359 }
330 360
331 // See PopulateSessionSpecificsWindow for use. 361 void SessionModelAssociator::Disassociate(int64 sync_id) {
332 void SessionModelAssociator::PopulateSessionSpecificsTab( 362 DCHECK(CalledOnValidThread());
333 const SessionTab* tab, sync_pb::SessionTab* session_tab) { 363 NOTIMPLEMENTED();
334 session_tab->set_tab_visual_index(tab->tab_visual_index); 364 // TODO(zea): we will need this once we support deleting foreign sessions.
335 session_tab->set_current_navigation_index( 365 }
336 tab->current_navigation_index); 366
337 session_tab->set_pinned(tab->pinned); 367 bool SessionModelAssociator::AssociateModels() {
338 session_tab->set_extension_app_id(tab->extension_app_id); 368 DCHECK(CalledOnValidThread());
339 for (std::vector<TabNavigation>::const_iterator i3 = 369
340 tab->navigations.begin(); i3 != tab->navigations.end(); ++i3) { 370 // Ensure that we disassociated properly, otherwise memory might leak.
341 const TabNavigation navigation = *i3; 371 DCHECK(foreign_tab_map_.empty());
342 sync_pb::TabNavigation* tab_navigation = 372 DCHECK(foreign_session_map_.empty());
343 session_tab->add_navigation(); 373 DCHECK(tab_pool_.capacity() == 0);
tim (not reviewing) 2010/12/20 18:58:44 DCHECK_EQ
Nicolas Zea 2010/12/21 18:12:23 Done.
344 PopulateSessionSpecificsNavigation(&navigation, tab_navigation); 374
345 } 375 local_session_syncid_ = sync_api::kInvalidId;
346 } 376
347 377 // Read any available foreign sessions and load any session data we may have.
348 // Called when populating session specifics to send to the sync model, called 378 // If we don't have any local session data in the db, create a header node.
349 // when associating models, or updating the sync model. 379 {
350 void SessionModelAssociator::PopulateSessionSpecificsWindow( 380 sync_api::WriteTransaction trans(
351 const SessionWindow* window, sync_pb::SessionWindow* session_window) { 381 sync_service_->backend()->GetUserShareHandle());
352 session_window->set_selected_tab_index(window->selected_tab_index); 382
353 if (window->type == 1) { 383 sync_api::ReadNode root(&trans);
354 session_window->set_browser_type( 384 if (!root.InitByTagLookup(kSessionsTag)) {
355 sync_pb::SessionWindow_BrowserType_TYPE_NORMAL); 385 LOG(ERROR) << kNoSessionsFolderError;
386 return false;
387 }
388
389 // Make sure we have a machine tag.
390 if (current_machine_tag_.empty())
391 InitializeCurrentMachineTag(&trans);
392
393 UpdateAssociationsFromSyncModel(root, &trans);
394
395 if (local_session_syncid_ == sync_api::kInvalidId) {
396 // The sync db didn't have a header node for us, we need to create one.
397 sync_api::WriteNode write_node(&trans);
398 if (!write_node.InitUniqueByCreation(syncable::SESSIONS, root,
399 current_machine_tag_)) {
400 LOG(ERROR) << "Failed to create sessions header sync node.";
401 return false;
402 }
403 write_node.SetTitle(UTF8ToWide(current_machine_tag_));
404 local_session_syncid_ = write_node.GetId();
405 }
406 }
407
408 // Check if anything has changed on the client side.
409 UpdateSyncModelDataFromClient();
410
411 VLOG(1) << "Session models associated.";
412
413 return true;
414 }
415
416 bool SessionModelAssociator::DisassociateModels() {
417 DCHECK(CalledOnValidThread());
418 DeleteForeignSessions();
419 tab_map_.clear();
420 tab_pool_.clear();
421 local_session_syncid_ = sync_api::kInvalidId;
422
423 // There is no local model stored with which to disassociate, just notify
424 // foreign session handlers.
425 NotificationService::current()->Notify(
426 NotificationType::FOREIGN_SESSION_DISABLED,
427 NotificationService::AllSources(),
428 NotificationService::NoDetails());
429 return true;
430 }
431
432 void SessionModelAssociator::InitializeCurrentMachineTag(
433 sync_api::WriteTransaction* trans) {
434 DCHECK(CalledOnValidThread());
435 syncable::Directory* dir = trans->GetWrappedWriteTrans()->directory();
436
437 // TODO(zea): We need a better way of creating a machine tag. The directory
438 // kernel's cache_guid changes every time syncing is turned on and off. This
439 // will result in session's associated with stale machine tags persisting on
440 // the server since that tag will not be reused. Eventually this should
441 // become some string identifiable to the user. (Home, Work, Laptop, etc.)
442 // See issue at http://crbug.com/59672
443 current_machine_tag_ = "session_sync";
444 current_machine_tag_.append(dir->cache_guid());
445 VLOG(1) << "Creating machine tag: " << current_machine_tag_;
446 tab_pool_.set_machine_tag(current_machine_tag_);
447 }
448
449 bool SessionModelAssociator::UpdateAssociationsFromSyncModel(
450 const sync_api::ReadNode& root,
451 const sync_api::BaseTransaction* trans) {
452 DCHECK(CalledOnValidThread());
453
454 // Iterate through the nodes and associate any foriegn sessions.
455 int64 id = root.GetFirstChildId();
456 while (id != sync_api::kInvalidId) {
457 sync_api::ReadNode sync_node(trans);
458 if (!sync_node.InitByIdLookup(id)) {
459 LOG(ERROR) << "Failed to fetch sync node for id " << id;
460 return false;
461 }
462
463 const sync_pb::SessionSpecifics& specifics =
464 sync_node.GetSessionSpecifics();
465 const int64 modification_time = sync_node.GetModificationTime();
466 if (specifics.session_tag() != GetCurrentMachineTag()) {
467 if (!AssociateForeignSpecifics(specifics, modification_time)) {
468 return false;
469 }
470 } else if (id != local_session_syncid_) {
471 // This is previously stored local session information.
472 if (specifics.has_header()) {
473 DCHECK(local_session_syncid_ == sync_api::kInvalidId);
474
475 // This is our previous header node, reuse it.
476 local_session_syncid_ = id;
477 } else {
478 DCHECK(specifics.has_tab());
479
480 // This is a tab node. We want to track these to reuse them in our free
481 // tab node pool. They will be overwritten eventually, so need to do
482 // anything else.
483 tab_pool_.AddTabNode(id);
484 }
485 }
486
487 id = sync_node.GetSuccessorId();
488 }
489
490 // After updating from sync model all tabid's should be free.
491 DCHECK(tab_pool_.full());
492
493 return true;
494 }
495
496 bool SessionModelAssociator::AssociateForeignSpecifics(
497 const sync_pb::SessionSpecifics& specifics,
498 const int64 modification_time) {
499 DCHECK(CalledOnValidThread());
500 std::string foreign_session_tag = specifics.session_tag();
501 DCHECK(foreign_session_tag != GetCurrentMachineTag() ||
502 sync_service_->cros_user() == "test user"); // For tests.
503
504 if (foreign_tab_map_.find(foreign_session_tag) == foreign_tab_map_.end()) {
505 foreign_tab_map_[foreign_session_tag] = new IDToSessionTabMap();
506 }
507 if (specifics.has_header()) {
508 // Read in the header data for this foreign session.
509 // Header data contains window information and ordered tab id's for each
510 // window.
511
512 // Load (or create) the ForeignSession object for this client.
513 scoped_ptr<ForeignSession> foreign_session;
514 if (foreign_session_map_.find(foreign_session_tag) !=
515 foreign_session_map_.end()) {
516 foreign_session.reset(foreign_session_map_[foreign_session_tag]);
356 } else { 517 } else {
357 session_window->set_browser_type( 518 foreign_session.reset(new ForeignSession);
358 sync_pb::SessionWindow_BrowserType_TYPE_POPUP); 519 foreign_session->foreign_session_tag = foreign_session_tag;
359 } 520 }
360 for (std::vector<SessionTab*>::const_iterator i2 = window->tabs.begin(); 521
361 i2 != window->tabs.end(); ++i2) { 522 const sync_pb::SessionHeader& header = specifics.header();
362 const SessionTab* tab = *i2; 523 foreign_session->windows.reserve(header.window_size());
363 if (tab->navigations.at(tab->current_navigation_index).virtual_url() == 524 VLOG(1) << "Associating " << foreign_session_tag << " with " <<
364 GURL(chrome::kChromeUINewTabURL)) { 525 header.window_size() << " windows.";
365 continue; 526 size_t i;
527 for (i = 0; i < (size_t)header.window_size(); ++i) {
528 if (i >= foreign_session->windows.size()) {
529 // This a new window, create it.
530 foreign_session->windows.push_back(new SessionWindow());
366 } 531 }
367 sync_pb::SessionTab* session_tab = session_window->add_session_tab(); 532 const sync_pb::SessionWindow& window_s = header.window(i);
368 PopulateSessionSpecificsTab(tab, session_tab); 533 PopulateSessionWindowFromSpecifics(foreign_session_tag,
369 } 534 window_s,
370 } 535 modification_time,
371 536 foreign_session->windows[i]);
372 bool SessionModelAssociator::WindowHasNoTabsToSync( 537 }
373 const SessionWindow* window) { 538 // Remove any remaining windows (in case windows were closed)
374 int num_populated = 0; 539 for (; i < foreign_session->windows.size(); ++i) {
375 for (std::vector<SessionTab*>::const_iterator i = window->tabs.begin(); 540 delete foreign_session->windows[i];
376 i != window->tabs.end(); ++i) { 541 }
377 const SessionTab* tab = *i; 542 foreign_session->windows.resize(header.window_size());
378 if (tab->navigations.at(tab->current_navigation_index).virtual_url() == 543
379 GURL(chrome::kChromeUINewTabURL)) { 544 foreign_session_map_[foreign_session_tag] = foreign_session.release();
380 continue; 545 } else if (specifics.has_tab()) {
381 } 546 const sync_pb::SessionTab& tab_s = specifics.tab();
382 num_populated++; 547 SessionID tab_id(tab_s.tab_id());
383 } 548 scoped_ptr<SessionTab> tab;
384 if (num_populated == 0) 549 if (foreign_tab_map_[foreign_session_tag]->find(tab_id) !=
385 return true; 550 foreign_tab_map_[foreign_session_tag]->end()) {
386 return false; 551 // We've already seen the header for this client, and created the
387 } 552 // appropriate tab objects. Go ahead and fill it with the tab info.
388 553 tab.reset((*foreign_tab_map_[foreign_session_tag])[tab_id]);
389 void SessionModelAssociator::OnGotSession(int handle, 554 unmapped_tabs_.erase(tab.get());
390 std::vector<SessionWindow*>* windows) { 555 VLOG(1) << "Associating " << foreign_session_tag << "'s seen tab " <<
391 sync_pb::SessionSpecifics specifics; 556 tab_id.id() << " at " << tab.get();
392 // Set the tag, then iterate through the vector of windows, extracting the 557 } else {
393 // window data, along with the tabs data and tab navigation data to populate 558 // Create the tab object, and associate it with the tab id in
394 // the session specifics. 559 // |foreign_tab_map_|. It will get associated with a window when we
395 specifics.set_session_tag(GetCurrentMachineTag()); 560 // associate the header node for this client.
396 FillSpecificsFromSessions(windows, &specifics); 561 tab.reset(new SessionTab());
397 bool has_nodes = false; 562 (*foreign_tab_map_[foreign_session_tag])[tab_id] = tab.get();
398 if (!SyncModelHasUserCreatedNodes(&has_nodes)) 563 unmapped_tabs_.insert(tab.get());
399 return; 564 VLOG(1) << "Associating " << foreign_session_tag << "'s new tab " <<
400 if (specifics.session_window_size() == 0 && has_nodes) 565 tab_id.id() << " at " << tab.get();
401 return; 566 }
402 sync_api::WriteTransaction trans( 567 PopulateSessionTabFromSpecifics(tab_s, modification_time, tab.release());
403 sync_service_->backend()->GetUserShareHandle()); 568 } else {
404 sync_api::ReadNode root(&trans); 569 NOTREACHED();
405 if (!root.InitByTagLookup(kSessionsTag)) { 570 return false;
406 LOG(ERROR) << kNoSessionsFolderError; 571 }
407 return; 572
408 } 573 return true;
409 UpdateSyncModel(&specifics, &trans, &root); 574 }
410 } 575
411 576 void SessionModelAssociator::DisassociateForeignSession(
412 void SessionModelAssociator::FillSpecificsFromSessions( 577 const std::string& foreign_session_tag) {
413 std::vector<SessionWindow*>* windows, 578 DCHECK(CalledOnValidThread());
414 sync_pb::SessionSpecifics* session) { 579 ForeignSessionMap::iterator iter =
415 for (std::vector<SessionWindow*>::const_iterator i = windows->begin(); 580 foreign_session_map_.find(foreign_session_tag);
416 i != windows->end(); ++i) { 581 if (iter != foreign_session_map_.end()) {
417 const SessionWindow* window = *i; 582 delete iter->second; // Delete the ForeignSession object.
418 if (WindowHasNoTabsToSync(window)) { 583 foreign_session_map_.erase(iter);
419 continue; 584 }
420 } 585 }
421 sync_pb::SessionWindow* session_window = session->add_session_window(); 586
422 PopulateSessionSpecificsWindow(window, session_window); 587 void SessionModelAssociator::PopulateSessionWindowFromSpecifics(
588 std::string foreign_session_tag,
589 const sync_pb::SessionWindow& specifics,
590 int64 mtime,
591 SessionWindow* session_window) {
592 DCHECK(CalledOnValidThread());
593 if (specifics.has_window_id())
594 session_window->window_id.set_id(specifics.window_id());
595 if (specifics.has_selected_tab_index())
596 session_window->selected_tab_index = specifics.selected_tab_index();
597 if (specifics.has_browser_type()) {
598 if (specifics.browser_type() ==
599 sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) {
600 session_window->type = 1;
601 } else {
602 session_window->type = 2;
603 }
604 }
605 session_window->timestamp = base::Time::FromInternalValue(mtime);
606 session_window->tabs.resize(specifics.tab_size());
607 for (int i = 0; i < specifics.tab_size(); i++) {
608 SessionID tab_id(specifics.tab(i));
609 scoped_ptr<SessionTab> tab;
610 IDToSessionTabMap::iterator iter =
611 foreign_tab_map_[foreign_session_tag]->find(tab_id);
612 if (iter != (*foreign_tab_map_[foreign_session_tag]).end()) {
613 tab.reset(iter->second);
614 unmapped_tabs_.erase(tab.get());
615 VLOG(1) << "Associating " << foreign_session_tag << "'s seen tab " <<
616 tab_id.id() << " at " << tab.get() << " with window " <<
617 specifics.window_id();
618 } else {
619 tab.reset(new SessionTab());
620 (*foreign_tab_map_[foreign_session_tag])[tab_id] = tab.get();
621 unmapped_tabs_.insert(tab.get());
622 VLOG(1) << "Associating " << foreign_session_tag << "'s new tab " <<
623 tab_id.id() << " at " << tab.get() << " with window " <<
624 specifics.window_id();
625 }
626 session_window->tabs[i] = tab.release();
627 }
628 }
629
630 void SessionModelAssociator::PopulateSessionTabFromSpecifics(
631 const sync_pb::SessionTab& specifics,
632 const int64 mtime,
633 SessionTab* tab) {
634 DCHECK(CalledOnValidThread());
635 if (specifics.has_tab_id())
636 tab->tab_id.set_id(specifics.tab_id());
637 if (specifics.has_window_id())
638 tab->window_id.set_id(specifics.window_id());
639 if (specifics.has_tab_visual_index())
640 tab->tab_visual_index = specifics.tab_visual_index();
641 if (specifics.has_current_navigation_index())
642 tab->current_navigation_index = specifics.current_navigation_index();
643 if (specifics.has_pinned())
644 tab->pinned = specifics.pinned();
645 if (specifics.has_extension_app_id())
646 tab->extension_app_id = specifics.extension_app_id();
647 tab->timestamp = base::Time::FromInternalValue(mtime);
648 tab->navigations.clear(); // In case we are reusing a previous SessionTab.
649 for (int i = 0; i < specifics.navigation_size(); i++) {
650 AppendSessionTabNavigation(specifics.navigation(i), &tab->navigations);
423 } 651 }
424 } 652 }
425 653
426 void SessionModelAssociator::AppendSessionTabNavigation( 654 void SessionModelAssociator::AppendSessionTabNavigation(
427 std::vector<TabNavigation>* navigations, 655 const sync_pb::TabNavigation& specifics,
428 const sync_pb::TabNavigation* navigation) { 656 std::vector<TabNavigation>* navigations) {
657 DCHECK(CalledOnValidThread());
429 int index = 0; 658 int index = 0;
430 GURL virtual_url; 659 GURL virtual_url;
431 GURL referrer; 660 GURL referrer;
432 string16 title; 661 string16 title;
433 std::string state; 662 std::string state;
434 PageTransition::Type transition(PageTransition::LINK); 663 PageTransition::Type transition(PageTransition::LINK);
435 if (navigation->has_index()) 664 if (specifics.has_index())
436 index = navigation->index(); 665 index = specifics.index();
437 if (navigation->has_virtual_url()) { 666 if (specifics.has_virtual_url()) {
438 GURL gurl(navigation->virtual_url()); 667 GURL gurl(specifics.virtual_url());
439 virtual_url = gurl; 668 virtual_url = gurl;
440 } 669 }
441 if (navigation->has_referrer()) { 670 if (specifics.has_referrer()) {
442 GURL gurl(navigation->referrer()); 671 GURL gurl(specifics.referrer());
443 referrer = gurl; 672 referrer = gurl;
444 } 673 }
445 if (navigation->has_title()) 674 if (specifics.has_title())
446 title = UTF8ToUTF16(navigation->title()); 675 title = UTF8ToUTF16(specifics.title());
447 if (navigation->has_page_transition() || 676 if (specifics.has_state())
448 navigation->has_navigation_qualifier()) { 677 state = specifics.state();
449 switch (navigation->page_transition()) { 678 if (specifics.has_page_transition() ||
679 specifics.has_navigation_qualifier()) {
680 switch (specifics.page_transition()) {
450 case sync_pb::TabNavigation_PageTransition_LINK: 681 case sync_pb::TabNavigation_PageTransition_LINK:
451 transition = PageTransition::LINK; 682 transition = PageTransition::LINK;
452 break; 683 break;
453 case sync_pb::TabNavigation_PageTransition_TYPED: 684 case sync_pb::TabNavigation_PageTransition_TYPED:
454 transition = PageTransition::TYPED; 685 transition = PageTransition::TYPED;
455 break; 686 break;
456 case sync_pb::TabNavigation_PageTransition_AUTO_BOOKMARK: 687 case sync_pb::TabNavigation_PageTransition_AUTO_BOOKMARK:
457 transition = PageTransition::AUTO_BOOKMARK; 688 transition = PageTransition::AUTO_BOOKMARK;
458 break; 689 break;
459 case sync_pb::TabNavigation_PageTransition_AUTO_SUBFRAME: 690 case sync_pb::TabNavigation_PageTransition_AUTO_SUBFRAME:
(...skipping 20 matching lines...) Expand all
480 case sync_pb::TabNavigation_PageTransition_KEYWORD_GENERATED: 711 case sync_pb::TabNavigation_PageTransition_KEYWORD_GENERATED:
481 transition = PageTransition::KEYWORD_GENERATED; 712 transition = PageTransition::KEYWORD_GENERATED;
482 break; 713 break;
483 case sync_pb::TabNavigation_PageTransition_CHAIN_START: 714 case sync_pb::TabNavigation_PageTransition_CHAIN_START:
484 transition = sync_pb::TabNavigation_PageTransition_CHAIN_START; 715 transition = sync_pb::TabNavigation_PageTransition_CHAIN_START;
485 break; 716 break;
486 case sync_pb::TabNavigation_PageTransition_CHAIN_END: 717 case sync_pb::TabNavigation_PageTransition_CHAIN_END:
487 transition = PageTransition::CHAIN_END; 718 transition = PageTransition::CHAIN_END;
488 break; 719 break;
489 default: 720 default:
490 switch (navigation->navigation_qualifier()) { 721 switch (specifics.navigation_qualifier()) {
491 case sync_pb:: 722 case sync_pb::
492 TabNavigation_PageTransitionQualifier_CLIENT_REDIRECT: 723 TabNavigation_PageTransitionQualifier_CLIENT_REDIRECT:
493 transition = PageTransition::CLIENT_REDIRECT; 724 transition = PageTransition::CLIENT_REDIRECT;
494 break; 725 break;
495 case sync_pb:: 726 case sync_pb::
496 TabNavigation_PageTransitionQualifier_SERVER_REDIRECT: 727 TabNavigation_PageTransitionQualifier_SERVER_REDIRECT:
497 transition = PageTransition::SERVER_REDIRECT; 728 transition = PageTransition::SERVER_REDIRECT;
498 break; 729 break;
499 default: 730 default:
500 transition = PageTransition::TYPED; 731 transition = PageTransition::TYPED;
501 } 732 }
502 } 733 }
503 } 734 }
504 TabNavigation tab_navigation(index, virtual_url, referrer, title, state, 735 TabNavigation tab_navigation(index, virtual_url, referrer, title, state,
505 transition); 736 transition);
506 navigations->insert(navigations->end(), tab_navigation); 737 navigations->insert(navigations->end(), tab_navigation);
507 } 738 }
508 739
509 void SessionModelAssociator::PopulateSessionTabFromSpecifics( 740 void SessionModelAssociator::UpdateSyncModelDataFromClient() {
510 SessionTab* session_tab, 741 DCHECK(CalledOnValidThread());
511 const sync_pb::SessionTab* tab, SessionID id) { 742 // TODO(zea): the logic for determining if we want to sync and the loading of
512 session_tab->window_id = id; 743 // the previous session should go here. We can probably reuse the code for
513 SessionID tabID; 744 // loading the current session from the old session implementation.
514 session_tab->tab_id = tabID; 745 // SessionService::SessionCallback* callback =
515 if (tab->has_tab_visual_index()) 746 // NewCallback(this, &SessionModelAssociator::OnGotSession);
516 session_tab->tab_visual_index = tab->tab_visual_index(); 747 // GetSessionService()->GetCurrentSession(&consumer_, callback);
517 if (tab->has_current_navigation_index()) { 748
518 session_tab->current_navigation_index = 749 // Associate all open windows and their tabs.
519 tab->current_navigation_index(); 750 ReassociateWindows(true);
520 } 751 }
521 if (tab->has_pinned()) 752
522 session_tab->pinned = tab->pinned(); 753 SessionModelAssociator::TabNodePool::TabNodePool(
523 if (tab->has_extension_app_id()) 754 ProfileSyncService* sync_service)
524 session_tab->extension_app_id = tab->extension_app_id(); 755 : tab_pool_fp_(-1),
525 for (int i3 = 0; i3 < tab->navigation_size(); i3++) { 756 sync_service_(sync_service) {
526 const sync_pb::TabNavigation* navigation = &tab->navigation(i3); 757 }
527 AppendSessionTabNavigation(&session_tab->navigations, navigation); 758
528 } 759 SessionModelAssociator::TabNodePool::TabNodePool(
529 } 760 ProfileSyncService* sync_service,
530 761 const std::string& current_machine_tag)
531 void SessionModelAssociator::PopulateSessionWindowFromSpecifics( 762 : tab_pool_fp_(-1),
532 SessionWindow* session_window, const sync_pb::SessionWindow* window) { 763 machine_tag_(current_machine_tag),
533 SessionID id; 764 sync_service_(sync_service) {
534 session_window->window_id = id; 765 }
535 if (window->has_selected_tab_index()) 766
536 session_window->selected_tab_index = window->selected_tab_index(); 767 void SessionModelAssociator::TabNodePool::AddTabNode(int64 sync_id) {
537 if (window->has_browser_type()) { 768 tab_syncid_pool_.resize(tab_syncid_pool_.size()+1);
538 if (window->browser_type() == 769 tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id;
539 sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) { 770 }
540 session_window->type = 1; 771
541 } else { 772 int64 SessionModelAssociator::TabNodePool::GetFreeTabNode() {
542 session_window->type = 2; 773 DCHECK(machine_tag_.length() > 0);
tim (not reviewing) 2010/12/20 18:58:44 DCHECK_GT
Nicolas Zea 2010/12/21 18:12:23 Done.
543 } 774 if (tab_pool_fp_ == -1) {
544 } 775 // Tab pool has no free nodes, allocate new one.
545 for (int i = 0; i < window->session_tab_size(); i++) { 776 sync_api::WriteTransaction trans(
546 const sync_pb::SessionTab& tab = window->session_tab(i); 777 sync_service_->backend()->GetUserShareHandle());
547 SessionTab* session_tab = new SessionTab(); 778 sync_api::ReadNode root(&trans);
548 PopulateSessionTabFromSpecifics(session_tab, &tab, id); 779 if (!root.InitByTagLookup(kSessionsTag)) {
549 session_window->tabs.insert(session_window->tabs.end(), session_tab); 780 LOG(ERROR) << kNoSessionsFolderError;
550 } 781 return 0;
551 } 782 }
552 783 size_t tab_node_id = tab_syncid_pool_.size();
553 bool SessionModelAssociator::UpdateSyncModel( 784 std::string tab_node_tag = TabIdToTag(machine_tag_, tab_node_id);
554 sync_pb::SessionSpecifics* session_data, 785 sync_api::WriteNode tab_node(&trans);
555 sync_api::WriteTransaction* trans, 786 if (!tab_node.InitUniqueByCreation(syncable::SESSIONS, root,
556 const sync_api::ReadNode* root) { 787 tab_node_tag)) {
557 const std::string id = session_data->session_tag(); 788 LOG(ERROR) << "Could not create new node!";
558 sync_api::WriteNode write_node(trans); 789 return -1;
559 if (!write_node.InitByClientTagLookup(syncable::SESSIONS, id)) { 790 }
560 sync_api::WriteNode create_node(trans); 791 tab_node.SetTitle(UTF8ToWide(tab_node_tag));
561 if (!create_node.InitUniqueByCreation(syncable::SESSIONS, *root, id)) { 792
562 LOG(ERROR) << "Could not create node for session " << id; 793 // Grow the pool by 1 since we created a new node. We don't actually need
794 // to put the node's id in the pool now, since the pool is still empty.
795 // The id will be added when that tab is closed and the node is freed.
796 tab_syncid_pool_.resize(tab_node_id + 1);
797 VLOG(1) << "Adding sync node " << tab_node.GetId() << " to tab syncid pool";
798 return tab_node.GetId();
799 } else {
800 // There are nodes available, grab next free and decrement free pointer.
801 return tab_syncid_pool_[static_cast<size_t>(tab_pool_fp_--)];
802 }
803 }
804
805 void SessionModelAssociator::TabNodePool::FreeTabNode(int64 sync_id) {
806 // Pool size should always match # of free tab nodes.
807 DCHECK(tab_pool_fp_ < static_cast<int64>(tab_syncid_pool_.size()));
808 tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id;
809 }
810
811 bool SessionModelAssociator::GetAllForeignSessions(
812 std::vector<const ForeignSession*>* sessions) {
813 DCHECK(CalledOnValidThread());
814
815 // Build vector of sessions from our foreign session map.
816 for (ForeignSessionMap::const_iterator i =
817 foreign_session_map_.begin(); i != foreign_session_map_.end(); ++i) {
818 // Only include sessions with open tabs.
819 ForeignSession* foreign_session = i->second;
820 if (foreign_session->windows.size() > 0 &&
821 !WindowHasNoTabsToSync(*foreign_session->windows[0]))
822 sessions->push_back(foreign_session);
823 }
824
825 return true;
826 }
827
828 bool SessionModelAssociator::GetForeignSession(
829 const std::string& tag,
830 std::vector<SessionWindow*>* sessions) {
831 DCHECK(CalledOnValidThread());
832
833 ForeignSessionMap::iterator iter = foreign_session_map_.find(tag);
834 if (iter == foreign_session_map_.end())
835 return false;
836 *sessions = iter->second->windows;
837
838 return true;
839 }
840
841 bool SessionModelAssociator::GetForeignTab(
842 const std::string& tag,
843 const SessionID::id_type tab_id,
844 const SessionTab** tab) {
845 DCHECK(CalledOnValidThread());
846 if (foreign_tab_map_.find(tag) == foreign_tab_map_.end())
847 return false;
848 SessionID id(tab_id);
849 if (foreign_tab_map_[tag]->find(id) == foreign_tab_map_[tag]->end())
850 return false;
851 *tab = (*foreign_tab_map_[tag])[id];
852 return true;
853 }
854
855 bool SessionModelAssociator::WindowHasNoTabsToSync(
856 const SessionWindow& window) {
857 DCHECK(CalledOnValidThread());
858 int num_populated = 0;
859 for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin();
860 i != window.tabs.end(); ++i) {
861 const SessionTab* tab = *i;
862 if (IsValidSessionTab(*tab))
863 num_populated++;
864 }
865 if (num_populated == 0)
866 return true;
867 return false;
868 }
869
870 // Valid local tab?
871 bool SessionModelAssociator::IsValidTab(const TabContents& tab) {
872 DCHECK(CalledOnValidThread());
873 if ((tab.profile() == sync_service_->profile() ||
874 sync_service_->profile() == NULL)) {
875 const NavigationEntry* entry = tab.controller().GetActiveEntry();
876 if (!entry)
563 return false; 877 return false;
564 } 878 if (entry->virtual_url().is_valid() &&
565 create_node.SetSessionSpecifics(*session_data); 879 (entry->virtual_url() != GURL(chrome::kChromeUINewTabURL) ||
566 return true; 880 tab.controller().entry_count() > 1)) {
567 } 881 return true;
568 write_node.SetSessionSpecifics(*session_data); 882 }
569 return true; 883 }
884 return false;
885 }
886
887 // Valid foreign tab?
888 bool SessionModelAssociator::IsValidSessionTab(const SessionTab& tab) {
889 DCHECK(CalledOnValidThread());
890 if (tab.navigations.empty())
891 return false;
892 int selected_index = tab.current_navigation_index;
893 selected_index = std::max(
894 0,
895 std::min(selected_index,
896 static_cast<int>(tab.navigations.size() - 1)));
897 if (selected_index == 0 &&
898 tab.navigations.size() == 1 &&
899 tab.navigations.at(selected_index).virtual_url() ==
900 GURL(chrome::kChromeUINewTabURL)) {
901 // This is a new tab with no further history, skip.
902 return false;
903 }
904 return true;
905 }
906
907 // ==========================================================================
908 // The following methods are not currently used but will likely become useful
909 // if we choose to sync the previous browser session.
910
911 SessionService* SessionModelAssociator::GetSessionService() {
912 DCHECK(CalledOnValidThread());
913 DCHECK(sync_service_);
914 Profile* profile = sync_service_->profile();
915 DCHECK(profile);
916 SessionService* sessions_service = profile->GetSessionService();
917 DCHECK(sessions_service);
918 return sessions_service;
919 }
920
921 void SessionModelAssociator::OnGotSession(
922 int handle,
923 std::vector<SessionWindow*>* windows) {
924 DCHECK(CalledOnValidThread());
925 DCHECK(local_session_syncid_);
926
927 sync_pb::SessionSpecifics specifics;
928 specifics.set_session_tag(GetCurrentMachineTag());
929 sync_pb::SessionHeader* header_s = specifics.mutable_header();
930 PopulateSessionSpecificsHeader(*windows, header_s);
931
932 sync_api::WriteTransaction trans(
933 sync_service_->backend()->GetUserShareHandle());
934 sync_api::ReadNode root(&trans);
935 if (!root.InitByTagLookup(kSessionsTag)) {
936 LOG(ERROR) << kNoSessionsFolderError;
937 return;
938 }
939
940 sync_api::WriteNode header_node(&trans);
941 if (!header_node.InitByIdLookup(local_session_syncid_)) {
942 LOG(ERROR) << "Failed to load local session header node.";
943 return;
944 }
945
946 header_node.SetSessionSpecifics(specifics);
947 }
948
949 void SessionModelAssociator::PopulateSessionSpecificsHeader(
950 const std::vector<SessionWindow*>& windows,
951 sync_pb::SessionHeader* header_s) {
952 DCHECK(CalledOnValidThread());
953
954 // Iterate through the vector of windows, extracting the window data, along
955 // with the tab data to populate the session specifics.
956 for (size_t i = 0; i < windows.size(); ++i) {
957 if (WindowHasNoTabsToSync(*(windows[i])))
958 continue;
959 sync_pb::SessionWindow* window_s = header_s->add_window();
960 PopulateSessionSpecificsWindow(*(windows[i]), window_s);
961 if (!SyncLocalWindowToSyncModel(*(windows[i])))
962 return;
963 }
964 }
965
966 // Called when populating session specifics to send to the sync model, called
967 // when associating models, or updating the sync model.
968 void SessionModelAssociator::PopulateSessionSpecificsWindow(
969 const SessionWindow& window,
970 sync_pb::SessionWindow* session_window) {
971 DCHECK(CalledOnValidThread());
972 session_window->set_window_id(window.window_id.id());
973 session_window->set_selected_tab_index(window.selected_tab_index);
974 if (window.type == Browser::TYPE_NORMAL) {
975 session_window->set_browser_type(
976 sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
977 } else if (window.type == Browser::TYPE_POPUP) {
978 session_window->set_browser_type(
979 sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
980 } else {
981 // ignore
982 LOG(WARNING) << "Session Sync unable to handle windows of type" <<
983 window.type;
984 return;
985 }
986 for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin();
987 i != window.tabs.end(); ++i) {
988 const SessionTab* tab = *i;
989 if (!IsValidSessionTab(*tab))
990 continue;
991 session_window->add_tab(tab->tab_id.id());
992 }
993 }
994
995 bool SessionModelAssociator::SyncLocalWindowToSyncModel(
996 const SessionWindow& window) {
997 DCHECK(CalledOnValidThread());
998 DCHECK(tab_map_.empty());
999 for (size_t i = 0; i < window.tabs.size(); ++i) {
1000 SessionTab* tab = window.tabs[i];
1001 int64 id = tab_pool_.GetFreeTabNode();
1002 if (id == -1) {
1003 LOG(ERROR) << "Failed to find/generate free sync node for tab.";
1004 return false;
1005 }
1006
1007 sync_api::WriteTransaction trans(
1008 sync_service_->backend()->GetUserShareHandle());
1009 if (!WriteSessionTabToSyncModel(*tab, id, &trans)) {
1010 return false;
1011 }
1012
1013 TabLinks t(id, tab);
1014 tab_map_[tab->tab_id] = t;
1015 }
1016 return true;
1017 }
1018
1019 bool SessionModelAssociator::WriteSessionTabToSyncModel(
1020 const SessionTab& tab,
1021 const int64 sync_id,
1022 sync_api::WriteTransaction* trans) {
1023 DCHECK(CalledOnValidThread());
1024 sync_api::WriteNode tab_node(trans);
1025 if (!tab_node.InitByIdLookup(sync_id)) {
1026 LOG(ERROR) << "Failed to look up tab node " << sync_id;
1027 return false;
1028 }
1029
1030 sync_pb::SessionSpecifics specifics;
1031 specifics.set_session_tag(GetCurrentMachineTag());
1032 sync_pb::SessionTab* tab_s = specifics.mutable_tab();
1033 PopulateSessionSpecificsTab(tab, tab_s);
1034 tab_node.SetSessionSpecifics(specifics);
1035 return true;
1036 }
1037
1038 // See PopulateSessionSpecificsWindow for use.
1039 void SessionModelAssociator::PopulateSessionSpecificsTab(
1040 const SessionTab& tab,
1041 sync_pb::SessionTab* session_tab) {
1042 DCHECK(CalledOnValidThread());
1043 session_tab->set_tab_id(tab.tab_id.id());
1044 session_tab->set_window_id(tab.window_id.id());
1045 session_tab->set_tab_visual_index(tab.tab_visual_index);
1046 session_tab->set_current_navigation_index(
1047 tab.current_navigation_index);
1048 session_tab->set_pinned(tab.pinned);
1049 session_tab->set_extension_app_id(tab.extension_app_id);
1050 for (std::vector<TabNavigation>::const_iterator i =
1051 tab.navigations.begin(); i != tab.navigations.end(); ++i) {
1052 const TabNavigation navigation = *i;
1053 sync_pb::TabNavigation* tab_navigation =
1054 session_tab->add_navigation();
1055 PopulateSessionSpecificsNavigation(&navigation, tab_navigation);
1056 }
570 } 1057 }
571 1058
572 } // namespace browser_sync 1059 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698