blob: 0b93d26a098e0345b6a44f4480a6094c277a0cc8 [file] [log] [blame]
[email protected]a636d8e52012-02-28 15:40:411// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]169627b2008-12-06 19:30:195#ifndef CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
6#define CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_
initial.commit09911bf2008-07-26 23:55:297
8#include <map>
[email protected]23da84e152010-07-14 01:29:059#include <string>
initial.commit09911bf2008-07-26 23:55:2910
11#include "base/basictypes.h"
[email protected]f9ffb892011-11-28 18:57:3112#include "base/callback.h"
[email protected]4c7f9c52012-12-11 21:51:3013#include "base/memory/scoped_vector.h"
sky1a14f492014-08-26 21:44:2614#include "base/memory/weak_ptr.h"
[email protected]e95b717f2014-02-06 13:47:1315#include "base/task/cancelable_task_tracker.h"
[email protected]cc86ccfe2013-06-28 00:10:5016#include "base/time/time.h"
[email protected]982921f12009-10-27 21:43:5317#include "chrome/browser/defaults.h"
skuhned1e250b2014-11-05 15:26:5018#include "chrome/browser/sessions/base_session_service_delegate_impl.h"
skuhnec3aa8cf2014-10-30 06:02:4519#include "chrome/browser/sessions/session_service_utils.h"
[email protected]2ad4a902010-11-17 06:05:1320#include "chrome/browser/ui/browser.h"
[email protected]0665ebe2013-02-13 09:53:1921#include "chrome/browser/ui/browser_finder.h"
[email protected]bf219e9eb2012-10-10 15:20:3822#include "chrome/browser/ui/browser_list_observer.h"
[email protected]540380fc2014-03-14 10:10:3423#include "components/keyed_service/core/keyed_service.h"
blundell56789fe2015-09-10 05:11:1624#include "components/sessions/core/tab_restore_service_client.h"
skuhneb7409dcf2014-11-14 04:06:5525#include "components/sessions/session_service_commands.h"
[email protected]6c2381d2011-10-19 02:52:5326#include "content/public/browser/notification_observer.h"
27#include "content/public/browser/notification_registrar.h"
[email protected]400eaf82011-08-22 15:47:3928#include "ui/base/ui_base_types.h"
initial.commit09911bf2008-07-26 23:55:2929
initial.commit09911bf2008-07-26 23:55:2930class Profile;
[email protected]ad23a092011-12-28 07:02:0431
32namespace content {
33class NavigationEntry;
[email protected]40600152012-09-26 10:04:3134class WebContents;
skuhneb7409dcf2014-11-14 04:06:5535} // namespace content
36
37namespace sessions {
38class SessionCommand;
39struct SessionTab;
40struct SessionWindow;
41} // namespace sessions
initial.commit09911bf2008-07-26 23:55:2942
43// SessionService ------------------------------------------------------------
44
45// SessionService is responsible for maintaining the state of open windows
[email protected]6ea265a2008-10-30 02:58:3646// and tabs so that they can be restored at a later date. The state of the
47// currently open browsers is referred to as the current session.
initial.commit09911bf2008-07-26 23:55:2948//
[email protected]668cd422011-04-06 21:00:0149// SessionService supports restoring from the last session. The last session
50// typically corresponds to the last run of the browser, but not always. For
51// example, if the user has a tabbed browser and app window running, closes the
52// tabbed browser, then creates a new tabbed browser the current session is made
53// the last session and the current session reset. This is done to provide the
54// illusion that app windows run in separate processes. Similar behavior occurs
55// with incognito windows.
initial.commit09911bf2008-07-26 23:55:2956//
skuhnec3aa8cf2014-10-30 06:02:4557// SessionService itself uses functions from session_service_commands to store
58// commands which can rebuild the open state of the browser (as |SessionWindow|,
59// |SessionTab| and |SerializedNavigationEntry|). The commands are periodically
60// flushed to |SessionBackend| and written to a file. Every so often
61// |SessionService| rebuilds the contents of the file from the open state of the
[email protected]40a7e412013-04-29 18:13:0162// browser.
skuhned1e250b2014-11-05 15:26:5063class SessionService : public BaseSessionServiceDelegateImpl,
[email protected]540380fc2014-03-14 10:10:3464 public KeyedService,
[email protected]bf219e9eb2012-10-10 15:20:3865 public content::NotificationObserver,
66 public chrome::BrowserListObserver {
[email protected]86ef6a392012-05-11 22:03:1167 friend class SessionServiceTestHelper;
initial.commit09911bf2008-07-26 23:55:2968 public:
skuhnec3aa8cf2014-10-30 06:02:4569 // Used to distinguish an application from a ordinary content window.
[email protected]a636d8e52012-02-28 15:40:4170 enum AppType {
71 TYPE_APP,
72 TYPE_NORMAL
73 };
74
initial.commit09911bf2008-07-26 23:55:2975 // Creates a SessionService for the specified profile.
76 explicit SessionService(Profile* profile);
77 // For testing.
[email protected]650b2d52013-02-10 03:41:4578 explicit SessionService(const base::FilePath& save_path);
initial.commit09911bf2008-07-26 23:55:2979
Daniel Chenga542fca2014-10-21 09:51:2980 ~SessionService() override;
[email protected]f0a58a382011-04-26 16:09:2981
skuhned58ab122014-10-22 20:23:0282 // This may be NULL during testing.
83 Profile* profile() const { return profile_; }
84
[email protected]8ddbb642011-09-01 02:52:2085 // Returns true if a new window opening should really be treated like the
86 // start of a session (with potential session restore, startup URLs, etc.).
87 // In particular, this is true if there are no tabbed browsers running
88 // currently (eg. because only background or other app pages are running).
89 bool ShouldNewWindowStartSession();
90
[email protected]c9b19942010-03-26 15:58:0891 // Invoke at a point when you think session restore might occur. For example,
92 // during startup and window creation this is invoked to see if a session
93 // needs to be restored. If a session needs to be restored it is done so
94 // asynchronously and true is returned. If false is returned the session was
95 // not restored and the caller needs to create a new window.
96 bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open);
97
initial.commit09911bf2008-07-26 23:55:2998 // Resets the contents of the file from the current state of all open
99 // browsers whose profile matches our profile.
100 void ResetFromCurrentBrowsers();
101
102 // Moves the current session to the last session. This is useful when a
103 // checkpoint occurs, such as when the user launches the app and no tabbed
104 // browsers are running.
105 void MoveCurrentSessionToLastSession();
106
skuhned1e250b2014-11-05 15:26:50107 // Deletes the last session.
108 void DeleteLastSession();
109
initial.commit09911bf2008-07-26 23:55:29110 // Associates a tab with a window.
111 void SetTabWindow(const SessionID& window_id,
112 const SessionID& tab_id);
113
114 // Sets the bounds of a window.
115 void SetWindowBounds(const SessionID& window_id,
116 const gfx::Rect& bounds,
[email protected]400eaf82011-08-22 15:47:39117 ui::WindowShowState show_state);
initial.commit09911bf2008-07-26 23:55:29118
119 // Sets the visual index of the tab in its parent window.
120 void SetTabIndexInWindow(const SessionID& window_id,
121 const SessionID& tab_id,
122 int new_index);
123
[email protected]5c0e6482009-07-14 20:20:09124 // Sets the pinned state of the tab.
125 void SetPinnedState(const SessionID& window_id,
126 const SessionID& tab_id,
127 bool is_pinned);
128
[email protected]c0e3ee42010-05-26 22:11:07129 // Notification that a tab has been closed. |closed_by_user_gesture| comes
[email protected]e6ff1d52012-04-17 20:00:40130 // from |WebContents::closed_by_user_gesture|; see it for details.
initial.commit09911bf2008-07-26 23:55:29131 //
132 // Note: this is invoked from the NavigationController's destructor, which is
133 // after the actual tab has been removed.
[email protected]c0e3ee42010-05-26 22:11:07134 void TabClosed(const SessionID& window_id,
135 const SessionID& tab_id,
136 bool closed_by_user_gesture);
initial.commit09911bf2008-07-26 23:55:29137
[email protected]7d52ea4c62013-11-13 03:52:14138 // Notification a window has opened.
139 void WindowOpened(Browser* browser);
140
initial.commit09911bf2008-07-26 23:55:29141 // Notification the window is about to close.
142 void WindowClosing(const SessionID& window_id);
143
144 // Notification a window has finished closing.
145 void WindowClosed(const SessionID& window_id);
146
[email protected]40600152012-09-26 10:04:31147 // Called when a tab is inserted.
148 void TabInserted(content::WebContents* contents);
149
150 // Called when a tab is closing.
151 void TabClosing(content::WebContents* contents);
152
initial.commit09911bf2008-07-26 23:55:29153 // Sets the type of window. In order for the contents of a window to be
154 // tracked SetWindowType must be invoked with a type we track
skuhnec3aa8cf2014-10-30 06:02:45155 // (ShouldRestoreOfWindowType returns true).
[email protected]a636d8e52012-02-28 15:40:41156 void SetWindowType(const SessionID& window_id,
157 Browser::Type type,
158 AppType app_type);
159
[email protected]18ab5152013-03-28 23:14:05160 // Sets the application name of the specified window.
161 void SetWindowAppName(const SessionID& window_id,
162 const std::string& app_name);
initial.commit09911bf2008-07-26 23:55:29163
[email protected]c12bf1a12008-09-17 16:28:49164 // Invoked when the NavigationController has removed entries from the back of
165 // the list. |count| gives the number of entries in the navigation controller.
166 void TabNavigationPathPrunedFromBack(const SessionID& window_id,
167 const SessionID& tab_id,
168 int count);
169
170 // Invoked when the NavigationController has removed entries from the front of
171 // the list. |count| gives the number of entries that were removed.
172 void TabNavigationPathPrunedFromFront(const SessionID& window_id,
173 const SessionID& tab_id,
174 int count);
initial.commit09911bf2008-07-26 23:55:29175
176 // Updates the navigation entry for the specified tab.
[email protected]40a7e412013-04-29 18:13:01177 void UpdateTabNavigation(
178 const SessionID& window_id,
179 const SessionID& tab_id,
180 const sessions::SerializedNavigationEntry& navigation);
initial.commit09911bf2008-07-26 23:55:29181
182 // Notification that a tab has restored its entries or a closed tab is being
183 // reused.
[email protected]b624ddc2012-11-15 18:04:13184 void TabRestored(content::WebContents* tab, bool pinned);
initial.commit09911bf2008-07-26 23:55:29185
186 // Sets the index of the selected entry in the navigation controller for the
187 // specified tab.
188 void SetSelectedNavigationIndex(const SessionID& window_id,
189 const SessionID& tab_id,
190 int index);
191
192 // Sets the index of the selected tab in the specified window.
193 void SetSelectedTabInWindow(const SessionID& window_id, int index);
194
[email protected]8d0f3312012-08-18 01:47:53195 // Sets the user agent override of the specified tab.
196 void SetTabUserAgentOverride(const SessionID& window_id,
197 const SessionID& tab_id,
198 const std::string& user_agent_override);
199
skuhnec3aa8cf2014-10-30 06:02:45200 // Sets the application extension id of the specified tab.
201 void SetTabExtensionAppID(const SessionID& window_id,
202 const SessionID& tab_id,
203 const std::string& extension_app_id);
204
georgesak5582cbe2015-05-22 22:08:07205 // Sets the last active time of the tab.
206 void SetLastActiveTime(const SessionID& window_id,
207 const SessionID& tab_id,
208 base::TimeTicks last_active_time);
209
initial.commit09911bf2008-07-26 23:55:29210 // Fetches the contents of the last session, notifying the callback when
211 // done. If the callback is supplied an empty vector of SessionWindows
212 // it means the session could not be restored.
[email protected]e95b717f2014-02-06 13:47:13213 base::CancelableTaskTracker::TaskId GetLastSession(
blundell56789fe2015-09-10 05:11:16214 const sessions::GetLastSessionCallback& callback,
[email protected]e95b717f2014-02-06 13:47:13215 base::CancelableTaskTracker* tracker);
[email protected]3acb70ef2010-03-01 18:44:38216
skuhned1e250b2014-11-05 15:26:50217 // BaseSessionServiceDelegateImpl:
218 void OnSavedCommands() override;
[email protected]70eb8ed92010-06-10 18:13:12219
initial.commit09911bf2008-07-26 23:55:29220 private:
[email protected]bf219e9eb2012-10-10 15:20:38221 // Allow tests to access our innards for testing purposes.
skuhned1e250b2014-11-05 15:26:50222 FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, SavedSessionNotification);
[email protected]bf219e9eb2012-10-10 15:20:38223 FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, RestoreActivation1);
224 FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, RestoreActivation2);
aseren748ab62e2015-01-12 13:33:02225 FRIEND_TEST_ALL_PREFIXES(SessionServiceTest, RemoveUnusedRestoreWindowsTest);
[email protected]51e5bdc2012-12-06 06:12:56226 FRIEND_TEST_ALL_PREFIXES(NoStartupWindowTest, DontInitSessionServiceForApps);
[email protected]bf219e9eb2012-10-10 15:20:38227
[email protected]23da84e152010-07-14 01:29:05228 typedef std::map<SessionID::id_type, std::pair<int, int> > IdToRange;
[email protected]97d2c1e2009-11-05 03:55:05229
[email protected]169627b2008-12-06 19:30:19230 void Init();
initial.commit09911bf2008-07-26 23:55:29231
skuhnec3aa8cf2014-10-30 06:02:45232 // Returns true if a window of given |window_type| and |app_type| should get
233 // restored upon session restore.
skuhneb7409dcf2014-11-14 04:06:55234 bool ShouldRestoreWindowOfType(sessions::SessionWindow::WindowType type,
skuhnec3aa8cf2014-10-30 06:02:45235 AppType app_type) const;
236
237 // Removes unrestorable windows from the previous windows list.
skuhneb7409dcf2014-11-14 04:06:55238 void RemoveUnusedRestoreWindows(
239 std::vector<sessions::SessionWindow*>* window_list);
skuhnec3aa8cf2014-10-30 06:02:45240
[email protected]c9b19942010-03-26 15:58:08241 // Implementation of RestoreIfNecessary. If |browser| is non-null and we need
242 // to restore, the tabs are added to it, otherwise a new browser is created.
243 bool RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
244 Browser* browser);
245
Daniel Chenga542fca2014-10-21 09:51:29246 void Observe(int type,
247 const content::NotificationSource& source,
248 const content::NotificationDetails& details) override;
[email protected]534e54b2008-08-13 15:40:09249
[email protected]bf219e9eb2012-10-10 15:20:38250 // chrome::BrowserListObserver
Daniel Chenga542fca2014-10-21 09:51:29251 void OnBrowserAdded(Browser* browser) override {}
252 void OnBrowserRemoved(Browser* browser) override {}
253 void OnBrowserSetLastActive(Browser* browser) override;
[email protected]bf219e9eb2012-10-10 15:20:38254
[email protected]4c7f9c52012-12-11 21:51:30255 // Converts |commands| to SessionWindows and notifies the callback.
blundell56789fe2015-09-10 05:11:16256 void OnGotSessionCommands(const sessions::GetLastSessionCallback& callback,
skuhneb7409dcf2014-11-14 04:06:55257 ScopedVector<sessions::SessionCommand> commands);
initial.commit09911bf2008-07-26 23:55:29258
initial.commit09911bf2008-07-26 23:55:29259 // Adds commands to commands that will recreate the state of the specified
[email protected]81898992011-06-14 22:15:00260 // tab. This adds at most kMaxNavigationCountToPersist navigations (in each
261 // direction from the current navigation index).
initial.commit09911bf2008-07-26 23:55:29262 // A pair is added to tab_to_available_range indicating the range of
263 // indices that were written.
264 void BuildCommandsForTab(
265 const SessionID& window_id,
[email protected]b624ddc2012-11-15 18:04:13266 content::WebContents* tab,
initial.commit09911bf2008-07-26 23:55:29267 int index_in_window,
[email protected]5c0e6482009-07-14 20:20:09268 bool is_pinned,
initial.commit09911bf2008-07-26 23:55:29269 IdToRange* tab_to_available_range);
270
271 // Adds commands to create the specified browser, and invokes
272 // BuildCommandsForTab for each of the tabs in the browser. This ignores
273 // any tabs not in the profile we were created with.
274 void BuildCommandsForBrowser(
275 Browser* browser,
initial.commit09911bf2008-07-26 23:55:29276 IdToRange* tab_to_available_range,
277 std::set<SessionID::id_type>* windows_to_track);
278
279 // Iterates over all the known browsers invoking BuildCommandsForBrowser.
skuhnec3aa8cf2014-10-30 06:02:45280 // This only adds browsers that should be tracked (|ShouldRestoreWindowOfType|
281 // returns true). All browsers that are tracked are added to windows_to_track
282 // (as long as it is non-null).
initial.commit09911bf2008-07-26 23:55:29283 void BuildCommandsFromBrowsers(
initial.commit09911bf2008-07-26 23:55:29284 IdToRange* tab_to_available_range,
285 std::set<SessionID::id_type>* windows_to_track);
286
skuhnec3aa8cf2014-10-30 06:02:45287 // Schedules a reset of the existing commands. A reset means the contents
288 // of the file are recreated from the state of the browser.
289 void ScheduleResetCommands();
initial.commit09911bf2008-07-26 23:55:29290
skuhne57b5b03f2014-10-31 03:59:55291 // Schedules the specified command.
skuhneb7409dcf2014-11-14 04:06:55292 void ScheduleCommand(scoped_ptr<sessions::SessionCommand> command);
initial.commit09911bf2008-07-26 23:55:29293
294 // Converts all pending tab/window closes to commands and schedules them.
295 void CommitPendingCloses();
296
initial.commit09911bf2008-07-26 23:55:29297 // Returns true if there is only one window open with a single tab that shares
298 // our profile.
[email protected]bf219e9eb2012-10-10 15:20:38299 bool IsOnlyOneTabLeft() const;
initial.commit09911bf2008-07-26 23:55:29300
[email protected]982921f12009-10-27 21:43:53301 // Returns true if there are open trackable browser windows whose ids do
302 // match |window_id| with our profile. A trackable window is a window from
skuhnec3aa8cf2014-10-30 06:02:45303 // which |ShouldRestoreWindowOfType| returns true. See
304 // |ShouldRestoreWindowOfType| for details.
[email protected]bf219e9eb2012-10-10 15:20:38305 bool HasOpenTrackableBrowsers(const SessionID& window_id) const;
initial.commit09911bf2008-07-26 23:55:29306
307 // Returns true if changes to tabs in the specified window should be tracked.
[email protected]bf219e9eb2012-10-10 15:20:38308 bool ShouldTrackChangesToWindow(const SessionID& window_id) const;
309
310 // Returns true if we track changes to the specified browser.
311 bool ShouldTrackBrowser(Browser* browser) const;
initial.commit09911bf2008-07-26 23:55:29312
[email protected]23da84e152010-07-14 01:29:05313 // Call when certain session relevant notifications
314 // (tab_closed, nav_list_pruned) occur. In addition, this is
315 // currently called when Save() is called to compare how often the
316 // session data is currently saved verses when we may want to save it.
317 // It records the data in UMA stats.
[email protected]432115822011-07-10 15:52:27318 void RecordSessionUpdateHistogramData(int type,
[email protected]23da84e152010-07-14 01:29:05319 base::TimeTicks* last_updated_time);
320
321 // Helper methods to record the histogram data
322 void RecordUpdatedTabClosed(base::TimeDelta delta, bool use_long_period);
323 void RecordUpdatedNavListPruned(base::TimeDelta delta, bool use_long_period);
324 void RecordUpdatedNavEntryCommit(base::TimeDelta delta, bool use_long_period);
325 void RecordUpdatedSaveTime(base::TimeDelta delta, bool use_long_period);
326 void RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,
327 bool use_long_period);
[email protected]4070a6b2009-11-05 23:33:55328
[email protected]3efee4172014-01-23 13:53:36329 // Deletes session data if no windows are open for the current profile.
330 void MaybeDeleteSessionOnlyData();
331
skuhned1e250b2014-11-05 15:26:50332 // Unit test accessors.
skuhneb7409dcf2014-11-14 04:06:55333 sessions::BaseSessionService* GetBaseSessionServiceForTest();
skuhned1e250b2014-11-05 15:26:50334
skuhned58ab122014-10-22 20:23:02335 // The profile. This may be null during testing.
336 Profile* profile_;
337
skuhned1e250b2014-11-05 15:26:50338 // The owned BaseSessionService.
skuhneb7409dcf2014-11-14 04:06:55339 scoped_ptr<sessions::BaseSessionService> base_session_service_;
skuhned1e250b2014-11-05 15:26:50340
[email protected]6c2381d2011-10-19 02:52:53341 content::NotificationRegistrar registrar_;
[email protected]6a02963e2009-01-06 16:58:03342
initial.commit09911bf2008-07-26 23:55:29343 // Maps from session tab id to the range of navigation entries that has
344 // been written to disk.
345 //
346 // This is only used if not all the navigation entries have been
347 // written.
348 IdToRange tab_to_available_range_;
349
[email protected]6ea265a2008-10-30 02:58:36350 // When the user closes the last window, where the last window is the
initial.commit09911bf2008-07-26 23:55:29351 // last tabbed browser and no more tabbed browsers are open with the same
352 // profile, the window ID is added here. These IDs are only committed (which
353 // marks them as closed) if the user creates a new tabbed browser.
354 typedef std::set<SessionID::id_type> PendingWindowCloseIDs;
355 PendingWindowCloseIDs pending_window_close_ids_;
356
357 // Set of tabs that have been closed by way of the last window or last tab
358 // closing, but not yet committed.
359 typedef std::set<SessionID::id_type> PendingTabCloseIDs;
360 PendingTabCloseIDs pending_tab_close_ids_;
361
362 // When a window other than the last window (see description of
363 // pending_window_close_ids) is closed, the id is added to this set.
364 typedef std::set<SessionID::id_type> WindowClosingIDs;
365 WindowClosingIDs window_closing_ids_;
366
367 // Set of windows we're tracking changes to. This is only browsers that
skuhnec3aa8cf2014-10-30 06:02:45368 // return true from |ShouldRestoreWindowOfType|.
initial.commit09911bf2008-07-26 23:55:29369 typedef std::set<SessionID::id_type> WindowsTracking;
370 WindowsTracking windows_tracking_;
371
[email protected]982921f12009-10-27 21:43:53372 // Are there any open trackable browsers?
373 bool has_open_trackable_browsers_;
initial.commit09911bf2008-07-26 23:55:29374
[email protected]6ea265a2008-10-30 02:58:36375 // If true and a new tabbed browser is created and there are no opened tabbed
[email protected]982921f12009-10-27 21:43:53376 // browser (has_open_trackable_browsers_ is false), then the current session
[email protected]668cd422011-04-06 21:00:01377 // is made the last session. See description above class for details on
378 // current/last session.
[email protected]6ea265a2008-10-30 02:58:36379 bool move_on_new_browser_;
[email protected]169627b2008-12-06 19:30:19380
[email protected]47db9a92011-01-24 20:10:13381 // Used for reporting frequency of session altering operations.
[email protected]23da84e152010-07-14 01:29:05382 base::TimeTicks last_updated_tab_closed_time_;
383 base::TimeTicks last_updated_nav_list_pruned_time_;
384 base::TimeTicks last_updated_nav_entry_commit_time_;
385 base::TimeTicks last_updated_save_time_;
386
387 // Constants used in calculating histogram data.
388 const base::TimeDelta save_delay_in_millis_;
389 const base::TimeDelta save_delay_in_mins_;
390 const base::TimeDelta save_delay_in_hrs_;
[email protected]70eb8ed92010-06-10 18:13:12391
[email protected]c4340bc02012-04-25 02:49:38392 // For browser_tests, since we want to simulate the browser shutting down
393 // without quitting.
394 bool force_browser_not_alive_with_no_windows_;
395
sky1a14f492014-08-26 21:44:26396 base::WeakPtrFactory<SessionService> weak_factory_;
397
[email protected]169627b2008-12-06 19:30:19398 DISALLOW_COPY_AND_ASSIGN(SessionService);
initial.commit09911bf2008-07-26 23:55:29399};
400
[email protected]169627b2008-12-06 19:30:19401#endif // CHROME_BROWSER_SESSIONS_SESSION_SERVICE_H_