- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / glue / synced_session_tracker.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/logging.h"
6 #include "base/stl_util.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/sync/glue/synced_session_tracker.h"
9
10 namespace browser_sync {
11
12 SyncedSessionTracker::SyncedSessionTracker() {
13 }
14
15 SyncedSessionTracker::~SyncedSessionTracker() {
16   Clear();
17 }
18
19 void SyncedSessionTracker::SetLocalSessionTag(
20     const std::string& local_session_tag) {
21   local_session_tag_ = local_session_tag;
22 }
23
24 bool SyncedSessionTracker::LookupAllForeignSessions(
25     std::vector<const SyncedSession*>* sessions) const {
26   DCHECK(sessions);
27   sessions->clear();
28   // Fill vector of sessions from our synced session map.
29   for (SyncedSessionMap::const_iterator i =
30     synced_session_map_.begin(); i != synced_session_map_.end(); ++i) {
31     // Only include foreign sessions with open tabs.
32     SyncedSession* foreign_session = i->second;
33     if (i->first != local_session_tag_ && !foreign_session->windows.empty()) {
34       bool found_tabs = false;
35       for (SyncedSession::SyncedWindowMap::const_iterator iter =
36                foreign_session->windows.begin();
37            iter != foreign_session->windows.end(); ++iter) {
38         if (!SessionWindowHasNoTabsToSync(*(iter->second))) {
39           found_tabs = true;
40           break;
41         }
42       }
43       if (found_tabs)
44         sessions->push_back(foreign_session);
45     }
46   }
47
48   return !sessions->empty();
49 }
50
51 bool SyncedSessionTracker::LookupSessionWindows(
52     const std::string& session_tag,
53     std::vector<const SessionWindow*>* windows) const {
54   DCHECK(windows);
55   windows->clear();
56   SyncedSessionMap::const_iterator iter = synced_session_map_.find(session_tag);
57   if (iter == synced_session_map_.end())
58     return false;
59   windows->clear();
60   for (SyncedSession::SyncedWindowMap::const_iterator window_iter =
61            iter->second->windows.begin();
62        window_iter != iter->second->windows.end(); window_iter++) {
63     windows->push_back(window_iter->second);
64   }
65   return true;
66 }
67
68 bool SyncedSessionTracker::LookupSessionTab(
69     const std::string& tag,
70     SessionID::id_type tab_id,
71     const SessionTab** tab) const {
72   DCHECK(tab);
73   SyncedTabMap::const_iterator tab_map_iter = synced_tab_map_.find(tag);
74   if (tab_map_iter == synced_tab_map_.end()) {
75     // We have no record of this session.
76     *tab = NULL;
77     return false;
78   }
79   IDToSessionTabMap::const_iterator tab_iter =
80       tab_map_iter->second.find(tab_id);
81   if (tab_iter == tab_map_iter->second.end()) {
82     // We have no record of this tab.
83     *tab = NULL;
84     return false;
85   }
86   *tab = tab_iter->second.tab_ptr;
87   return true;
88 }
89
90 bool SyncedSessionTracker::LookupTabNodeIds(
91     const std::string& session_tag, std::set<int>* tab_node_ids) {
92   tab_node_ids->clear();
93   SyncedTabMap::const_iterator tab_map_iter =
94       synced_tab_map_.find(session_tag);
95   if (tab_map_iter == synced_tab_map_.end())
96     return false;
97
98   IDToSessionTabMap::const_iterator tab_iter = tab_map_iter->second.begin();
99   while (tab_iter != tab_map_iter->second.end()) {
100     if (tab_iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID)
101       tab_node_ids->insert(tab_iter->second.tab_node_id);
102     ++tab_iter;
103   }
104   return true;
105 }
106
107 SyncedSession* SyncedSessionTracker::GetSession(
108     const std::string& session_tag) {
109   SyncedSession* synced_session = NULL;
110   if (synced_session_map_.find(session_tag) !=
111       synced_session_map_.end()) {
112     synced_session = synced_session_map_[session_tag];
113   } else {
114     synced_session = new SyncedSession;
115     DVLOG(1) << "Creating new session with tag " << session_tag << " at "
116              << synced_session;
117     synced_session->session_tag = session_tag;
118     synced_session_map_[session_tag] = synced_session;
119   }
120   DCHECK(synced_session);
121   return synced_session;
122 }
123
124 bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
125   bool found_session = false;
126   SyncedSessionMap::iterator iter = synced_session_map_.find(session_tag);
127   if (iter != synced_session_map_.end()) {
128     SyncedSession* session = iter->second;
129     synced_session_map_.erase(iter);
130     delete session;  // Delete the SyncedSession object.
131     found_session = true;
132   }
133   synced_window_map_.erase(session_tag);
134   // It's possible there was no header node but there were tab nodes.
135   if (synced_tab_map_.erase(session_tag) > 0) {
136     found_session = true;
137   }
138   return found_session;
139 }
140
141 void SyncedSessionTracker::ResetSessionTracking(
142     const std::string& session_tag) {
143   // Reset window tracking.
144   GetSession(session_tag)->windows.clear();
145   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
146   if (window_iter != synced_window_map_.end()) {
147     for (IDToSessionWindowMap::iterator window_map_iter =
148              window_iter->second.begin();
149          window_map_iter != window_iter->second.end(); ++window_map_iter) {
150       window_map_iter->second.owned = false;
151       // We clear out the tabs to prevent double referencing of the same tab.
152       // All tabs that are in use will be added back as needed.
153       window_map_iter->second.window_ptr->tabs.clear();
154     }
155   }
156
157   // Reset tab tracking.
158   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
159   if (tab_iter != synced_tab_map_.end()) {
160     for (IDToSessionTabMap::iterator tab_map_iter =
161              tab_iter->second.begin();
162          tab_map_iter != tab_iter->second.end(); ++tab_map_iter) {
163       tab_map_iter->second.owned = false;
164     }
165   }
166 }
167
168 bool SyncedSessionTracker::DeleteOldSessionWindowIfNecessary(
169     SessionWindowWrapper window_wrapper) {
170   // Clear the tabs first, since we don't want the destructor to destroy
171   // them. Their deletion will be handled by DeleteOldSessionTab below.
172   if (!window_wrapper.owned) {
173     DVLOG(1) << "Deleting closed window "
174              << window_wrapper.window_ptr->window_id.id();
175     window_wrapper.window_ptr->tabs.clear();
176     delete window_wrapper.window_ptr;
177     return true;
178   }
179   return false;
180 }
181
182 bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
183     SessionTabWrapper tab_wrapper) {
184   if (!tab_wrapper.owned) {
185     if (VLOG_IS_ON(1)) {
186       SessionTab* tab_ptr = tab_wrapper.tab_ptr;
187       std::string title;
188       if (tab_ptr->navigations.size() > 0) {
189         title = " (" + UTF16ToUTF8(
190             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
191       }
192       DVLOG(1) << "Deleting closed tab " << tab_ptr->tab_id.id() << title
193                << " from window " << tab_ptr->window_id.id();
194     }
195     unmapped_tabs_.erase(tab_wrapper.tab_ptr);
196     delete tab_wrapper.tab_ptr;
197     return true;
198   }
199   return false;
200 }
201
202 void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
203   // Go through and delete any windows or tabs without owners.
204   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
205   if (window_iter != synced_window_map_.end()) {
206     for (IDToSessionWindowMap::iterator iter = window_iter->second.begin();
207          iter != window_iter->second.end();) {
208       SessionWindowWrapper window_wrapper = iter->second;
209       if (DeleteOldSessionWindowIfNecessary(window_wrapper))
210         window_iter->second.erase(iter++);
211       else
212         ++iter;
213     }
214   }
215
216   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
217   if (tab_iter != synced_tab_map_.end()) {
218     for (IDToSessionTabMap::iterator iter = tab_iter->second.begin();
219          iter != tab_iter->second.end();) {
220       SessionTabWrapper tab_wrapper = iter->second;
221       if (DeleteOldSessionTabIfNecessary(tab_wrapper))
222         tab_iter->second.erase(iter++);
223       else
224         ++iter;
225     }
226   }
227 }
228
229 void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
230                                               SessionID::id_type window_id) {
231   SessionWindow* window_ptr = NULL;
232   IDToSessionWindowMap::iterator iter =
233       synced_window_map_[session_tag].find(window_id);
234   if (iter != synced_window_map_[session_tag].end()) {
235     iter->second.owned = true;
236     window_ptr = iter->second.window_ptr;
237     DVLOG(1) << "Putting seen window " << window_id  << " at " << window_ptr
238              << "in " << (session_tag == local_session_tag_ ?
239                           "local session" : session_tag);
240   } else {
241     // Create the window.
242     window_ptr = new SessionWindow();
243     window_ptr->window_id.set_id(window_id);
244     synced_window_map_[session_tag][window_id] =
245         SessionWindowWrapper(window_ptr, IS_OWNED);
246     DVLOG(1) << "Putting new window " << window_id  << " at " << window_ptr
247              << "in " << (session_tag == local_session_tag_ ?
248                           "local session" : session_tag);
249   }
250   DCHECK(window_ptr);
251   DCHECK_EQ(window_ptr->window_id.id(), window_id);
252   DCHECK_EQ(reinterpret_cast<SessionWindow*>(NULL),
253             GetSession(session_tag)->windows[window_id]);
254   GetSession(session_tag)->windows[window_id] = window_ptr;
255 }
256
257 void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
258                                           SessionID::id_type window_id,
259                                           SessionID::id_type tab_id,
260                                           size_t tab_index) {
261   // We're called here for two reasons. 1) We've received an update to the
262   // SessionWindow information of a SessionHeader node for a foreign session,
263   // and 2) The SessionHeader node for our local session changed. In both cases
264   // we need to update our tracking state to reflect the change.
265   //
266   // Because the SessionHeader nodes are separate from the individual tab nodes
267   // and we don't store tab_node_ids in the header / SessionWindow specifics,
268   // the tab_node_ids are not always available when processing headers.
269   // We know that we will eventually process (via GetTab) every single tab node
270   // in the system, so we permit ourselves to use kInvalidTabNodeID here and
271   // rely on the later update to build the mapping (or a restart).
272   // TODO(tim): Bug 98892. Update comment when Sync API conversion finishes to
273   // mention that in the meantime, the only ill effect is that we may not be
274   // able to fully clean up a stale foreign session, but it will get garbage
275   // collected eventually.
276   SessionTab* tab_ptr = GetTabImpl(
277       session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
278   unmapped_tabs_.erase(tab_ptr);
279   synced_tab_map_[session_tag][tab_id].owned = true;
280   tab_ptr->window_id.set_id(window_id);
281   DVLOG(1) << "  - tab " << tab_id << " added to window "<< window_id;
282   DCHECK(GetSession(session_tag)->windows.find(window_id) !=
283          GetSession(session_tag)->windows.end());
284   std::vector<SessionTab*>& window_tabs =
285       GetSession(session_tag)->windows[window_id]->tabs;
286   if (window_tabs.size() <= tab_index) {
287     window_tabs.resize(tab_index+1, NULL);
288   }
289   DCHECK(!window_tabs[tab_index]);
290   window_tabs[tab_index] = tab_ptr;
291 }
292
293 SessionTab* SyncedSessionTracker::GetTab(
294     const std::string& session_tag,
295     SessionID::id_type tab_id,
296     int tab_node_id) {
297   DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
298   return GetTabImpl(session_tag, tab_id, tab_node_id);
299 }
300
301 SessionTab* SyncedSessionTracker::GetTabImpl(
302     const std::string& session_tag,
303     SessionID::id_type tab_id,
304     int tab_node_id) {
305   SessionTab* tab_ptr = NULL;
306   IDToSessionTabMap::iterator iter =
307       synced_tab_map_[session_tag].find(tab_id);
308   if (iter != synced_tab_map_[session_tag].end()) {
309     tab_ptr = iter->second.tab_ptr;
310     if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
311         tab_id != TabNodePool::kInvalidTabID) {
312       // We likely created this tab as an out-of-order update to the header
313       // node for this session before actually associating the tab itself, so
314       // the tab node id wasn't available at the time.  Update it.
315
316       if (iter->second.tab_node_id != tab_node_id &&
317           iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID) {
318         // We are updating tab_node_id for a valid tab_id, ideally this should
319         // never happen, but there are a few existing foreign sessions that
320         // may violate this constraint.
321         // TODO(shashishekhar): Introduce a DCHECK here to enforce this
322         // constraint in future.
323         DLOG(ERROR)
324             << "Updating tab_node_id for " << session_tag << " tab: " << tab_id
325             << " from: " << iter->second.tab_node_id << " to: " << tab_node_id;
326       }
327       iter->second.tab_node_id = tab_node_id;
328     }
329
330     if (VLOG_IS_ON(1)) {
331       std::string title;
332       if (tab_ptr->navigations.size() > 0) {
333         title = " (" + UTF16ToUTF8(
334             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
335       }
336       DVLOG(1) << "Getting "
337                << (session_tag == local_session_tag_ ?
338                    "local session" : session_tag)
339                << "'s seen tab " << tab_id  << " at " << tab_ptr << title;
340     }
341   } else {
342     tab_ptr = new SessionTab();
343     tab_ptr->tab_id.set_id(tab_id);
344     synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
345                                                              NOT_OWNED,
346                                                              tab_node_id);
347     unmapped_tabs_.insert(tab_ptr);
348     DVLOG(1) << "Getting "
349              << (session_tag == local_session_tag_ ?
350                  "local session" : session_tag)
351              << "'s new tab " << tab_id  << " at " << tab_ptr;
352   }
353   DCHECK(tab_ptr);
354   DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
355   return tab_ptr;
356 }
357
358 void SyncedSessionTracker::Clear() {
359   // Delete SyncedSession objects (which also deletes all their windows/tabs).
360   STLDeleteValues(&synced_session_map_);
361
362   // Go through and delete any tabs we had allocated but had not yet placed into
363   // a SyncedSessionobject.
364   STLDeleteElements(&unmapped_tabs_);
365
366   // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
367   // themselves; they should have all been deleted above).
368   synced_window_map_.clear();
369   synced_tab_map_.clear();
370
371   local_session_tag_.clear();
372 }
373
374 }  // namespace browser_sync