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

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

Powered by Google App Engine
This is Rietveld 408576698