• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "chrome/browser/sessions/session_service.h"
6 
7 #include <algorithm>
8 #include <set>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/command_line.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/metrics/histogram.h"
17 #include "base/pickle.h"
18 #include "base/threading/thread.h"
19 #include "chrome/browser/background/background_mode_manager.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/defaults.h"
23 #include "chrome/browser/extensions/tab_helper.h"
24 #include "chrome/browser/prefs/session_startup_pref.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/sessions/session_backend.h"
27 #include "chrome/browser/sessions/session_command.h"
28 #include "chrome/browser/sessions/session_data_deleter.h"
29 #include "chrome/browser/sessions/session_restore.h"
30 #include "chrome/browser/sessions/session_tab_helper.h"
31 #include "chrome/browser/sessions/session_types.h"
32 #include "chrome/browser/ui/browser_iterator.h"
33 #include "chrome/browser/ui/browser_list.h"
34 #include "chrome/browser/ui/browser_tabstrip.h"
35 #include "chrome/browser/ui/browser_window.h"
36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/browser/ui/startup/startup_browser_creator.h"
38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
39 #include "components/startup_metric_utils/startup_metric_utils.h"
40 #include "content/public/browser/navigation_details.h"
41 #include "content/public/browser/navigation_entry.h"
42 #include "content/public/browser/notification_details.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/session_storage_namespace.h"
45 #include "content/public/browser/web_contents.h"
46 #include "extensions/common/extension.h"
47 
48 #if defined(OS_MACOSX)
49 #include "chrome/browser/app_controller_mac.h"
50 #endif
51 
52 using base::Time;
53 using content::NavigationEntry;
54 using content::WebContents;
55 using sessions::SerializedNavigationEntry;
56 
57 // Identifier for commands written to file.
58 static const SessionCommand::id_type kCommandSetTabWindow = 0;
59 // OBSOLETE Superseded by kCommandSetWindowBounds3.
60 // static const SessionCommand::id_type kCommandSetWindowBounds = 1;
61 static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2;
62 // Original kCommandTabClosed/kCommandWindowClosed. See comment in
63 // MigrateClosedPayload for details on why they were replaced.
64 static const SessionCommand::id_type kCommandTabClosedObsolete = 3;
65 static const SessionCommand::id_type kCommandWindowClosedObsolete = 4;
66 static const SessionCommand::id_type
67     kCommandTabNavigationPathPrunedFromBack = 5;
68 static const SessionCommand::id_type kCommandUpdateTabNavigation = 6;
69 static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7;
70 static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8;
71 static const SessionCommand::id_type kCommandSetWindowType = 9;
72 // OBSOLETE Superseded by kCommandSetWindowBounds3. Except for data migration.
73 // static const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
74 static const SessionCommand::id_type
75     kCommandTabNavigationPathPrunedFromFront = 11;
76 static const SessionCommand::id_type kCommandSetPinnedState = 12;
77 static const SessionCommand::id_type kCommandSetExtensionAppID = 13;
78 static const SessionCommand::id_type kCommandSetWindowBounds3 = 14;
79 static const SessionCommand::id_type kCommandSetWindowAppName = 15;
80 static const SessionCommand::id_type kCommandTabClosed = 16;
81 static const SessionCommand::id_type kCommandWindowClosed = 17;
82 static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 18;
83 static const SessionCommand::id_type kCommandSessionStorageAssociated = 19;
84 static const SessionCommand::id_type kCommandSetActiveWindow = 20;
85 
86 // Every kWritesPerReset commands triggers recreating the file.
87 static const int kWritesPerReset = 250;
88 
89 namespace {
90 
91 // Various payload structures.
92 struct ClosedPayload {
93   SessionID::id_type id;
94   int64 close_time;
95 };
96 
97 struct WindowBoundsPayload2 {
98   SessionID::id_type window_id;
99   int32 x;
100   int32 y;
101   int32 w;
102   int32 h;
103   bool is_maximized;
104 };
105 
106 struct WindowBoundsPayload3 {
107   SessionID::id_type window_id;
108   int32 x;
109   int32 y;
110   int32 w;
111   int32 h;
112   int32 show_state;
113 };
114 
115 typedef SessionID::id_type ActiveWindowPayload;
116 
117 struct IDAndIndexPayload {
118   SessionID::id_type id;
119   int32 index;
120 };
121 
122 typedef IDAndIndexPayload TabIndexInWindowPayload;
123 
124 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload;
125 
126 typedef IDAndIndexPayload SelectedNavigationIndexPayload;
127 
128 typedef IDAndIndexPayload SelectedTabInIndexPayload;
129 
130 typedef IDAndIndexPayload WindowTypePayload;
131 
132 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
133 
134 struct PinnedStatePayload {
135   SessionID::id_type tab_id;
136   bool pinned_state;
137 };
138 
139 // Returns the show state to store to disk based |state|.
AdjustShowState(ui::WindowShowState state)140 ui::WindowShowState AdjustShowState(ui::WindowShowState state) {
141   switch (state) {
142     case ui::SHOW_STATE_NORMAL:
143     case ui::SHOW_STATE_MINIMIZED:
144     case ui::SHOW_STATE_MAXIMIZED:
145     case ui::SHOW_STATE_FULLSCREEN:
146     case ui::SHOW_STATE_DETACHED:
147       return state;
148 
149     case ui::SHOW_STATE_DEFAULT:
150     case ui::SHOW_STATE_INACTIVE:
151     case ui::SHOW_STATE_END:
152       return ui::SHOW_STATE_NORMAL;
153   }
154   return ui::SHOW_STATE_NORMAL;
155 }
156 
157 // Migrates a |ClosedPayload|, returning true on success (migration was
158 // necessary and happened), or false (migration was not necessary or was not
159 // successful).
MigrateClosedPayload(const SessionCommand & command,ClosedPayload * payload)160 bool MigrateClosedPayload(const SessionCommand& command,
161                           ClosedPayload* payload) {
162 #if defined(OS_CHROMEOS)
163   // Pre M17 versions of chromeos were 32bit. Post M17 is 64 bit. Apparently the
164   // 32 bit versions of chrome on pre M17 resulted in a sizeof 12 for the
165   // ClosedPayload, where as post M17 64-bit gives a sizeof 16 (presumably the
166   // struct is padded).
167   if ((command.id() == kCommandWindowClosedObsolete ||
168        command.id() == kCommandTabClosedObsolete) &&
169       command.size() == 12 && sizeof(payload->id) == 4 &&
170       sizeof(payload->close_time) == 8) {
171     memcpy(&payload->id, command.contents(), 4);
172     memcpy(&payload->close_time, command.contents() + 4, 8);
173     return true;
174   } else {
175     return false;
176   }
177 #else
178   return false;
179 #endif
180 }
181 
182 }  // namespace
183 
184 // SessionService -------------------------------------------------------------
185 
SessionService(Profile * profile)186 SessionService::SessionService(Profile* profile)
187     : BaseSessionService(SESSION_RESTORE, profile, base::FilePath()),
188       has_open_trackable_browsers_(false),
189       move_on_new_browser_(false),
190       save_delay_in_millis_(base::TimeDelta::FromMilliseconds(2500)),
191       save_delay_in_mins_(base::TimeDelta::FromMinutes(10)),
192       save_delay_in_hrs_(base::TimeDelta::FromHours(8)),
193       force_browser_not_alive_with_no_windows_(false) {
194   Init();
195 }
196 
SessionService(const base::FilePath & save_path)197 SessionService::SessionService(const base::FilePath& save_path)
198     : BaseSessionService(SESSION_RESTORE, NULL, save_path),
199       has_open_trackable_browsers_(false),
200       move_on_new_browser_(false),
201       save_delay_in_millis_(base::TimeDelta::FromMilliseconds(2500)),
202       save_delay_in_mins_(base::TimeDelta::FromMinutes(10)),
203       save_delay_in_hrs_(base::TimeDelta::FromHours(8)),
204       force_browser_not_alive_with_no_windows_(false)  {
205   Init();
206 }
207 
~SessionService()208 SessionService::~SessionService() {
209   // The BrowserList should outlive the SessionService since it's static and
210   // the SessionService is a BrowserContextKeyedService.
211   BrowserList::RemoveObserver(this);
212   Save();
213 }
214 
RestoreIfNecessary(const std::vector<GURL> & urls_to_open)215 bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open) {
216   return RestoreIfNecessary(urls_to_open, NULL);
217 }
218 
ResetFromCurrentBrowsers()219 void SessionService::ResetFromCurrentBrowsers() {
220   ScheduleReset();
221 }
222 
MoveCurrentSessionToLastSession()223 void SessionService::MoveCurrentSessionToLastSession() {
224   pending_tab_close_ids_.clear();
225   window_closing_ids_.clear();
226   pending_window_close_ids_.clear();
227 
228   Save();
229 
230   RunTaskOnBackendThread(
231       FROM_HERE, base::Bind(&SessionBackend::MoveCurrentSessionToLastSession,
232                             backend()));
233 }
234 
SetTabWindow(const SessionID & window_id,const SessionID & tab_id)235 void SessionService::SetTabWindow(const SessionID& window_id,
236                                   const SessionID& tab_id) {
237   if (!ShouldTrackChangesToWindow(window_id))
238     return;
239 
240   ScheduleCommand(CreateSetTabWindowCommand(window_id, tab_id));
241 }
242 
SetWindowBounds(const SessionID & window_id,const gfx::Rect & bounds,ui::WindowShowState show_state)243 void SessionService::SetWindowBounds(const SessionID& window_id,
244                                      const gfx::Rect& bounds,
245                                      ui::WindowShowState show_state) {
246   if (!ShouldTrackChangesToWindow(window_id))
247     return;
248 
249   ScheduleCommand(CreateSetWindowBoundsCommand(window_id, bounds, show_state));
250 }
251 
SetTabIndexInWindow(const SessionID & window_id,const SessionID & tab_id,int new_index)252 void SessionService::SetTabIndexInWindow(const SessionID& window_id,
253                                          const SessionID& tab_id,
254                                          int new_index) {
255   if (!ShouldTrackChangesToWindow(window_id))
256     return;
257 
258   ScheduleCommand(CreateSetTabIndexInWindowCommand(tab_id, new_index));
259 }
260 
SetPinnedState(const SessionID & window_id,const SessionID & tab_id,bool is_pinned)261 void SessionService::SetPinnedState(const SessionID& window_id,
262                                     const SessionID& tab_id,
263                                     bool is_pinned) {
264   if (!ShouldTrackChangesToWindow(window_id))
265     return;
266 
267   ScheduleCommand(CreatePinnedStateCommand(tab_id, is_pinned));
268 }
269 
TabClosed(const SessionID & window_id,const SessionID & tab_id,bool closed_by_user_gesture)270 void SessionService::TabClosed(const SessionID& window_id,
271                                const SessionID& tab_id,
272                                bool closed_by_user_gesture) {
273   if (!tab_id.id())
274     return;  // Hapens when the tab is replaced.
275 
276   if (!ShouldTrackChangesToWindow(window_id))
277     return;
278 
279   IdToRange::iterator i = tab_to_available_range_.find(tab_id.id());
280   if (i != tab_to_available_range_.end())
281     tab_to_available_range_.erase(i);
282 
283   if (find(pending_window_close_ids_.begin(), pending_window_close_ids_.end(),
284            window_id.id()) != pending_window_close_ids_.end()) {
285     // Tab is in last window. Don't commit it immediately, instead add it to the
286     // list of tabs to close. If the user creates another window, the close is
287     // committed.
288     pending_tab_close_ids_.insert(tab_id.id());
289   } else if (find(window_closing_ids_.begin(), window_closing_ids_.end(),
290                   window_id.id()) != window_closing_ids_.end() ||
291              !IsOnlyOneTabLeft() ||
292              closed_by_user_gesture) {
293     // Close is the result of one of the following:
294     // . window close (and it isn't the last window).
295     // . closing a tab and there are other windows/tabs open.
296     // . closed by a user gesture.
297     // In all cases we need to mark the tab as explicitly closed.
298     ScheduleCommand(CreateTabClosedCommand(tab_id.id()));
299   } else {
300     // User closed the last tab in the last tabbed browser. Don't mark the
301     // tab closed.
302     pending_tab_close_ids_.insert(tab_id.id());
303     has_open_trackable_browsers_ = false;
304   }
305 }
306 
WindowOpened(Browser * browser)307 void SessionService::WindowOpened(Browser* browser) {
308   if (!ShouldTrackBrowser(browser))
309     return;
310 
311   AppType app_type = browser->is_app() ? TYPE_APP : TYPE_NORMAL;
312   RestoreIfNecessary(std::vector<GURL>(), browser);
313   SetWindowType(browser->session_id(), browser->type(), app_type);
314   SetWindowAppName(browser->session_id(), browser->app_name());
315 }
316 
WindowClosing(const SessionID & window_id)317 void SessionService::WindowClosing(const SessionID& window_id) {
318   if (!ShouldTrackChangesToWindow(window_id))
319     return;
320 
321   // The window is about to close. If there are other tabbed browsers with the
322   // same original profile commit the close immediately.
323   //
324   // NOTE: if the user chooses the exit menu item session service is destroyed
325   // and this code isn't hit.
326   if (has_open_trackable_browsers_) {
327     // Closing a window can never make has_open_trackable_browsers_ go from
328     // false to true, so only update it if already true.
329     has_open_trackable_browsers_ = HasOpenTrackableBrowsers(window_id);
330   }
331   if (should_record_close_as_pending())
332     pending_window_close_ids_.insert(window_id.id());
333   else
334     window_closing_ids_.insert(window_id.id());
335 }
336 
WindowClosed(const SessionID & window_id)337 void SessionService::WindowClosed(const SessionID& window_id) {
338   if (!ShouldTrackChangesToWindow(window_id)) {
339     // The last window may be one that is not tracked.
340     MaybeDeleteSessionOnlyData();
341     return;
342   }
343 
344   windows_tracking_.erase(window_id.id());
345 
346   if (window_closing_ids_.find(window_id.id()) != window_closing_ids_.end()) {
347     window_closing_ids_.erase(window_id.id());
348     ScheduleCommand(CreateWindowClosedCommand(window_id.id()));
349   } else if (pending_window_close_ids_.find(window_id.id()) ==
350              pending_window_close_ids_.end()) {
351     // We'll hit this if user closed the last tab in a window.
352     has_open_trackable_browsers_ = HasOpenTrackableBrowsers(window_id);
353     if (should_record_close_as_pending())
354       pending_window_close_ids_.insert(window_id.id());
355     else
356       ScheduleCommand(CreateWindowClosedCommand(window_id.id()));
357   }
358   MaybeDeleteSessionOnlyData();
359 }
360 
SetWindowType(const SessionID & window_id,Browser::Type type,AppType app_type)361 void SessionService::SetWindowType(const SessionID& window_id,
362                                    Browser::Type type,
363                                    AppType app_type) {
364   if (!should_track_changes_for_browser_type(type, app_type))
365     return;
366 
367   windows_tracking_.insert(window_id.id());
368 
369   // The user created a new tabbed browser with our profile. Commit any
370   // pending closes.
371   CommitPendingCloses();
372 
373   has_open_trackable_browsers_ = true;
374   move_on_new_browser_ = true;
375 
376   ScheduleCommand(
377       CreateSetWindowTypeCommand(window_id, WindowTypeForBrowserType(type)));
378 }
379 
SetWindowAppName(const SessionID & window_id,const std::string & app_name)380 void SessionService::SetWindowAppName(
381     const SessionID& window_id,
382     const std::string& app_name) {
383   if (!ShouldTrackChangesToWindow(window_id))
384     return;
385 
386   ScheduleCommand(CreateSetTabExtensionAppIDCommand(
387                       kCommandSetWindowAppName,
388                       window_id.id(),
389                       app_name));
390 }
391 
TabNavigationPathPrunedFromBack(const SessionID & window_id,const SessionID & tab_id,int count)392 void SessionService::TabNavigationPathPrunedFromBack(const SessionID& window_id,
393                                                      const SessionID& tab_id,
394                                                      int count) {
395   if (!ShouldTrackChangesToWindow(window_id))
396     return;
397 
398   TabNavigationPathPrunedFromBackPayload payload = { 0 };
399   payload.id = tab_id.id();
400   payload.index = count;
401   SessionCommand* command =
402       new SessionCommand(kCommandTabNavigationPathPrunedFromBack,
403                          sizeof(payload));
404   memcpy(command->contents(), &payload, sizeof(payload));
405   ScheduleCommand(command);
406 }
407 
TabNavigationPathPrunedFromFront(const SessionID & window_id,const SessionID & tab_id,int count)408 void SessionService::TabNavigationPathPrunedFromFront(
409     const SessionID& window_id,
410     const SessionID& tab_id,
411     int count) {
412   if (!ShouldTrackChangesToWindow(window_id))
413     return;
414 
415   // Update the range of indices.
416   if (tab_to_available_range_.find(tab_id.id()) !=
417       tab_to_available_range_.end()) {
418     std::pair<int, int>& range = tab_to_available_range_[tab_id.id()];
419     range.first = std::max(0, range.first - count);
420     range.second = std::max(0, range.second - count);
421   }
422 
423   TabNavigationPathPrunedFromFrontPayload payload = { 0 };
424   payload.id = tab_id.id();
425   payload.index = count;
426   SessionCommand* command =
427       new SessionCommand(kCommandTabNavigationPathPrunedFromFront,
428                          sizeof(payload));
429   memcpy(command->contents(), &payload, sizeof(payload));
430   ScheduleCommand(command);
431 }
432 
UpdateTabNavigation(const SessionID & window_id,const SessionID & tab_id,const SerializedNavigationEntry & navigation)433 void SessionService::UpdateTabNavigation(
434     const SessionID& window_id,
435     const SessionID& tab_id,
436     const SerializedNavigationEntry& navigation) {
437   if (!ShouldTrackEntry(navigation.virtual_url()) ||
438       !ShouldTrackChangesToWindow(window_id)) {
439     return;
440   }
441 
442   if (tab_to_available_range_.find(tab_id.id()) !=
443       tab_to_available_range_.end()) {
444     std::pair<int, int>& range = tab_to_available_range_[tab_id.id()];
445     range.first = std::min(navigation.index(), range.first);
446     range.second = std::max(navigation.index(), range.second);
447   }
448   ScheduleCommand(CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
449                                                    tab_id.id(), navigation));
450 }
451 
TabRestored(WebContents * tab,bool pinned)452 void SessionService::TabRestored(WebContents* tab, bool pinned) {
453   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(tab);
454   if (!ShouldTrackChangesToWindow(session_tab_helper->window_id()))
455     return;
456 
457   BuildCommandsForTab(session_tab_helper->window_id(), tab, -1,
458                       pinned, &pending_commands(), NULL);
459   StartSaveTimer();
460 }
461 
SetSelectedNavigationIndex(const SessionID & window_id,const SessionID & tab_id,int index)462 void SessionService::SetSelectedNavigationIndex(const SessionID& window_id,
463                                                 const SessionID& tab_id,
464                                                 int index) {
465   if (!ShouldTrackChangesToWindow(window_id))
466     return;
467 
468   if (tab_to_available_range_.find(tab_id.id()) !=
469       tab_to_available_range_.end()) {
470     if (index < tab_to_available_range_[tab_id.id()].first ||
471         index > tab_to_available_range_[tab_id.id()].second) {
472       // The new index is outside the range of what we've archived, schedule
473       // a reset.
474       ResetFromCurrentBrowsers();
475       return;
476     }
477   }
478   ScheduleCommand(CreateSetSelectedNavigationIndexCommand(tab_id, index));
479 }
480 
SetSelectedTabInWindow(const SessionID & window_id,int index)481 void SessionService::SetSelectedTabInWindow(const SessionID& window_id,
482                                             int index) {
483   if (!ShouldTrackChangesToWindow(window_id))
484     return;
485 
486   ScheduleCommand(CreateSetSelectedTabInWindow(window_id, index));
487 }
488 
SetTabUserAgentOverride(const SessionID & window_id,const SessionID & tab_id,const std::string & user_agent_override)489 void SessionService::SetTabUserAgentOverride(
490     const SessionID& window_id,
491     const SessionID& tab_id,
492     const std::string& user_agent_override) {
493   if (!ShouldTrackChangesToWindow(window_id))
494     return;
495 
496   ScheduleCommand(CreateSetTabUserAgentOverrideCommand(
497       kCommandSetTabUserAgentOverride, tab_id.id(), user_agent_override));
498 }
499 
GetLastSession(const SessionCallback & callback,CancelableTaskTracker * tracker)500 CancelableTaskTracker::TaskId SessionService::GetLastSession(
501     const SessionCallback& callback,
502     CancelableTaskTracker* tracker) {
503   // OnGotSessionCommands maps the SessionCommands to browser state, then run
504   // the callback.
505   return ScheduleGetLastSessionCommands(
506       base::Bind(&SessionService::OnGotSessionCommands,
507                  base::Unretained(this), callback),
508       tracker);
509 }
510 
Save()511 void SessionService::Save() {
512   bool had_commands = !pending_commands().empty();
513   BaseSessionService::Save();
514   if (had_commands) {
515     RecordSessionUpdateHistogramData(chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
516                                      &last_updated_save_time_);
517     content::NotificationService::current()->Notify(
518         chrome::NOTIFICATION_SESSION_SERVICE_SAVED,
519         content::Source<Profile>(profile()),
520         content::NotificationService::NoDetails());
521   }
522 }
523 
Init()524 void SessionService::Init() {
525   // Register for the notifications we're interested in.
526   registrar_.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED,
527                  content::NotificationService::AllSources());
528   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED,
529                  content::NotificationService::AllSources());
530   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
531                  content::NotificationService::AllSources());
532   registrar_.Add(
533       this, chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
534       content::NotificationService::AllSources());
535 
536   BrowserList::AddObserver(this);
537 }
538 
processed_any_commands()539 bool SessionService::processed_any_commands() {
540   return backend()->inited() || !pending_commands().empty();
541 }
542 
ShouldNewWindowStartSession()543 bool SessionService::ShouldNewWindowStartSession() {
544   // ChromeOS and OSX have different ideas of application lifetime than
545   // the other platforms.
546   // On ChromeOS opening a new window should never start a new session.
547 #if defined(OS_CHROMEOS)
548   if (!force_browser_not_alive_with_no_windows_)
549     return false;
550 #endif
551   if (!has_open_trackable_browsers_ &&
552       !StartupBrowserCreator::InSynchronousProfileLaunch() &&
553       !SessionRestore::IsRestoring(profile())
554 #if defined(OS_MACOSX)
555       // On OSX, a new window should not start a new session if it was opened
556       // from the dock or the menubar.
557       && !app_controller_mac::IsOpeningNewWindow()
558 #endif  // OS_MACOSX
559       ) {
560     return true;
561   }
562   return false;
563 }
564 
RestoreIfNecessary(const std::vector<GURL> & urls_to_open,Browser * browser)565 bool SessionService::RestoreIfNecessary(const std::vector<GURL>& urls_to_open,
566                                         Browser* browser) {
567   if (ShouldNewWindowStartSession()) {
568     // We're going from no tabbed browsers to a tabbed browser (and not in
569     // process startup), restore the last session.
570     if (move_on_new_browser_) {
571       // Make the current session the last.
572       MoveCurrentSessionToLastSession();
573       move_on_new_browser_ = false;
574     }
575     SessionStartupPref pref = StartupBrowserCreator::GetSessionStartupPref(
576         *CommandLine::ForCurrentProcess(), profile());
577     if (pref.type == SessionStartupPref::LAST) {
578       SessionRestore::RestoreSession(
579           profile(), browser,
580           browser ? browser->host_desktop_type() : chrome::GetActiveDesktop(),
581           browser ? 0 : SessionRestore::ALWAYS_CREATE_TABBED_BROWSER,
582           urls_to_open);
583       return true;
584     }
585   }
586   return false;
587 }
588 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)589 void SessionService::Observe(int type,
590                              const content::NotificationSource& source,
591                              const content::NotificationDetails& details) {
592   // All of our messages have the NavigationController as the source.
593   switch (type) {
594     case content::NOTIFICATION_NAV_LIST_PRUNED: {
595       WebContents* web_contents =
596           content::Source<content::NavigationController>(source).ptr()->
597               GetWebContents();
598       SessionTabHelper* session_tab_helper =
599           SessionTabHelper::FromWebContents(web_contents);
600       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
601         return;
602       content::Details<content::PrunedDetails> pruned_details(details);
603       if (pruned_details->from_front) {
604         TabNavigationPathPrunedFromFront(
605             session_tab_helper->window_id(),
606             session_tab_helper->session_id(),
607             pruned_details->count);
608       } else {
609         TabNavigationPathPrunedFromBack(
610             session_tab_helper->window_id(),
611             session_tab_helper->session_id(),
612             web_contents->GetController().GetEntryCount());
613       }
614       RecordSessionUpdateHistogramData(type,
615                                        &last_updated_nav_list_pruned_time_);
616       break;
617     }
618 
619     case content::NOTIFICATION_NAV_ENTRY_CHANGED: {
620       WebContents* web_contents =
621           content::Source<content::NavigationController>(source).ptr()->
622               GetWebContents();
623       SessionTabHelper* session_tab_helper =
624           SessionTabHelper::FromWebContents(web_contents);
625       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
626         return;
627       content::Details<content::EntryChangedDetails> changed(details);
628       const SerializedNavigationEntry navigation =
629           SerializedNavigationEntry::FromNavigationEntry(
630               changed->index, *changed->changed_entry);
631       UpdateTabNavigation(session_tab_helper->window_id(),
632                           session_tab_helper->session_id(),
633                           navigation);
634       break;
635     }
636 
637     case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
638       WebContents* web_contents =
639           content::Source<content::NavigationController>(source).ptr()->
640               GetWebContents();
641       SessionTabHelper* session_tab_helper =
642           SessionTabHelper::FromWebContents(web_contents);
643       if (!session_tab_helper || web_contents->GetBrowserContext() != profile())
644         return;
645       int current_entry_index =
646           web_contents->GetController().GetCurrentEntryIndex();
647       SetSelectedNavigationIndex(
648           session_tab_helper->window_id(),
649           session_tab_helper->session_id(),
650           current_entry_index);
651       const SerializedNavigationEntry navigation =
652           SerializedNavigationEntry::FromNavigationEntry(
653               current_entry_index,
654               *web_contents->GetController().GetEntryAtIndex(
655                   current_entry_index));
656       UpdateTabNavigation(
657           session_tab_helper->window_id(),
658           session_tab_helper->session_id(),
659           navigation);
660       content::Details<content::LoadCommittedDetails> changed(details);
661       if (changed->type == content::NAVIGATION_TYPE_NEW_PAGE ||
662         changed->type == content::NAVIGATION_TYPE_EXISTING_PAGE) {
663         RecordSessionUpdateHistogramData(type,
664                                          &last_updated_nav_entry_commit_time_);
665       }
666       break;
667     }
668 
669     case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
670       extensions::TabHelper* extension_tab_helper =
671           content::Source<extensions::TabHelper>(source).ptr();
672       if (extension_tab_helper->web_contents()->GetBrowserContext() !=
673               profile()) {
674         return;
675       }
676       if (extension_tab_helper->extension_app()) {
677         SessionTabHelper* session_tab_helper =
678             SessionTabHelper::FromWebContents(
679                 extension_tab_helper->web_contents());
680         SetTabExtensionAppID(session_tab_helper->window_id(),
681                              session_tab_helper->session_id(),
682                              extension_tab_helper->extension_app()->id());
683       }
684       break;
685     }
686 
687     default:
688       NOTREACHED();
689   }
690 }
691 
OnBrowserSetLastActive(Browser * browser)692 void SessionService::OnBrowserSetLastActive(Browser* browser) {
693   if (ShouldTrackBrowser(browser))
694     ScheduleCommand(CreateSetActiveWindowCommand(browser->session_id()));
695 }
696 
SetTabExtensionAppID(const SessionID & window_id,const SessionID & tab_id,const std::string & extension_app_id)697 void SessionService::SetTabExtensionAppID(
698     const SessionID& window_id,
699     const SessionID& tab_id,
700     const std::string& extension_app_id) {
701   if (!ShouldTrackChangesToWindow(window_id))
702     return;
703 
704   ScheduleCommand(CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID,
705       tab_id.id(), extension_app_id));
706 }
707 
CreateSetSelectedTabInWindow(const SessionID & window_id,int index)708 SessionCommand* SessionService::CreateSetSelectedTabInWindow(
709     const SessionID& window_id,
710     int index) {
711   SelectedTabInIndexPayload payload = { 0 };
712   payload.id = window_id.id();
713   payload.index = index;
714   SessionCommand* command = new SessionCommand(kCommandSetSelectedTabInIndex,
715                                  sizeof(payload));
716   memcpy(command->contents(), &payload, sizeof(payload));
717   return command;
718 }
719 
CreateSetTabWindowCommand(const SessionID & window_id,const SessionID & tab_id)720 SessionCommand* SessionService::CreateSetTabWindowCommand(
721     const SessionID& window_id,
722     const SessionID& tab_id) {
723   SessionID::id_type payload[] = { window_id.id(), tab_id.id() };
724   SessionCommand* command =
725       new SessionCommand(kCommandSetTabWindow, sizeof(payload));
726   memcpy(command->contents(), payload, sizeof(payload));
727   return command;
728 }
729 
CreateSetWindowBoundsCommand(const SessionID & window_id,const gfx::Rect & bounds,ui::WindowShowState show_state)730 SessionCommand* SessionService::CreateSetWindowBoundsCommand(
731     const SessionID& window_id,
732     const gfx::Rect& bounds,
733     ui::WindowShowState show_state) {
734   WindowBoundsPayload3 payload = { 0 };
735   payload.window_id = window_id.id();
736   payload.x = bounds.x();
737   payload.y = bounds.y();
738   payload.w = bounds.width();
739   payload.h = bounds.height();
740   payload.show_state = AdjustShowState(show_state);
741   SessionCommand* command = new SessionCommand(kCommandSetWindowBounds3,
742                                                sizeof(payload));
743   memcpy(command->contents(), &payload, sizeof(payload));
744   return command;
745 }
746 
CreateSetTabIndexInWindowCommand(const SessionID & tab_id,int new_index)747 SessionCommand* SessionService::CreateSetTabIndexInWindowCommand(
748     const SessionID& tab_id,
749     int new_index) {
750   TabIndexInWindowPayload payload = { 0 };
751   payload.id = tab_id.id();
752   payload.index = new_index;
753   SessionCommand* command =
754       new SessionCommand(kCommandSetTabIndexInWindow, sizeof(payload));
755   memcpy(command->contents(), &payload, sizeof(payload));
756   return command;
757 }
758 
CreateTabClosedCommand(const SessionID::id_type tab_id)759 SessionCommand* SessionService::CreateTabClosedCommand(
760     const SessionID::id_type tab_id) {
761   ClosedPayload payload;
762   // Because of what appears to be a compiler bug setting payload to {0} doesn't
763   // set the padding to 0, resulting in Purify reporting an UMR when we write
764   // the structure to disk. To avoid this we explicitly memset the struct.
765   memset(&payload, 0, sizeof(payload));
766   payload.id = tab_id;
767   payload.close_time = Time::Now().ToInternalValue();
768   SessionCommand* command =
769       new SessionCommand(kCommandTabClosed, sizeof(payload));
770   memcpy(command->contents(), &payload, sizeof(payload));
771   return command;
772 }
773 
CreateWindowClosedCommand(const SessionID::id_type window_id)774 SessionCommand* SessionService::CreateWindowClosedCommand(
775     const SessionID::id_type window_id) {
776   ClosedPayload payload;
777   // See comment in CreateTabClosedCommand as to why we do this.
778   memset(&payload, 0, sizeof(payload));
779   payload.id = window_id;
780   payload.close_time = Time::Now().ToInternalValue();
781   SessionCommand* command =
782       new SessionCommand(kCommandWindowClosed, sizeof(payload));
783   memcpy(command->contents(), &payload, sizeof(payload));
784   return command;
785 }
786 
CreateSetSelectedNavigationIndexCommand(const SessionID & tab_id,int index)787 SessionCommand* SessionService::CreateSetSelectedNavigationIndexCommand(
788     const SessionID& tab_id,
789     int index) {
790   SelectedNavigationIndexPayload payload = { 0 };
791   payload.id = tab_id.id();
792   payload.index = index;
793   SessionCommand* command = new SessionCommand(
794       kCommandSetSelectedNavigationIndex, sizeof(payload));
795   memcpy(command->contents(), &payload, sizeof(payload));
796   return command;
797 }
798 
CreateSetWindowTypeCommand(const SessionID & window_id,WindowType type)799 SessionCommand* SessionService::CreateSetWindowTypeCommand(
800     const SessionID& window_id,
801     WindowType type) {
802   WindowTypePayload payload = { 0 };
803   payload.id = window_id.id();
804   payload.index = static_cast<int32>(type);
805   SessionCommand* command = new SessionCommand(
806       kCommandSetWindowType, sizeof(payload));
807   memcpy(command->contents(), &payload, sizeof(payload));
808   return command;
809 }
810 
CreatePinnedStateCommand(const SessionID & tab_id,bool is_pinned)811 SessionCommand* SessionService::CreatePinnedStateCommand(
812     const SessionID& tab_id,
813     bool is_pinned) {
814   PinnedStatePayload payload = { 0 };
815   payload.tab_id = tab_id.id();
816   payload.pinned_state = is_pinned;
817   SessionCommand* command =
818       new SessionCommand(kCommandSetPinnedState, sizeof(payload));
819   memcpy(command->contents(), &payload, sizeof(payload));
820   return command;
821 }
822 
CreateSessionStorageAssociatedCommand(const SessionID & tab_id,const std::string & session_storage_persistent_id)823 SessionCommand* SessionService::CreateSessionStorageAssociatedCommand(
824     const SessionID& tab_id,
825     const std::string& session_storage_persistent_id) {
826   Pickle pickle;
827   pickle.WriteInt(tab_id.id());
828   pickle.WriteString(session_storage_persistent_id);
829   return new SessionCommand(kCommandSessionStorageAssociated, pickle);
830 }
831 
CreateSetActiveWindowCommand(const SessionID & window_id)832 SessionCommand* SessionService::CreateSetActiveWindowCommand(
833     const SessionID& window_id) {
834   ActiveWindowPayload payload = 0;
835   payload = window_id.id();
836   SessionCommand* command =
837       new SessionCommand(kCommandSetActiveWindow, sizeof(payload));
838   memcpy(command->contents(), &payload, sizeof(payload));
839   return command;
840 }
841 
OnGotSessionCommands(const SessionCallback & callback,ScopedVector<SessionCommand> commands)842 void SessionService::OnGotSessionCommands(
843     const SessionCallback& callback,
844     ScopedVector<SessionCommand> commands) {
845   ScopedVector<SessionWindow> valid_windows;
846   SessionID::id_type active_window_id = 0;
847 
848   RestoreSessionFromCommands(
849       commands.get(), &valid_windows.get(), &active_window_id);
850   callback.Run(valid_windows.Pass(), active_window_id);
851 }
852 
RestoreSessionFromCommands(const std::vector<SessionCommand * > & commands,std::vector<SessionWindow * > * valid_windows,SessionID::id_type * active_window_id)853 void SessionService::RestoreSessionFromCommands(
854     const std::vector<SessionCommand*>& commands,
855     std::vector<SessionWindow*>* valid_windows,
856     SessionID::id_type* active_window_id) {
857   std::map<int, SessionTab*> tabs;
858   std::map<int, SessionWindow*> windows;
859 
860   VLOG(1) << "RestoreSessionFromCommands " << commands.size();
861   if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
862     AddTabsToWindows(&tabs, &windows);
863     SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows);
864     UpdateSelectedTabIndex(valid_windows);
865   }
866   STLDeleteValues(&tabs);
867   // Don't delete conents of windows, that is done by the caller as all
868   // valid windows are added to valid_windows.
869 }
870 
UpdateSelectedTabIndex(std::vector<SessionWindow * > * windows)871 void SessionService::UpdateSelectedTabIndex(
872     std::vector<SessionWindow*>* windows) {
873   for (std::vector<SessionWindow*>::const_iterator i = windows->begin();
874        i != windows->end(); ++i) {
875     // See note in SessionWindow as to why we do this.
876     int new_index = 0;
877     for (std::vector<SessionTab*>::const_iterator j = (*i)->tabs.begin();
878          j != (*i)->tabs.end(); ++j) {
879       if ((*j)->tab_visual_index == (*i)->selected_tab_index) {
880         new_index = static_cast<int>(j - (*i)->tabs.begin());
881         break;
882       }
883     }
884     (*i)->selected_tab_index = new_index;
885   }
886 }
887 
GetWindow(SessionID::id_type window_id,IdToSessionWindow * windows)888 SessionWindow* SessionService::GetWindow(
889     SessionID::id_type window_id,
890     IdToSessionWindow* windows) {
891   std::map<int, SessionWindow*>::iterator i = windows->find(window_id);
892   if (i == windows->end()) {
893     SessionWindow* window = new SessionWindow();
894     window->window_id.set_id(window_id);
895     (*windows)[window_id] = window;
896     return window;
897   }
898   return i->second;
899 }
900 
GetTab(SessionID::id_type tab_id,IdToSessionTab * tabs)901 SessionTab* SessionService::GetTab(
902     SessionID::id_type tab_id,
903     IdToSessionTab* tabs) {
904   DCHECK(tabs);
905   std::map<int, SessionTab*>::iterator i = tabs->find(tab_id);
906   if (i == tabs->end()) {
907     SessionTab* tab = new SessionTab();
908     tab->tab_id.set_id(tab_id);
909     (*tabs)[tab_id] = tab;
910     return tab;
911   }
912   return i->second;
913 }
914 
915 std::vector<SerializedNavigationEntry>::iterator
FindClosestNavigationWithIndex(std::vector<SerializedNavigationEntry> * navigations,int index)916   SessionService::FindClosestNavigationWithIndex(
917     std::vector<SerializedNavigationEntry>* navigations,
918     int index) {
919   DCHECK(navigations);
920   for (std::vector<SerializedNavigationEntry>::iterator
921            i = navigations->begin(); i != navigations->end(); ++i) {
922     if (i->index() >= index)
923       return i;
924   }
925   return navigations->end();
926 }
927 
928 // Function used in sorting windows. Sorting is done based on window id. As
929 // window ids increment for each new window, this effectively sorts by creation
930 // time.
WindowOrderSortFunction(const SessionWindow * w1,const SessionWindow * w2)931 static bool WindowOrderSortFunction(const SessionWindow* w1,
932                                     const SessionWindow* w2) {
933   return w1->window_id.id() < w2->window_id.id();
934 }
935 
936 // Compares the two tabs based on visual index.
TabVisualIndexSortFunction(const SessionTab * t1,const SessionTab * t2)937 static bool TabVisualIndexSortFunction(const SessionTab* t1,
938                                        const SessionTab* t2) {
939   const int delta = t1->tab_visual_index - t2->tab_visual_index;
940   return delta == 0 ? (t1->tab_id.id() < t2->tab_id.id()) : (delta < 0);
941 }
942 
SortTabsBasedOnVisualOrderAndPrune(std::map<int,SessionWindow * > * windows,std::vector<SessionWindow * > * valid_windows)943 void SessionService::SortTabsBasedOnVisualOrderAndPrune(
944     std::map<int, SessionWindow*>* windows,
945     std::vector<SessionWindow*>* valid_windows) {
946   std::map<int, SessionWindow*>::iterator i = windows->begin();
947   while (i != windows->end()) {
948     SessionWindow* window = i->second;
949     AppType app_type = window->app_name.empty() ? TYPE_NORMAL : TYPE_APP;
950     if (window->tabs.empty() || window->is_constrained ||
951         !should_track_changes_for_browser_type(
952             static_cast<Browser::Type>(window->type),
953             app_type)) {
954       delete window;
955       windows->erase(i++);
956     } else {
957       // Valid window; sort the tabs and add it to the list of valid windows.
958       std::sort(window->tabs.begin(), window->tabs.end(),
959                 &TabVisualIndexSortFunction);
960       // Otherwise, add the window such that older windows appear first.
961       if (valid_windows->empty()) {
962         valid_windows->push_back(window);
963       } else {
964         valid_windows->insert(
965             std::upper_bound(valid_windows->begin(), valid_windows->end(),
966                              window, &WindowOrderSortFunction),
967             window);
968       }
969       ++i;
970     }
971   }
972 }
973 
AddTabsToWindows(std::map<int,SessionTab * > * tabs,std::map<int,SessionWindow * > * windows)974 void SessionService::AddTabsToWindows(std::map<int, SessionTab*>* tabs,
975                                       std::map<int, SessionWindow*>* windows) {
976   VLOG(1) << "AddTabsToWindws";
977   VLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
978   std::map<int, SessionTab*>::iterator i = tabs->begin();
979   while (i != tabs->end()) {
980     SessionTab* tab = i->second;
981     if (tab->window_id.id() && !tab->navigations.empty()) {
982       SessionWindow* window = GetWindow(tab->window_id.id(), windows);
983       window->tabs.push_back(tab);
984       tabs->erase(i++);
985 
986       // See note in SessionTab as to why we do this.
987       std::vector<SerializedNavigationEntry>::iterator j =
988           FindClosestNavigationWithIndex(&(tab->navigations),
989                                          tab->current_navigation_index);
990       if (j == tab->navigations.end()) {
991         tab->current_navigation_index =
992             static_cast<int>(tab->navigations.size() - 1);
993       } else {
994         tab->current_navigation_index =
995             static_cast<int>(j - tab->navigations.begin());
996       }
997     } else {
998       // Never got a set tab index in window, or tabs are empty, nothing
999       // to do.
1000       ++i;
1001     }
1002   }
1003 }
1004 
CreateTabsAndWindows(const std::vector<SessionCommand * > & data,std::map<int,SessionTab * > * tabs,std::map<int,SessionWindow * > * windows,SessionID::id_type * active_window_id)1005 bool SessionService::CreateTabsAndWindows(
1006     const std::vector<SessionCommand*>& data,
1007     std::map<int, SessionTab*>* tabs,
1008     std::map<int, SessionWindow*>* windows,
1009     SessionID::id_type* active_window_id) {
1010   // If the file is corrupt (command with wrong size, or unknown command), we
1011   // still return true and attempt to restore what we we can.
1012   VLOG(1) << "CreateTabsAndWindows";
1013 
1014   startup_metric_utils::ScopedSlowStartupUMA
1015       scoped_timer("Startup.SlowStartupSessionServiceCreateTabsAndWindows");
1016 
1017   for (std::vector<SessionCommand*>::const_iterator i = data.begin();
1018        i != data.end(); ++i) {
1019     const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
1020     const SessionCommand* command = *i;
1021 
1022     VLOG(1) << "Read command " << (int) command->id();
1023     switch (command->id()) {
1024       case kCommandSetTabWindow: {
1025         SessionID::id_type payload[2];
1026         if (!command->GetPayload(payload, sizeof(payload))) {
1027           VLOG(1) << "Failed reading command " << command->id();
1028           return true;
1029         }
1030         GetTab(payload[1], tabs)->window_id.set_id(payload[0]);
1031         break;
1032       }
1033 
1034       // This is here for forward migration only.  New data is saved with
1035       // |kCommandSetWindowBounds3|.
1036       case kCommandSetWindowBounds2: {
1037         WindowBoundsPayload2 payload;
1038         if (!command->GetPayload(&payload, sizeof(payload))) {
1039           VLOG(1) << "Failed reading command " << command->id();
1040           return true;
1041         }
1042         GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
1043                                                               payload.y,
1044                                                               payload.w,
1045                                                               payload.h);
1046         GetWindow(payload.window_id, windows)->show_state =
1047             payload.is_maximized ?
1048                 ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL;
1049         break;
1050       }
1051 
1052       case kCommandSetWindowBounds3: {
1053         WindowBoundsPayload3 payload;
1054         if (!command->GetPayload(&payload, sizeof(payload))) {
1055           VLOG(1) << "Failed reading command " << command->id();
1056           return true;
1057         }
1058         GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
1059                                                               payload.y,
1060                                                               payload.w,
1061                                                               payload.h);
1062         // SHOW_STATE_INACTIVE is not persisted.
1063         ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
1064         if (payload.show_state > ui::SHOW_STATE_DEFAULT &&
1065             payload.show_state < ui::SHOW_STATE_END &&
1066             payload.show_state != ui::SHOW_STATE_INACTIVE) {
1067           show_state = static_cast<ui::WindowShowState>(payload.show_state);
1068         } else {
1069           NOTREACHED();
1070         }
1071         GetWindow(payload.window_id, windows)->show_state = show_state;
1072         break;
1073       }
1074 
1075       case kCommandSetTabIndexInWindow: {
1076         TabIndexInWindowPayload payload;
1077         if (!command->GetPayload(&payload, sizeof(payload))) {
1078           VLOG(1) << "Failed reading command " << command->id();
1079           return true;
1080         }
1081         GetTab(payload.id, tabs)->tab_visual_index = payload.index;
1082         break;
1083       }
1084 
1085       case kCommandTabClosedObsolete:
1086       case kCommandWindowClosedObsolete:
1087       case kCommandTabClosed:
1088       case kCommandWindowClosed: {
1089         ClosedPayload payload;
1090         if (!command->GetPayload(&payload, sizeof(payload)) &&
1091             !MigrateClosedPayload(*command, &payload)) {
1092           VLOG(1) << "Failed reading command " << command->id();
1093           return true;
1094         }
1095         if (command->id() == kCommandTabClosed ||
1096             command->id() == kCommandTabClosedObsolete) {
1097           delete GetTab(payload.id, tabs);
1098           tabs->erase(payload.id);
1099         } else {
1100           delete GetWindow(payload.id, windows);
1101           windows->erase(payload.id);
1102         }
1103         break;
1104       }
1105 
1106       case kCommandTabNavigationPathPrunedFromBack: {
1107         TabNavigationPathPrunedFromBackPayload payload;
1108         if (!command->GetPayload(&payload, sizeof(payload))) {
1109           VLOG(1) << "Failed reading command " << command->id();
1110           return true;
1111         }
1112         SessionTab* tab = GetTab(payload.id, tabs);
1113         tab->navigations.erase(
1114             FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
1115             tab->navigations.end());
1116         break;
1117       }
1118 
1119       case kCommandTabNavigationPathPrunedFromFront: {
1120         TabNavigationPathPrunedFromFrontPayload payload;
1121         if (!command->GetPayload(&payload, sizeof(payload)) ||
1122             payload.index <= 0) {
1123           VLOG(1) << "Failed reading command " << command->id();
1124           return true;
1125         }
1126         SessionTab* tab = GetTab(payload.id, tabs);
1127 
1128         // Update the selected navigation index.
1129         tab->current_navigation_index =
1130             std::max(-1, tab->current_navigation_index - payload.index);
1131 
1132         // And update the index of existing navigations.
1133         for (std::vector<SerializedNavigationEntry>::iterator
1134                  i = tab->navigations.begin();
1135              i != tab->navigations.end();) {
1136           i->set_index(i->index() - payload.index);
1137           if (i->index() < 0)
1138             i = tab->navigations.erase(i);
1139           else
1140             ++i;
1141         }
1142         break;
1143       }
1144 
1145       case kCommandUpdateTabNavigation: {
1146         SerializedNavigationEntry navigation;
1147         SessionID::id_type tab_id;
1148         if (!RestoreUpdateTabNavigationCommand(
1149                 *command, &navigation, &tab_id)) {
1150           VLOG(1) << "Failed reading command " << command->id();
1151           return true;
1152         }
1153         SessionTab* tab = GetTab(tab_id, tabs);
1154         std::vector<SerializedNavigationEntry>::iterator i =
1155             FindClosestNavigationWithIndex(&(tab->navigations),
1156                                            navigation.index());
1157         if (i != tab->navigations.end() && i->index() == navigation.index())
1158           *i = navigation;
1159         else
1160           tab->navigations.insert(i, navigation);
1161         break;
1162       }
1163 
1164       case kCommandSetSelectedNavigationIndex: {
1165         SelectedNavigationIndexPayload payload;
1166         if (!command->GetPayload(&payload, sizeof(payload))) {
1167           VLOG(1) << "Failed reading command " << command->id();
1168           return true;
1169         }
1170         GetTab(payload.id, tabs)->current_navigation_index = payload.index;
1171         break;
1172       }
1173 
1174       case kCommandSetSelectedTabInIndex: {
1175         SelectedTabInIndexPayload payload;
1176         if (!command->GetPayload(&payload, sizeof(payload))) {
1177           VLOG(1) << "Failed reading command " << command->id();
1178           return true;
1179         }
1180         GetWindow(payload.id, windows)->selected_tab_index = payload.index;
1181         break;
1182       }
1183 
1184       case kCommandSetWindowType: {
1185         WindowTypePayload payload;
1186         if (!command->GetPayload(&payload, sizeof(payload))) {
1187           VLOG(1) << "Failed reading command " << command->id();
1188           return true;
1189         }
1190         GetWindow(payload.id, windows)->is_constrained = false;
1191         GetWindow(payload.id, windows)->type =
1192             BrowserTypeForWindowType(
1193                 static_cast<WindowType>(payload.index));
1194         break;
1195       }
1196 
1197       case kCommandSetPinnedState: {
1198         PinnedStatePayload payload;
1199         if (!command->GetPayload(&payload, sizeof(payload))) {
1200           VLOG(1) << "Failed reading command " << command->id();
1201           return true;
1202         }
1203         GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state;
1204         break;
1205       }
1206 
1207       case kCommandSetWindowAppName: {
1208         SessionID::id_type window_id;
1209         std::string app_name;
1210         if (!RestoreSetWindowAppNameCommand(*command, &window_id, &app_name))
1211           return true;
1212 
1213         GetWindow(window_id, windows)->app_name.swap(app_name);
1214         break;
1215       }
1216 
1217       case kCommandSetExtensionAppID: {
1218         SessionID::id_type tab_id;
1219         std::string extension_app_id;
1220         if (!RestoreSetTabExtensionAppIDCommand(
1221                 *command, &tab_id, &extension_app_id)) {
1222           VLOG(1) << "Failed reading command " << command->id();
1223           return true;
1224         }
1225 
1226         GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
1227         break;
1228       }
1229 
1230       case kCommandSetTabUserAgentOverride: {
1231         SessionID::id_type tab_id;
1232         std::string user_agent_override;
1233         if (!RestoreSetTabUserAgentOverrideCommand(
1234                 *command, &tab_id, &user_agent_override)) {
1235           return true;
1236         }
1237 
1238         GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override);
1239         break;
1240       }
1241 
1242       case kCommandSessionStorageAssociated: {
1243         scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
1244         SessionID::id_type command_tab_id;
1245         std::string session_storage_persistent_id;
1246         PickleIterator iter(*command_pickle.get());
1247         if (!command_pickle->ReadInt(&iter, &command_tab_id) ||
1248             !command_pickle->ReadString(&iter, &session_storage_persistent_id))
1249           return true;
1250         // Associate the session storage back.
1251         GetTab(command_tab_id, tabs)->session_storage_persistent_id =
1252             session_storage_persistent_id;
1253         break;
1254       }
1255 
1256       case kCommandSetActiveWindow: {
1257         ActiveWindowPayload payload;
1258         if (!command->GetPayload(&payload, sizeof(payload))) {
1259           VLOG(1) << "Failed reading command " << command->id();
1260           return true;
1261         }
1262         *active_window_id = payload;
1263         break;
1264       }
1265 
1266       default:
1267         VLOG(1) << "Failed reading an unknown command " << command->id();
1268         return true;
1269     }
1270   }
1271   return true;
1272 }
1273 
BuildCommandsForTab(const SessionID & window_id,WebContents * tab,int index_in_window,bool is_pinned,std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range)1274 void SessionService::BuildCommandsForTab(const SessionID& window_id,
1275                                          WebContents* tab,
1276                                          int index_in_window,
1277                                          bool is_pinned,
1278                                          std::vector<SessionCommand*>* commands,
1279                                          IdToRange* tab_to_available_range) {
1280   DCHECK(tab && commands && window_id.id());
1281   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(tab);
1282   const SessionID& session_id(session_tab_helper->session_id());
1283   commands->push_back(CreateSetTabWindowCommand(window_id, session_id));
1284 
1285   const int current_index = tab->GetController().GetCurrentEntryIndex();
1286   const int min_index = std::max(0,
1287                                  current_index - max_persist_navigation_count);
1288   const int max_index =
1289       std::min(current_index + max_persist_navigation_count,
1290                tab->GetController().GetEntryCount());
1291   const int pending_index = tab->GetController().GetPendingEntryIndex();
1292   if (tab_to_available_range) {
1293     (*tab_to_available_range)[session_id.id()] =
1294         std::pair<int, int>(min_index, max_index);
1295   }
1296 
1297   if (is_pinned) {
1298     commands->push_back(CreatePinnedStateCommand(session_id, true));
1299   }
1300 
1301   extensions::TabHelper* extensions_tab_helper =
1302       extensions::TabHelper::FromWebContents(tab);
1303   if (extensions_tab_helper->extension_app()) {
1304     commands->push_back(
1305         CreateSetTabExtensionAppIDCommand(
1306             kCommandSetExtensionAppID, session_id.id(),
1307             extensions_tab_helper->extension_app()->id()));
1308   }
1309 
1310   const std::string& ua_override = tab->GetUserAgentOverride();
1311   if (!ua_override.empty()) {
1312     commands->push_back(
1313         CreateSetTabUserAgentOverrideCommand(
1314             kCommandSetTabUserAgentOverride, session_id.id(), ua_override));
1315   }
1316 
1317   for (int i = min_index; i < max_index; ++i) {
1318     const NavigationEntry* entry = (i == pending_index) ?
1319         tab->GetController().GetPendingEntry() :
1320         tab->GetController().GetEntryAtIndex(i);
1321     DCHECK(entry);
1322     if (ShouldTrackEntry(entry->GetVirtualURL())) {
1323       const SerializedNavigationEntry navigation =
1324           SerializedNavigationEntry::FromNavigationEntry(i, *entry);
1325       commands->push_back(
1326           CreateUpdateTabNavigationCommand(
1327               kCommandUpdateTabNavigation, session_id.id(), navigation));
1328     }
1329   }
1330   commands->push_back(
1331       CreateSetSelectedNavigationIndexCommand(session_id, current_index));
1332 
1333   if (index_in_window != -1) {
1334     commands->push_back(
1335         CreateSetTabIndexInWindowCommand(session_id, index_in_window));
1336   }
1337 
1338   // Record the association between the sessionStorage namespace and the tab.
1339   content::SessionStorageNamespace* session_storage_namespace =
1340       tab->GetController().GetDefaultSessionStorageNamespace();
1341   ScheduleCommand(CreateSessionStorageAssociatedCommand(
1342       session_tab_helper->session_id(),
1343       session_storage_namespace->persistent_id()));
1344 }
1345 
BuildCommandsForBrowser(Browser * browser,std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range,std::set<SessionID::id_type> * windows_to_track)1346 void SessionService::BuildCommandsForBrowser(
1347     Browser* browser,
1348     std::vector<SessionCommand*>* commands,
1349     IdToRange* tab_to_available_range,
1350     std::set<SessionID::id_type>* windows_to_track) {
1351   DCHECK(browser && commands);
1352   DCHECK(browser->session_id().id());
1353 
1354   commands->push_back(
1355       CreateSetWindowBoundsCommand(browser->session_id(),
1356                                    browser->window()->GetRestoredBounds(),
1357                                    browser->window()->GetRestoredState()));
1358 
1359   commands->push_back(CreateSetWindowTypeCommand(
1360       browser->session_id(), WindowTypeForBrowserType(browser->type())));
1361 
1362   if (!browser->app_name().empty()) {
1363     commands->push_back(CreateSetWindowAppNameCommand(
1364         kCommandSetWindowAppName,
1365         browser->session_id().id(),
1366         browser->app_name()));
1367   }
1368 
1369   windows_to_track->insert(browser->session_id().id());
1370   TabStripModel* tab_strip = browser->tab_strip_model();
1371   for (int i = 0; i < tab_strip->count(); ++i) {
1372     WebContents* tab = tab_strip->GetWebContentsAt(i);
1373     DCHECK(tab);
1374     BuildCommandsForTab(browser->session_id(), tab, i,
1375                         tab_strip->IsTabPinned(i),
1376                         commands, tab_to_available_range);
1377   }
1378 
1379   commands->push_back(
1380       CreateSetSelectedTabInWindow(browser->session_id(),
1381                                    browser->tab_strip_model()->active_index()));
1382 }
1383 
BuildCommandsFromBrowsers(std::vector<SessionCommand * > * commands,IdToRange * tab_to_available_range,std::set<SessionID::id_type> * windows_to_track)1384 void SessionService::BuildCommandsFromBrowsers(
1385     std::vector<SessionCommand*>* commands,
1386     IdToRange* tab_to_available_range,
1387     std::set<SessionID::id_type>* windows_to_track) {
1388   DCHECK(commands);
1389   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1390     Browser* browser = *it;
1391     // Make sure the browser has tabs and a window. Browser's destructor
1392     // removes itself from the BrowserList. When a browser is closed the
1393     // destructor is not necessarily run immediately. This means it's possible
1394     // for us to get a handle to a browser that is about to be removed. If
1395     // the tab count is 0 or the window is NULL, the browser is about to be
1396     // deleted, so we ignore it.
1397     if (ShouldTrackBrowser(browser) && browser->tab_strip_model()->count() &&
1398         browser->window()) {
1399       BuildCommandsForBrowser(browser, commands, tab_to_available_range,
1400                               windows_to_track);
1401     }
1402   }
1403 }
1404 
ScheduleReset()1405 void SessionService::ScheduleReset() {
1406   set_pending_reset(true);
1407   STLDeleteElements(&pending_commands());
1408   tab_to_available_range_.clear();
1409   windows_tracking_.clear();
1410   BuildCommandsFromBrowsers(&pending_commands(), &tab_to_available_range_,
1411                             &windows_tracking_);
1412   if (!windows_tracking_.empty()) {
1413     // We're lazily created on startup and won't get an initial batch of
1414     // SetWindowType messages. Set these here to make sure our state is correct.
1415     has_open_trackable_browsers_ = true;
1416     move_on_new_browser_ = true;
1417   }
1418   StartSaveTimer();
1419 }
1420 
ReplacePendingCommand(SessionCommand * command)1421 bool SessionService::ReplacePendingCommand(SessionCommand* command) {
1422   // We optimize page navigations, which can happen quite frequently and
1423   // are expensive. And activation is like Highlander, there can only be one!
1424   if (command->id() != kCommandUpdateTabNavigation &&
1425       command->id() != kCommandSetActiveWindow) {
1426     return false;
1427   }
1428   for (std::vector<SessionCommand*>::reverse_iterator i =
1429        pending_commands().rbegin(); i != pending_commands().rend(); ++i) {
1430     SessionCommand* existing_command = *i;
1431     if (command->id() == kCommandUpdateTabNavigation &&
1432         existing_command->id() == kCommandUpdateTabNavigation) {
1433       scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
1434       PickleIterator iterator(*command_pickle);
1435       SessionID::id_type command_tab_id;
1436       int command_nav_index;
1437       if (!command_pickle->ReadInt(&iterator, &command_tab_id) ||
1438           !command_pickle->ReadInt(&iterator, &command_nav_index)) {
1439         return false;
1440       }
1441       SessionID::id_type existing_tab_id;
1442       int existing_nav_index;
1443       {
1444         // Creating a pickle like this means the Pickle references the data from
1445         // the command. Make sure we delete the pickle before the command, else
1446         // the pickle references deleted memory.
1447         scoped_ptr<Pickle> existing_pickle(existing_command->PayloadAsPickle());
1448         iterator = PickleIterator(*existing_pickle);
1449         if (!existing_pickle->ReadInt(&iterator, &existing_tab_id) ||
1450             !existing_pickle->ReadInt(&iterator, &existing_nav_index)) {
1451           return false;
1452         }
1453       }
1454       if (existing_tab_id == command_tab_id &&
1455           existing_nav_index == command_nav_index) {
1456         // existing_command is an update for the same tab/index pair. Replace
1457         // it with the new one. We need to add to the end of the list just in
1458         // case there is a prune command after the update command.
1459         delete existing_command;
1460         pending_commands().erase(i.base() - 1);
1461         pending_commands().push_back(command);
1462         return true;
1463       }
1464       return false;
1465     }
1466     if (command->id() == kCommandSetActiveWindow &&
1467         existing_command->id() == kCommandSetActiveWindow) {
1468       *i = command;
1469       delete existing_command;
1470       return true;
1471     }
1472   }
1473   return false;
1474 }
1475 
ScheduleCommand(SessionCommand * command)1476 void SessionService::ScheduleCommand(SessionCommand* command) {
1477   DCHECK(command);
1478   if (ReplacePendingCommand(command))
1479     return;
1480   BaseSessionService::ScheduleCommand(command);
1481   // Don't schedule a reset on tab closed/window closed. Otherwise we may
1482   // lose tabs/windows we want to restore from if we exit right after this.
1483   if (!pending_reset() && pending_window_close_ids_.empty() &&
1484       commands_since_reset() >= kWritesPerReset &&
1485       (command->id() != kCommandTabClosed &&
1486        command->id() != kCommandWindowClosed)) {
1487     ScheduleReset();
1488   }
1489 }
1490 
CommitPendingCloses()1491 void SessionService::CommitPendingCloses() {
1492   for (PendingTabCloseIDs::iterator i = pending_tab_close_ids_.begin();
1493        i != pending_tab_close_ids_.end(); ++i) {
1494     ScheduleCommand(CreateTabClosedCommand(*i));
1495   }
1496   pending_tab_close_ids_.clear();
1497 
1498   for (PendingWindowCloseIDs::iterator i = pending_window_close_ids_.begin();
1499        i != pending_window_close_ids_.end(); ++i) {
1500     ScheduleCommand(CreateWindowClosedCommand(*i));
1501   }
1502   pending_window_close_ids_.clear();
1503 }
1504 
IsOnlyOneTabLeft() const1505 bool SessionService::IsOnlyOneTabLeft() const {
1506   if (!profile()) {
1507     // We're testing, always return false.
1508     return false;
1509   }
1510 
1511   int window_count = 0;
1512   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1513     Browser* browser = *it;
1514     const SessionID::id_type window_id = browser->session_id().id();
1515     if (ShouldTrackBrowser(browser) &&
1516         window_closing_ids_.find(window_id) == window_closing_ids_.end()) {
1517       if (++window_count > 1)
1518         return false;
1519       // By the time this is invoked the tab has been removed. As such, we use
1520       // > 0 here rather than > 1.
1521       if (browser->tab_strip_model()->count() > 0)
1522         return false;
1523     }
1524   }
1525   return true;
1526 }
1527 
HasOpenTrackableBrowsers(const SessionID & window_id) const1528 bool SessionService::HasOpenTrackableBrowsers(
1529     const SessionID& window_id) const {
1530   if (!profile()) {
1531     // We're testing, always return false.
1532     return true;
1533   }
1534 
1535   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1536     Browser* browser = *it;
1537     const SessionID::id_type browser_id = browser->session_id().id();
1538     if (browser_id != window_id.id() &&
1539         window_closing_ids_.find(browser_id) == window_closing_ids_.end() &&
1540         ShouldTrackBrowser(browser)) {
1541       return true;
1542     }
1543   }
1544   return false;
1545 }
1546 
ShouldTrackChangesToWindow(const SessionID & window_id) const1547 bool SessionService::ShouldTrackChangesToWindow(
1548     const SessionID& window_id) const {
1549   return windows_tracking_.find(window_id.id()) != windows_tracking_.end();
1550 }
1551 
ShouldTrackBrowser(Browser * browser) const1552 bool SessionService::ShouldTrackBrowser(Browser* browser) const {
1553   AppType app_type = browser->is_app() ? TYPE_APP : TYPE_NORMAL;
1554   return browser->profile() == profile() &&
1555          should_track_changes_for_browser_type(browser->type(), app_type);
1556 }
1557 
should_track_changes_for_browser_type(Browser::Type type,AppType app_type)1558 bool SessionService::should_track_changes_for_browser_type(Browser::Type type,
1559                                                            AppType app_type) {
1560 #if defined(OS_CHROMEOS)
1561   // Restore app popups for chromeos alone.
1562   if (type == Browser::TYPE_POPUP && app_type == TYPE_APP)
1563     return true;
1564 #endif
1565 
1566   return type == Browser::TYPE_TABBED ||
1567         (type == Browser::TYPE_POPUP && browser_defaults::kRestorePopups);
1568 }
1569 
WindowTypeForBrowserType(Browser::Type type)1570 SessionService::WindowType SessionService::WindowTypeForBrowserType(
1571     Browser::Type type) {
1572   switch (type) {
1573     case Browser::TYPE_POPUP:
1574       return TYPE_POPUP;
1575     case Browser::TYPE_TABBED:
1576       return TYPE_TABBED;
1577     default:
1578       DCHECK(false);
1579       return TYPE_TABBED;
1580   }
1581 }
1582 
BrowserTypeForWindowType(WindowType type)1583 Browser::Type SessionService::BrowserTypeForWindowType(WindowType type) {
1584   switch (type) {
1585     case TYPE_POPUP:
1586       return Browser::TYPE_POPUP;
1587     case TYPE_TABBED:
1588     default:
1589       return Browser::TYPE_TABBED;
1590   }
1591 }
1592 
RecordSessionUpdateHistogramData(int type,base::TimeTicks * last_updated_time)1593 void SessionService::RecordSessionUpdateHistogramData(int type,
1594     base::TimeTicks* last_updated_time) {
1595   if (!last_updated_time->is_null()) {
1596     base::TimeDelta delta = base::TimeTicks::Now() - *last_updated_time;
1597     // We're interested in frequent updates periods longer than
1598     // 10 minutes.
1599     bool use_long_period = false;
1600     if (delta >= save_delay_in_mins_) {
1601       use_long_period = true;
1602     }
1603     switch (type) {
1604       case chrome::NOTIFICATION_SESSION_SERVICE_SAVED :
1605         RecordUpdatedSaveTime(delta, use_long_period);
1606         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
1607         break;
1608       case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
1609         RecordUpdatedTabClosed(delta, use_long_period);
1610         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
1611         break;
1612       case content::NOTIFICATION_NAV_LIST_PRUNED:
1613         RecordUpdatedNavListPruned(delta, use_long_period);
1614         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
1615         break;
1616       case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
1617         RecordUpdatedNavEntryCommit(delta, use_long_period);
1618         RecordUpdatedSessionNavigationOrTab(delta, use_long_period);
1619         break;
1620       default:
1621         NOTREACHED() << "Bad type sent to RecordSessionUpdateHistogramData";
1622         break;
1623     }
1624   }
1625   (*last_updated_time) = base::TimeTicks::Now();
1626 }
1627 
RecordUpdatedTabClosed(base::TimeDelta delta,bool use_long_period)1628 void SessionService::RecordUpdatedTabClosed(base::TimeDelta delta,
1629                                             bool use_long_period) {
1630   std::string name("SessionRestore.TabClosedPeriod");
1631   UMA_HISTOGRAM_CUSTOM_TIMES(name,
1632       delta,
1633       // 2500ms is the default save delay.
1634       save_delay_in_millis_,
1635       save_delay_in_mins_,
1636       50);
1637   if (use_long_period) {
1638     std::string long_name_("SessionRestore.TabClosedLongPeriod");
1639     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
1640         delta,
1641         save_delay_in_mins_,
1642         save_delay_in_hrs_,
1643         50);
1644   }
1645 }
1646 
RecordUpdatedNavListPruned(base::TimeDelta delta,bool use_long_period)1647 void SessionService::RecordUpdatedNavListPruned(base::TimeDelta delta,
1648                                                 bool use_long_period) {
1649   std::string name("SessionRestore.NavigationListPrunedPeriod");
1650   UMA_HISTOGRAM_CUSTOM_TIMES(name,
1651       delta,
1652       // 2500ms is the default save delay.
1653       save_delay_in_millis_,
1654       save_delay_in_mins_,
1655       50);
1656   if (use_long_period) {
1657     std::string long_name_("SessionRestore.NavigationListPrunedLongPeriod");
1658     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
1659         delta,
1660         save_delay_in_mins_,
1661         save_delay_in_hrs_,
1662         50);
1663   }
1664 }
1665 
RecordUpdatedNavEntryCommit(base::TimeDelta delta,bool use_long_period)1666 void SessionService::RecordUpdatedNavEntryCommit(base::TimeDelta delta,
1667                                                  bool use_long_period) {
1668   std::string name("SessionRestore.NavEntryCommittedPeriod");
1669   UMA_HISTOGRAM_CUSTOM_TIMES(name,
1670       delta,
1671       // 2500ms is the default save delay.
1672       save_delay_in_millis_,
1673       save_delay_in_mins_,
1674       50);
1675   if (use_long_period) {
1676     std::string long_name_("SessionRestore.NavEntryCommittedLongPeriod");
1677     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
1678         delta,
1679         save_delay_in_mins_,
1680         save_delay_in_hrs_,
1681         50);
1682   }
1683 }
1684 
RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,bool use_long_period)1685 void SessionService::RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta,
1686                                                          bool use_long_period) {
1687   std::string name("SessionRestore.NavOrTabUpdatePeriod");
1688   UMA_HISTOGRAM_CUSTOM_TIMES(name,
1689       delta,
1690       // 2500ms is the default save delay.
1691       save_delay_in_millis_,
1692       save_delay_in_mins_,
1693       50);
1694   if (use_long_period) {
1695     std::string long_name_("SessionRestore.NavOrTabUpdateLongPeriod");
1696     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
1697         delta,
1698         save_delay_in_mins_,
1699         save_delay_in_hrs_,
1700         50);
1701   }
1702 }
1703 
RecordUpdatedSaveTime(base::TimeDelta delta,bool use_long_period)1704 void SessionService::RecordUpdatedSaveTime(base::TimeDelta delta,
1705                                            bool use_long_period) {
1706   std::string name("SessionRestore.SavePeriod");
1707   UMA_HISTOGRAM_CUSTOM_TIMES(name,
1708       delta,
1709       // 2500ms is the default save delay.
1710       save_delay_in_millis_,
1711       save_delay_in_mins_,
1712       50);
1713   if (use_long_period) {
1714     std::string long_name_("SessionRestore.SaveLongPeriod");
1715     UMA_HISTOGRAM_CUSTOM_TIMES(long_name_,
1716         delta,
1717         save_delay_in_mins_,
1718         save_delay_in_hrs_,
1719         50);
1720   }
1721 }
1722 
TabInserted(WebContents * contents)1723 void SessionService::TabInserted(WebContents* contents) {
1724   SessionTabHelper* session_tab_helper =
1725       SessionTabHelper::FromWebContents(contents);
1726   if (!ShouldTrackChangesToWindow(session_tab_helper->window_id()))
1727     return;
1728   SetTabWindow(session_tab_helper->window_id(),
1729                session_tab_helper->session_id());
1730   extensions::TabHelper* extensions_tab_helper =
1731       extensions::TabHelper::FromWebContents(contents);
1732   if (extensions_tab_helper &&
1733       extensions_tab_helper->extension_app()) {
1734     SetTabExtensionAppID(
1735         session_tab_helper->window_id(),
1736         session_tab_helper->session_id(),
1737         extensions_tab_helper->extension_app()->id());
1738   }
1739 
1740   // Record the association between the SessionStorageNamespace and the
1741   // tab.
1742   //
1743   // TODO(ajwong): This should be processing the whole map rather than
1744   // just the default. This in particular will not work for tabs with only
1745   // isolated apps which won't have a default partition.
1746   content::SessionStorageNamespace* session_storage_namespace =
1747       contents->GetController().GetDefaultSessionStorageNamespace();
1748   ScheduleCommand(CreateSessionStorageAssociatedCommand(
1749       session_tab_helper->session_id(),
1750       session_storage_namespace->persistent_id()));
1751   session_storage_namespace->SetShouldPersist(true);
1752 }
1753 
TabClosing(WebContents * contents)1754 void SessionService::TabClosing(WebContents* contents) {
1755   // Allow the associated sessionStorage to get deleted; it won't be needed
1756   // in the session restore.
1757   content::SessionStorageNamespace* session_storage_namespace =
1758       contents->GetController().GetDefaultSessionStorageNamespace();
1759   session_storage_namespace->SetShouldPersist(false);
1760   SessionTabHelper* session_tab_helper =
1761       SessionTabHelper::FromWebContents(contents);
1762   TabClosed(session_tab_helper->window_id(),
1763             session_tab_helper->session_id(),
1764             contents->GetClosedByUserGesture());
1765   RecordSessionUpdateHistogramData(content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
1766                                    &last_updated_tab_closed_time_);
1767 }
1768 
MaybeDeleteSessionOnlyData()1769 void SessionService::MaybeDeleteSessionOnlyData() {
1770   // Clear session data if the last window for a profile has been closed and
1771   // closing the last window would normally close Chrome, unless background mode
1772   // is active.
1773   if (has_open_trackable_browsers_ ||
1774       browser_defaults::kBrowserAliveWithNoWindows ||
1775       g_browser_process->background_mode_manager()->IsBackgroundModeActive()) {
1776     return;
1777   }
1778 
1779   // Check for any open windows for the current profile that we aren't tracking.
1780   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1781     if ((*it)->profile() == profile())
1782       return;
1783   }
1784   DeleteSessionOnlyData(profile());
1785 }
1786