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

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

Powered by Google App Engine
This is Rietveld 408576698