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.
5 #include "chrome/browser/sessions/session_service.h"
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"
48 #if defined(OS_MACOSX)
49 #include "chrome/browser/app_controller_mac.h"
53 using content::NavigationEntry
;
54 using content::WebContents
;
55 using sessions::SerializedNavigationEntry
;
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;
86 // Every kWritesPerReset commands triggers recreating the file.
87 static const int kWritesPerReset
= 250;
91 // Various payload structures.
92 struct ClosedPayload
{
93 SessionID::id_type id
;
97 struct WindowBoundsPayload2
{
98 SessionID::id_type window_id
;
106 struct WindowBoundsPayload3
{
107 SessionID::id_type window_id
;
115 typedef SessionID::id_type ActiveWindowPayload
;
117 struct IDAndIndexPayload
{
118 SessionID::id_type id
;
122 typedef IDAndIndexPayload TabIndexInWindowPayload
;
124 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload
;
126 typedef IDAndIndexPayload SelectedNavigationIndexPayload
;
128 typedef IDAndIndexPayload SelectedTabInIndexPayload
;
130 typedef IDAndIndexPayload WindowTypePayload
;
132 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload
;
134 struct PinnedStatePayload
{
135 SessionID::id_type tab_id
;
139 // Returns the show state to store to disk based |state|.
140 ui::WindowShowState
AdjustShowState(ui::WindowShowState 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
:
149 case ui::SHOW_STATE_DEFAULT
:
150 case ui::SHOW_STATE_INACTIVE
:
151 case ui::SHOW_STATE_END
:
152 return ui::SHOW_STATE_NORMAL
;
154 return ui::SHOW_STATE_NORMAL
;
157 // Migrates a |ClosedPayload|, returning true on success (migration was
158 // necessary and happened), or false (migration was not necessary or was not
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);
184 // SessionService -------------------------------------------------------------
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) {
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) {
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);
215 bool SessionService::RestoreIfNecessary(const std::vector
<GURL
>& urls_to_open
) {
216 return RestoreIfNecessary(urls_to_open
, NULL
);
219 void SessionService::ResetFromCurrentBrowsers() {
223 void SessionService::MoveCurrentSessionToLastSession() {
224 pending_tab_close_ids_
.clear();
225 window_closing_ids_
.clear();
226 pending_window_close_ids_
.clear();
230 RunTaskOnBackendThread(
231 FROM_HERE
, base::Bind(&SessionBackend::MoveCurrentSessionToLastSession
,
235 void SessionService::SetTabWindow(const SessionID
& window_id
,
236 const SessionID
& tab_id
) {
237 if (!ShouldTrackChangesToWindow(window_id
))
240 ScheduleCommand(CreateSetTabWindowCommand(window_id
, tab_id
));
243 void SessionService::SetWindowBounds(const SessionID
& window_id
,
244 const gfx::Rect
& bounds
,
245 ui::WindowShowState show_state
) {
246 if (!ShouldTrackChangesToWindow(window_id
))
249 ScheduleCommand(CreateSetWindowBoundsCommand(window_id
, bounds
, show_state
));
252 void SessionService::SetTabIndexInWindow(const SessionID
& window_id
,
253 const SessionID
& tab_id
,
255 if (!ShouldTrackChangesToWindow(window_id
))
258 ScheduleCommand(CreateSetTabIndexInWindowCommand(tab_id
, new_index
));
261 void SessionService::SetPinnedState(const SessionID
& window_id
,
262 const SessionID
& tab_id
,
264 if (!ShouldTrackChangesToWindow(window_id
))
267 ScheduleCommand(CreatePinnedStateCommand(tab_id
, is_pinned
));
270 void SessionService::TabClosed(const SessionID
& window_id
,
271 const SessionID
& tab_id
,
272 bool closed_by_user_gesture
) {
274 return; // Hapens when the tab is replaced.
276 if (!ShouldTrackChangesToWindow(window_id
))
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
);
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
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()));
300 // User closed the last tab in the last tabbed browser. Don't mark the
302 pending_tab_close_ids_
.insert(tab_id
.id());
303 has_open_trackable_browsers_
= false;
307 void SessionService::WindowOpened(Browser
* browser
) {
308 if (!ShouldTrackBrowser(browser
))
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());
317 void SessionService::WindowClosing(const SessionID
& window_id
) {
318 if (!ShouldTrackChangesToWindow(window_id
))
321 // The window is about to close. If there are other tabbed browsers with the
322 // same original profile commit the close immediately.
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
);
331 if (should_record_close_as_pending())
332 pending_window_close_ids_
.insert(window_id
.id());
334 window_closing_ids_
.insert(window_id
.id());
337 void SessionService::WindowClosed(const SessionID
& window_id
) {
338 if (!ShouldTrackChangesToWindow(window_id
))
341 windows_tracking_
.erase(window_id
.id());
343 if (window_closing_ids_
.find(window_id
.id()) != window_closing_ids_
.end()) {
344 window_closing_ids_
.erase(window_id
.id());
345 ScheduleCommand(CreateWindowClosedCommand(window_id
.id()));
346 } else if (pending_window_close_ids_
.find(window_id
.id()) ==
347 pending_window_close_ids_
.end()) {
348 // We'll hit this if user closed the last tab in a window.
349 has_open_trackable_browsers_
= HasOpenTrackableBrowsers(window_id
);
350 if (should_record_close_as_pending())
351 pending_window_close_ids_
.insert(window_id
.id());
353 ScheduleCommand(CreateWindowClosedCommand(window_id
.id()));
355 // Clear session data if the last window for a profile has been closed and
356 // closing the last window would normally close Chrome, unless background mode
358 if (!has_open_trackable_browsers_
&&
359 !browser_defaults::kBrowserAliveWithNoWindows
&&
360 !g_browser_process
->background_mode_manager()->IsBackgroundModeActive()) {
361 DeleteSessionOnlyData(profile());
365 void SessionService::SetWindowType(const SessionID
& window_id
,
368 if (!should_track_changes_for_browser_type(type
, app_type
))
371 windows_tracking_
.insert(window_id
.id());
373 // The user created a new tabbed browser with our profile. Commit any
375 CommitPendingCloses();
377 has_open_trackable_browsers_
= true;
378 move_on_new_browser_
= true;
381 CreateSetWindowTypeCommand(window_id
, WindowTypeForBrowserType(type
)));
384 void SessionService::SetWindowAppName(
385 const SessionID
& window_id
,
386 const std::string
& app_name
) {
387 if (!ShouldTrackChangesToWindow(window_id
))
390 ScheduleCommand(CreateSetTabExtensionAppIDCommand(
391 kCommandSetWindowAppName
,
396 void SessionService::TabNavigationPathPrunedFromBack(const SessionID
& window_id
,
397 const SessionID
& tab_id
,
399 if (!ShouldTrackChangesToWindow(window_id
))
402 TabNavigationPathPrunedFromBackPayload payload
= { 0 };
403 payload
.id
= tab_id
.id();
404 payload
.index
= count
;
405 SessionCommand
* command
=
406 new SessionCommand(kCommandTabNavigationPathPrunedFromBack
,
408 memcpy(command
->contents(), &payload
, sizeof(payload
));
409 ScheduleCommand(command
);
412 void SessionService::TabNavigationPathPrunedFromFront(
413 const SessionID
& window_id
,
414 const SessionID
& tab_id
,
416 if (!ShouldTrackChangesToWindow(window_id
))
419 // Update the range of indices.
420 if (tab_to_available_range_
.find(tab_id
.id()) !=
421 tab_to_available_range_
.end()) {
422 std::pair
<int, int>& range
= tab_to_available_range_
[tab_id
.id()];
423 range
.first
= std::max(0, range
.first
- count
);
424 range
.second
= std::max(0, range
.second
- count
);
427 TabNavigationPathPrunedFromFrontPayload payload
= { 0 };
428 payload
.id
= tab_id
.id();
429 payload
.index
= count
;
430 SessionCommand
* command
=
431 new SessionCommand(kCommandTabNavigationPathPrunedFromFront
,
433 memcpy(command
->contents(), &payload
, sizeof(payload
));
434 ScheduleCommand(command
);
437 void SessionService::UpdateTabNavigation(
438 const SessionID
& window_id
,
439 const SessionID
& tab_id
,
440 const SerializedNavigationEntry
& navigation
) {
441 if (!ShouldTrackEntry(navigation
.virtual_url()) ||
442 !ShouldTrackChangesToWindow(window_id
)) {
446 if (tab_to_available_range_
.find(tab_id
.id()) !=
447 tab_to_available_range_
.end()) {
448 std::pair
<int, int>& range
= tab_to_available_range_
[tab_id
.id()];
449 range
.first
= std::min(navigation
.index(), range
.first
);
450 range
.second
= std::max(navigation
.index(), range
.second
);
452 ScheduleCommand(CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation
,
453 tab_id
.id(), navigation
));
456 void SessionService::TabRestored(WebContents
* tab
, bool pinned
) {
457 SessionTabHelper
* session_tab_helper
= SessionTabHelper::FromWebContents(tab
);
458 if (!ShouldTrackChangesToWindow(session_tab_helper
->window_id()))
461 BuildCommandsForTab(session_tab_helper
->window_id(), tab
, -1,
462 pinned
, &pending_commands(), NULL
);
466 void SessionService::SetSelectedNavigationIndex(const SessionID
& window_id
,
467 const SessionID
& tab_id
,
469 if (!ShouldTrackChangesToWindow(window_id
))
472 if (tab_to_available_range_
.find(tab_id
.id()) !=
473 tab_to_available_range_
.end()) {
474 if (index
< tab_to_available_range_
[tab_id
.id()].first
||
475 index
> tab_to_available_range_
[tab_id
.id()].second
) {
476 // The new index is outside the range of what we've archived, schedule
478 ResetFromCurrentBrowsers();
482 ScheduleCommand(CreateSetSelectedNavigationIndexCommand(tab_id
, index
));
485 void SessionService::SetSelectedTabInWindow(const SessionID
& window_id
,
487 if (!ShouldTrackChangesToWindow(window_id
))
490 ScheduleCommand(CreateSetSelectedTabInWindow(window_id
, index
));
493 void SessionService::SetTabUserAgentOverride(
494 const SessionID
& window_id
,
495 const SessionID
& tab_id
,
496 const std::string
& user_agent_override
) {
497 if (!ShouldTrackChangesToWindow(window_id
))
500 ScheduleCommand(CreateSetTabUserAgentOverrideCommand(
501 kCommandSetTabUserAgentOverride
, tab_id
.id(), user_agent_override
));
504 CancelableTaskTracker::TaskId
SessionService::GetLastSession(
505 const SessionCallback
& callback
,
506 CancelableTaskTracker
* tracker
) {
507 // OnGotSessionCommands maps the SessionCommands to browser state, then run
509 return ScheduleGetLastSessionCommands(
510 base::Bind(&SessionService::OnGotSessionCommands
,
511 base::Unretained(this), callback
),
515 void SessionService::Save() {
516 bool had_commands
= !pending_commands().empty();
517 BaseSessionService::Save();
519 RecordSessionUpdateHistogramData(chrome::NOTIFICATION_SESSION_SERVICE_SAVED
,
520 &last_updated_save_time_
);
521 content::NotificationService::current()->Notify(
522 chrome::NOTIFICATION_SESSION_SERVICE_SAVED
,
523 content::Source
<Profile
>(profile()),
524 content::NotificationService::NoDetails());
528 void SessionService::Init() {
529 // Register for the notifications we're interested in.
530 registrar_
.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED
,
531 content::NotificationService::AllSources());
532 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED
,
533 content::NotificationService::AllSources());
534 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
535 content::NotificationService::AllSources());
537 this, chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED
,
538 content::NotificationService::AllSources());
540 BrowserList::AddObserver(this);
543 bool SessionService::processed_any_commands() {
544 return backend()->inited() || !pending_commands().empty();
547 bool SessionService::ShouldNewWindowStartSession() {
548 // ChromeOS and OSX have different ideas of application lifetime than
549 // the other platforms.
550 // On ChromeOS opening a new window should never start a new session.
551 #if defined(OS_CHROMEOS)
552 if (!force_browser_not_alive_with_no_windows_
)
555 if (!has_open_trackable_browsers_
&&
556 !StartupBrowserCreator::InSynchronousProfileLaunch() &&
557 !SessionRestore::IsRestoring(profile())
558 #if defined(OS_MACOSX)
559 // On OSX, a new window should not start a new session if it was opened
560 // from the dock or the menubar.
561 && !app_controller_mac::IsOpeningNewWindow()
569 bool SessionService::RestoreIfNecessary(const std::vector
<GURL
>& urls_to_open
,
571 if (ShouldNewWindowStartSession()) {
572 // We're going from no tabbed browsers to a tabbed browser (and not in
573 // process startup), restore the last session.
574 if (move_on_new_browser_
) {
575 // Make the current session the last.
576 MoveCurrentSessionToLastSession();
577 move_on_new_browser_
= false;
579 SessionStartupPref pref
= StartupBrowserCreator::GetSessionStartupPref(
580 *CommandLine::ForCurrentProcess(), profile());
581 if (pref
.type
== SessionStartupPref::LAST
) {
582 SessionRestore::RestoreSession(
584 browser
? browser
->host_desktop_type() : chrome::GetActiveDesktop(),
585 browser
? 0 : SessionRestore::ALWAYS_CREATE_TABBED_BROWSER
,
593 void SessionService::Observe(int type
,
594 const content::NotificationSource
& source
,
595 const content::NotificationDetails
& details
) {
596 // All of our messages have the NavigationController as the source.
598 case content::NOTIFICATION_NAV_LIST_PRUNED
: {
599 WebContents
* web_contents
=
600 content::Source
<content::NavigationController
>(source
).ptr()->
602 SessionTabHelper
* session_tab_helper
=
603 SessionTabHelper::FromWebContents(web_contents
);
604 if (!session_tab_helper
|| web_contents
->GetBrowserContext() != profile())
606 content::Details
<content::PrunedDetails
> pruned_details(details
);
607 if (pruned_details
->from_front
) {
608 TabNavigationPathPrunedFromFront(
609 session_tab_helper
->window_id(),
610 session_tab_helper
->session_id(),
611 pruned_details
->count
);
613 TabNavigationPathPrunedFromBack(
614 session_tab_helper
->window_id(),
615 session_tab_helper
->session_id(),
616 web_contents
->GetController().GetEntryCount());
618 RecordSessionUpdateHistogramData(type
,
619 &last_updated_nav_list_pruned_time_
);
623 case content::NOTIFICATION_NAV_ENTRY_CHANGED
: {
624 WebContents
* web_contents
=
625 content::Source
<content::NavigationController
>(source
).ptr()->
627 SessionTabHelper
* session_tab_helper
=
628 SessionTabHelper::FromWebContents(web_contents
);
629 if (!session_tab_helper
|| web_contents
->GetBrowserContext() != profile())
631 content::Details
<content::EntryChangedDetails
> changed(details
);
632 const SerializedNavigationEntry navigation
=
633 SerializedNavigationEntry::FromNavigationEntry(
634 changed
->index
, *changed
->changed_entry
);
635 UpdateTabNavigation(session_tab_helper
->window_id(),
636 session_tab_helper
->session_id(),
641 case content::NOTIFICATION_NAV_ENTRY_COMMITTED
: {
642 WebContents
* web_contents
=
643 content::Source
<content::NavigationController
>(source
).ptr()->
645 SessionTabHelper
* session_tab_helper
=
646 SessionTabHelper::FromWebContents(web_contents
);
647 if (!session_tab_helper
|| web_contents
->GetBrowserContext() != profile())
649 int current_entry_index
=
650 web_contents
->GetController().GetCurrentEntryIndex();
651 SetSelectedNavigationIndex(
652 session_tab_helper
->window_id(),
653 session_tab_helper
->session_id(),
654 current_entry_index
);
655 const SerializedNavigationEntry navigation
=
656 SerializedNavigationEntry::FromNavigationEntry(
658 *web_contents
->GetController().GetEntryAtIndex(
659 current_entry_index
));
661 session_tab_helper
->window_id(),
662 session_tab_helper
->session_id(),
664 content::Details
<content::LoadCommittedDetails
> changed(details
);
665 if (changed
->type
== content::NAVIGATION_TYPE_NEW_PAGE
||
666 changed
->type
== content::NAVIGATION_TYPE_EXISTING_PAGE
) {
667 RecordSessionUpdateHistogramData(type
,
668 &last_updated_nav_entry_commit_time_
);
673 case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED
: {
674 extensions::TabHelper
* extension_tab_helper
=
675 content::Source
<extensions::TabHelper
>(source
).ptr();
676 if (extension_tab_helper
->web_contents()->GetBrowserContext() !=
680 if (extension_tab_helper
->extension_app()) {
681 SessionTabHelper
* session_tab_helper
=
682 SessionTabHelper::FromWebContents(
683 extension_tab_helper
->web_contents());
684 SetTabExtensionAppID(session_tab_helper
->window_id(),
685 session_tab_helper
->session_id(),
686 extension_tab_helper
->extension_app()->id());
696 void SessionService::OnBrowserSetLastActive(Browser
* browser
) {
697 if (ShouldTrackBrowser(browser
))
698 ScheduleCommand(CreateSetActiveWindowCommand(browser
->session_id()));
701 void SessionService::SetTabExtensionAppID(
702 const SessionID
& window_id
,
703 const SessionID
& tab_id
,
704 const std::string
& extension_app_id
) {
705 if (!ShouldTrackChangesToWindow(window_id
))
708 ScheduleCommand(CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID
,
709 tab_id
.id(), extension_app_id
));
712 SessionCommand
* SessionService::CreateSetSelectedTabInWindow(
713 const SessionID
& window_id
,
715 SelectedTabInIndexPayload payload
= { 0 };
716 payload
.id
= window_id
.id();
717 payload
.index
= index
;
718 SessionCommand
* command
= new SessionCommand(kCommandSetSelectedTabInIndex
,
720 memcpy(command
->contents(), &payload
, sizeof(payload
));
724 SessionCommand
* SessionService::CreateSetTabWindowCommand(
725 const SessionID
& window_id
,
726 const SessionID
& tab_id
) {
727 SessionID::id_type payload
[] = { window_id
.id(), tab_id
.id() };
728 SessionCommand
* command
=
729 new SessionCommand(kCommandSetTabWindow
, sizeof(payload
));
730 memcpy(command
->contents(), payload
, sizeof(payload
));
734 SessionCommand
* SessionService::CreateSetWindowBoundsCommand(
735 const SessionID
& window_id
,
736 const gfx::Rect
& bounds
,
737 ui::WindowShowState show_state
) {
738 WindowBoundsPayload3 payload
= { 0 };
739 payload
.window_id
= window_id
.id();
740 payload
.x
= bounds
.x();
741 payload
.y
= bounds
.y();
742 payload
.w
= bounds
.width();
743 payload
.h
= bounds
.height();
744 payload
.show_state
= AdjustShowState(show_state
);
745 SessionCommand
* command
= new SessionCommand(kCommandSetWindowBounds3
,
747 memcpy(command
->contents(), &payload
, sizeof(payload
));
751 SessionCommand
* SessionService::CreateSetTabIndexInWindowCommand(
752 const SessionID
& tab_id
,
754 TabIndexInWindowPayload payload
= { 0 };
755 payload
.id
= tab_id
.id();
756 payload
.index
= new_index
;
757 SessionCommand
* command
=
758 new SessionCommand(kCommandSetTabIndexInWindow
, sizeof(payload
));
759 memcpy(command
->contents(), &payload
, sizeof(payload
));
763 SessionCommand
* SessionService::CreateTabClosedCommand(
764 const SessionID::id_type tab_id
) {
765 ClosedPayload payload
;
766 // Because of what appears to be a compiler bug setting payload to {0} doesn't
767 // set the padding to 0, resulting in Purify reporting an UMR when we write
768 // the structure to disk. To avoid this we explicitly memset the struct.
769 memset(&payload
, 0, sizeof(payload
));
771 payload
.close_time
= Time::Now().ToInternalValue();
772 SessionCommand
* command
=
773 new SessionCommand(kCommandTabClosed
, sizeof(payload
));
774 memcpy(command
->contents(), &payload
, sizeof(payload
));
778 SessionCommand
* SessionService::CreateWindowClosedCommand(
779 const SessionID::id_type window_id
) {
780 ClosedPayload payload
;
781 // See comment in CreateTabClosedCommand as to why we do this.
782 memset(&payload
, 0, sizeof(payload
));
783 payload
.id
= window_id
;
784 payload
.close_time
= Time::Now().ToInternalValue();
785 SessionCommand
* command
=
786 new SessionCommand(kCommandWindowClosed
, sizeof(payload
));
787 memcpy(command
->contents(), &payload
, sizeof(payload
));
791 SessionCommand
* SessionService::CreateSetSelectedNavigationIndexCommand(
792 const SessionID
& tab_id
,
794 SelectedNavigationIndexPayload payload
= { 0 };
795 payload
.id
= tab_id
.id();
796 payload
.index
= index
;
797 SessionCommand
* command
= new SessionCommand(
798 kCommandSetSelectedNavigationIndex
, sizeof(payload
));
799 memcpy(command
->contents(), &payload
, sizeof(payload
));
803 SessionCommand
* SessionService::CreateSetWindowTypeCommand(
804 const SessionID
& window_id
,
806 WindowTypePayload payload
= { 0 };
807 payload
.id
= window_id
.id();
808 payload
.index
= static_cast<int32
>(type
);
809 SessionCommand
* command
= new SessionCommand(
810 kCommandSetWindowType
, sizeof(payload
));
811 memcpy(command
->contents(), &payload
, sizeof(payload
));
815 SessionCommand
* SessionService::CreatePinnedStateCommand(
816 const SessionID
& tab_id
,
818 PinnedStatePayload payload
= { 0 };
819 payload
.tab_id
= tab_id
.id();
820 payload
.pinned_state
= is_pinned
;
821 SessionCommand
* command
=
822 new SessionCommand(kCommandSetPinnedState
, sizeof(payload
));
823 memcpy(command
->contents(), &payload
, sizeof(payload
));
827 SessionCommand
* SessionService::CreateSessionStorageAssociatedCommand(
828 const SessionID
& tab_id
,
829 const std::string
& session_storage_persistent_id
) {
831 pickle
.WriteInt(tab_id
.id());
832 pickle
.WriteString(session_storage_persistent_id
);
833 return new SessionCommand(kCommandSessionStorageAssociated
, pickle
);
836 SessionCommand
* SessionService::CreateSetActiveWindowCommand(
837 const SessionID
& window_id
) {
838 ActiveWindowPayload payload
= 0;
839 payload
= window_id
.id();
840 SessionCommand
* command
=
841 new SessionCommand(kCommandSetActiveWindow
, sizeof(payload
));
842 memcpy(command
->contents(), &payload
, sizeof(payload
));
846 void SessionService::OnGotSessionCommands(
847 const SessionCallback
& callback
,
848 ScopedVector
<SessionCommand
> commands
) {
849 ScopedVector
<SessionWindow
> valid_windows
;
850 SessionID::id_type active_window_id
= 0;
852 RestoreSessionFromCommands(
853 commands
.get(), &valid_windows
.get(), &active_window_id
);
854 callback
.Run(valid_windows
.Pass(), active_window_id
);
857 void SessionService::RestoreSessionFromCommands(
858 const std::vector
<SessionCommand
*>& commands
,
859 std::vector
<SessionWindow
*>* valid_windows
,
860 SessionID::id_type
* active_window_id
) {
861 std::map
<int, SessionTab
*> tabs
;
862 std::map
<int, SessionWindow
*> windows
;
864 VLOG(1) << "RestoreSessionFromCommands " << commands
.size();
865 if (CreateTabsAndWindows(commands
, &tabs
, &windows
, active_window_id
)) {
866 AddTabsToWindows(&tabs
, &windows
);
867 SortTabsBasedOnVisualOrderAndPrune(&windows
, valid_windows
);
868 UpdateSelectedTabIndex(valid_windows
);
870 STLDeleteValues(&tabs
);
871 // Don't delete conents of windows, that is done by the caller as all
872 // valid windows are added to valid_windows.
875 void SessionService::UpdateSelectedTabIndex(
876 std::vector
<SessionWindow
*>* windows
) {
877 for (std::vector
<SessionWindow
*>::const_iterator i
= windows
->begin();
878 i
!= windows
->end(); ++i
) {
879 // See note in SessionWindow as to why we do this.
881 for (std::vector
<SessionTab
*>::const_iterator j
= (*i
)->tabs
.begin();
882 j
!= (*i
)->tabs
.end(); ++j
) {
883 if ((*j
)->tab_visual_index
== (*i
)->selected_tab_index
) {
884 new_index
= static_cast<int>(j
- (*i
)->tabs
.begin());
888 (*i
)->selected_tab_index
= new_index
;
892 SessionWindow
* SessionService::GetWindow(
893 SessionID::id_type window_id
,
894 IdToSessionWindow
* windows
) {
895 std::map
<int, SessionWindow
*>::iterator i
= windows
->find(window_id
);
896 if (i
== windows
->end()) {
897 SessionWindow
* window
= new SessionWindow();
898 window
->window_id
.set_id(window_id
);
899 (*windows
)[window_id
] = window
;
905 SessionTab
* SessionService::GetTab(
906 SessionID::id_type tab_id
,
907 IdToSessionTab
* tabs
) {
909 std::map
<int, SessionTab
*>::iterator i
= tabs
->find(tab_id
);
910 if (i
== tabs
->end()) {
911 SessionTab
* tab
= new SessionTab();
912 tab
->tab_id
.set_id(tab_id
);
913 (*tabs
)[tab_id
] = tab
;
919 std::vector
<SerializedNavigationEntry
>::iterator
920 SessionService::FindClosestNavigationWithIndex(
921 std::vector
<SerializedNavigationEntry
>* navigations
,
924 for (std::vector
<SerializedNavigationEntry
>::iterator
925 i
= navigations
->begin(); i
!= navigations
->end(); ++i
) {
926 if (i
->index() >= index
)
929 return navigations
->end();
932 // Function used in sorting windows. Sorting is done based on window id. As
933 // window ids increment for each new window, this effectively sorts by creation
935 static bool WindowOrderSortFunction(const SessionWindow
* w1
,
936 const SessionWindow
* w2
) {
937 return w1
->window_id
.id() < w2
->window_id
.id();
940 // Compares the two tabs based on visual index.
941 static bool TabVisualIndexSortFunction(const SessionTab
* t1
,
942 const SessionTab
* t2
) {
943 const int delta
= t1
->tab_visual_index
- t2
->tab_visual_index
;
944 return delta
== 0 ? (t1
->tab_id
.id() < t2
->tab_id
.id()) : (delta
< 0);
947 void SessionService::SortTabsBasedOnVisualOrderAndPrune(
948 std::map
<int, SessionWindow
*>* windows
,
949 std::vector
<SessionWindow
*>* valid_windows
) {
950 std::map
<int, SessionWindow
*>::iterator i
= windows
->begin();
951 while (i
!= windows
->end()) {
952 SessionWindow
* window
= i
->second
;
953 AppType app_type
= window
->app_name
.empty() ? TYPE_NORMAL
: TYPE_APP
;
954 if (window
->tabs
.empty() || window
->is_constrained
||
955 !should_track_changes_for_browser_type(
956 static_cast<Browser::Type
>(window
->type
),
961 // Valid window; sort the tabs and add it to the list of valid windows.
962 std::sort(window
->tabs
.begin(), window
->tabs
.end(),
963 &TabVisualIndexSortFunction
);
964 // Otherwise, add the window such that older windows appear first.
965 if (valid_windows
->empty()) {
966 valid_windows
->push_back(window
);
968 valid_windows
->insert(
969 std::upper_bound(valid_windows
->begin(), valid_windows
->end(),
970 window
, &WindowOrderSortFunction
),
978 void SessionService::AddTabsToWindows(std::map
<int, SessionTab
*>* tabs
,
979 std::map
<int, SessionWindow
*>* windows
) {
980 VLOG(1) << "AddTabsToWindws";
981 VLOG(1) << "Tabs " << tabs
->size() << ", windows " << windows
->size();
982 std::map
<int, SessionTab
*>::iterator i
= tabs
->begin();
983 while (i
!= tabs
->end()) {
984 SessionTab
* tab
= i
->second
;
985 if (tab
->window_id
.id() && !tab
->navigations
.empty()) {
986 SessionWindow
* window
= GetWindow(tab
->window_id
.id(), windows
);
987 window
->tabs
.push_back(tab
);
990 // See note in SessionTab as to why we do this.
991 std::vector
<SerializedNavigationEntry
>::iterator j
=
992 FindClosestNavigationWithIndex(&(tab
->navigations
),
993 tab
->current_navigation_index
);
994 if (j
== tab
->navigations
.end()) {
995 tab
->current_navigation_index
=
996 static_cast<int>(tab
->navigations
.size() - 1);
998 tab
->current_navigation_index
=
999 static_cast<int>(j
- tab
->navigations
.begin());
1002 // Never got a set tab index in window, or tabs are empty, nothing
1009 bool SessionService::CreateTabsAndWindows(
1010 const std::vector
<SessionCommand
*>& data
,
1011 std::map
<int, SessionTab
*>* tabs
,
1012 std::map
<int, SessionWindow
*>* windows
,
1013 SessionID::id_type
* active_window_id
) {
1014 // If the file is corrupt (command with wrong size, or unknown command), we
1015 // still return true and attempt to restore what we we can.
1016 VLOG(1) << "CreateTabsAndWindows";
1018 startup_metric_utils::ScopedSlowStartupUMA
1019 scoped_timer("Startup.SlowStartupSessionServiceCreateTabsAndWindows");
1021 for (std::vector
<SessionCommand
*>::const_iterator i
= data
.begin();
1022 i
!= data
.end(); ++i
) {
1023 const SessionCommand::id_type kCommandSetWindowBounds2
= 10;
1024 const SessionCommand
* command
= *i
;
1026 VLOG(1) << "Read command " << (int) command
->id();
1027 switch (command
->id()) {
1028 case kCommandSetTabWindow
: {
1029 SessionID::id_type payload
[2];
1030 if (!command
->GetPayload(payload
, sizeof(payload
))) {
1031 VLOG(1) << "Failed reading command " << command
->id();
1034 GetTab(payload
[1], tabs
)->window_id
.set_id(payload
[0]);
1038 // This is here for forward migration only. New data is saved with
1039 // |kCommandSetWindowBounds3|.
1040 case kCommandSetWindowBounds2
: {
1041 WindowBoundsPayload2 payload
;
1042 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1043 VLOG(1) << "Failed reading command " << command
->id();
1046 GetWindow(payload
.window_id
, windows
)->bounds
.SetRect(payload
.x
,
1050 GetWindow(payload
.window_id
, windows
)->show_state
=
1051 payload
.is_maximized
?
1052 ui::SHOW_STATE_MAXIMIZED
: ui::SHOW_STATE_NORMAL
;
1056 case kCommandSetWindowBounds3
: {
1057 WindowBoundsPayload3 payload
;
1058 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1059 VLOG(1) << "Failed reading command " << command
->id();
1062 GetWindow(payload
.window_id
, windows
)->bounds
.SetRect(payload
.x
,
1066 // SHOW_STATE_INACTIVE is not persisted.
1067 ui::WindowShowState show_state
= ui::SHOW_STATE_NORMAL
;
1068 if (payload
.show_state
> ui::SHOW_STATE_DEFAULT
&&
1069 payload
.show_state
< ui::SHOW_STATE_END
&&
1070 payload
.show_state
!= ui::SHOW_STATE_INACTIVE
) {
1071 show_state
= static_cast<ui::WindowShowState
>(payload
.show_state
);
1075 GetWindow(payload
.window_id
, windows
)->show_state
= show_state
;
1079 case kCommandSetTabIndexInWindow
: {
1080 TabIndexInWindowPayload payload
;
1081 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1082 VLOG(1) << "Failed reading command " << command
->id();
1085 GetTab(payload
.id
, tabs
)->tab_visual_index
= payload
.index
;
1089 case kCommandTabClosedObsolete
:
1090 case kCommandWindowClosedObsolete
:
1091 case kCommandTabClosed
:
1092 case kCommandWindowClosed
: {
1093 ClosedPayload payload
;
1094 if (!command
->GetPayload(&payload
, sizeof(payload
)) &&
1095 !MigrateClosedPayload(*command
, &payload
)) {
1096 VLOG(1) << "Failed reading command " << command
->id();
1099 if (command
->id() == kCommandTabClosed
||
1100 command
->id() == kCommandTabClosedObsolete
) {
1101 delete GetTab(payload
.id
, tabs
);
1102 tabs
->erase(payload
.id
);
1104 delete GetWindow(payload
.id
, windows
);
1105 windows
->erase(payload
.id
);
1110 case kCommandTabNavigationPathPrunedFromBack
: {
1111 TabNavigationPathPrunedFromBackPayload payload
;
1112 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1113 VLOG(1) << "Failed reading command " << command
->id();
1116 SessionTab
* tab
= GetTab(payload
.id
, tabs
);
1117 tab
->navigations
.erase(
1118 FindClosestNavigationWithIndex(&(tab
->navigations
), payload
.index
),
1119 tab
->navigations
.end());
1123 case kCommandTabNavigationPathPrunedFromFront
: {
1124 TabNavigationPathPrunedFromFrontPayload payload
;
1125 if (!command
->GetPayload(&payload
, sizeof(payload
)) ||
1126 payload
.index
<= 0) {
1127 VLOG(1) << "Failed reading command " << command
->id();
1130 SessionTab
* tab
= GetTab(payload
.id
, tabs
);
1132 // Update the selected navigation index.
1133 tab
->current_navigation_index
=
1134 std::max(-1, tab
->current_navigation_index
- payload
.index
);
1136 // And update the index of existing navigations.
1137 for (std::vector
<SerializedNavigationEntry
>::iterator
1138 i
= tab
->navigations
.begin();
1139 i
!= tab
->navigations
.end();) {
1140 i
->set_index(i
->index() - payload
.index
);
1142 i
= tab
->navigations
.erase(i
);
1149 case kCommandUpdateTabNavigation
: {
1150 SerializedNavigationEntry navigation
;
1151 SessionID::id_type tab_id
;
1152 if (!RestoreUpdateTabNavigationCommand(
1153 *command
, &navigation
, &tab_id
)) {
1154 VLOG(1) << "Failed reading command " << command
->id();
1157 SessionTab
* tab
= GetTab(tab_id
, tabs
);
1158 std::vector
<SerializedNavigationEntry
>::iterator i
=
1159 FindClosestNavigationWithIndex(&(tab
->navigations
),
1160 navigation
.index());
1161 if (i
!= tab
->navigations
.end() && i
->index() == navigation
.index())
1164 tab
->navigations
.insert(i
, navigation
);
1168 case kCommandSetSelectedNavigationIndex
: {
1169 SelectedNavigationIndexPayload payload
;
1170 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1171 VLOG(1) << "Failed reading command " << command
->id();
1174 GetTab(payload
.id
, tabs
)->current_navigation_index
= payload
.index
;
1178 case kCommandSetSelectedTabInIndex
: {
1179 SelectedTabInIndexPayload payload
;
1180 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1181 VLOG(1) << "Failed reading command " << command
->id();
1184 GetWindow(payload
.id
, windows
)->selected_tab_index
= payload
.index
;
1188 case kCommandSetWindowType
: {
1189 WindowTypePayload payload
;
1190 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1191 VLOG(1) << "Failed reading command " << command
->id();
1194 GetWindow(payload
.id
, windows
)->is_constrained
= false;
1195 GetWindow(payload
.id
, windows
)->type
=
1196 BrowserTypeForWindowType(
1197 static_cast<WindowType
>(payload
.index
));
1201 case kCommandSetPinnedState
: {
1202 PinnedStatePayload payload
;
1203 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1204 VLOG(1) << "Failed reading command " << command
->id();
1207 GetTab(payload
.tab_id
, tabs
)->pinned
= payload
.pinned_state
;
1211 case kCommandSetWindowAppName
: {
1212 SessionID::id_type window_id
;
1213 std::string app_name
;
1214 if (!RestoreSetWindowAppNameCommand(*command
, &window_id
, &app_name
))
1217 GetWindow(window_id
, windows
)->app_name
.swap(app_name
);
1221 case kCommandSetExtensionAppID
: {
1222 SessionID::id_type tab_id
;
1223 std::string extension_app_id
;
1224 if (!RestoreSetTabExtensionAppIDCommand(
1225 *command
, &tab_id
, &extension_app_id
)) {
1226 VLOG(1) << "Failed reading command " << command
->id();
1230 GetTab(tab_id
, tabs
)->extension_app_id
.swap(extension_app_id
);
1234 case kCommandSetTabUserAgentOverride
: {
1235 SessionID::id_type tab_id
;
1236 std::string user_agent_override
;
1237 if (!RestoreSetTabUserAgentOverrideCommand(
1238 *command
, &tab_id
, &user_agent_override
)) {
1242 GetTab(tab_id
, tabs
)->user_agent_override
.swap(user_agent_override
);
1246 case kCommandSessionStorageAssociated
: {
1247 scoped_ptr
<Pickle
> command_pickle(command
->PayloadAsPickle());
1248 SessionID::id_type command_tab_id
;
1249 std::string session_storage_persistent_id
;
1250 PickleIterator
iter(*command_pickle
.get());
1251 if (!command_pickle
->ReadInt(&iter
, &command_tab_id
) ||
1252 !command_pickle
->ReadString(&iter
, &session_storage_persistent_id
))
1254 // Associate the session storage back.
1255 GetTab(command_tab_id
, tabs
)->session_storage_persistent_id
=
1256 session_storage_persistent_id
;
1260 case kCommandSetActiveWindow
: {
1261 ActiveWindowPayload payload
;
1262 if (!command
->GetPayload(&payload
, sizeof(payload
))) {
1263 VLOG(1) << "Failed reading command " << command
->id();
1266 *active_window_id
= payload
;
1271 VLOG(1) << "Failed reading an unknown command " << command
->id();
1278 void SessionService::BuildCommandsForTab(const SessionID
& window_id
,
1280 int index_in_window
,
1282 std::vector
<SessionCommand
*>* commands
,
1283 IdToRange
* tab_to_available_range
) {
1284 DCHECK(tab
&& commands
&& window_id
.id());
1285 SessionTabHelper
* session_tab_helper
= SessionTabHelper::FromWebContents(tab
);
1286 const SessionID
& session_id(session_tab_helper
->session_id());
1287 commands
->push_back(CreateSetTabWindowCommand(window_id
, session_id
));
1289 const int current_index
= tab
->GetController().GetCurrentEntryIndex();
1290 const int min_index
= std::max(0,
1291 current_index
- max_persist_navigation_count
);
1292 const int max_index
=
1293 std::min(current_index
+ max_persist_navigation_count
,
1294 tab
->GetController().GetEntryCount());
1295 const int pending_index
= tab
->GetController().GetPendingEntryIndex();
1296 if (tab_to_available_range
) {
1297 (*tab_to_available_range
)[session_id
.id()] =
1298 std::pair
<int, int>(min_index
, max_index
);
1302 commands
->push_back(CreatePinnedStateCommand(session_id
, true));
1305 extensions::TabHelper
* extensions_tab_helper
=
1306 extensions::TabHelper::FromWebContents(tab
);
1307 if (extensions_tab_helper
->extension_app()) {
1308 commands
->push_back(
1309 CreateSetTabExtensionAppIDCommand(
1310 kCommandSetExtensionAppID
, session_id
.id(),
1311 extensions_tab_helper
->extension_app()->id()));
1314 const std::string
& ua_override
= tab
->GetUserAgentOverride();
1315 if (!ua_override
.empty()) {
1316 commands
->push_back(
1317 CreateSetTabUserAgentOverrideCommand(
1318 kCommandSetTabUserAgentOverride
, session_id
.id(), ua_override
));
1321 for (int i
= min_index
; i
< max_index
; ++i
) {
1322 const NavigationEntry
* entry
= (i
== pending_index
) ?
1323 tab
->GetController().GetPendingEntry() :
1324 tab
->GetController().GetEntryAtIndex(i
);
1326 if (ShouldTrackEntry(entry
->GetVirtualURL())) {
1327 const SerializedNavigationEntry navigation
=
1328 SerializedNavigationEntry::FromNavigationEntry(i
, *entry
);
1329 commands
->push_back(
1330 CreateUpdateTabNavigationCommand(
1331 kCommandUpdateTabNavigation
, session_id
.id(), navigation
));
1334 commands
->push_back(
1335 CreateSetSelectedNavigationIndexCommand(session_id
, current_index
));
1337 if (index_in_window
!= -1) {
1338 commands
->push_back(
1339 CreateSetTabIndexInWindowCommand(session_id
, index_in_window
));
1342 // Record the association between the sessionStorage namespace and the tab.
1343 content::SessionStorageNamespace
* session_storage_namespace
=
1344 tab
->GetController().GetDefaultSessionStorageNamespace();
1345 ScheduleCommand(CreateSessionStorageAssociatedCommand(
1346 session_tab_helper
->session_id(),
1347 session_storage_namespace
->persistent_id()));
1350 void SessionService::BuildCommandsForBrowser(
1352 std::vector
<SessionCommand
*>* commands
,
1353 IdToRange
* tab_to_available_range
,
1354 std::set
<SessionID::id_type
>* windows_to_track
) {
1355 DCHECK(browser
&& commands
);
1356 DCHECK(browser
->session_id().id());
1358 commands
->push_back(
1359 CreateSetWindowBoundsCommand(browser
->session_id(),
1360 browser
->window()->GetRestoredBounds(),
1361 browser
->window()->GetRestoredState()));
1363 commands
->push_back(CreateSetWindowTypeCommand(
1364 browser
->session_id(), WindowTypeForBrowserType(browser
->type())));
1366 if (!browser
->app_name().empty()) {
1367 commands
->push_back(CreateSetWindowAppNameCommand(
1368 kCommandSetWindowAppName
,
1369 browser
->session_id().id(),
1370 browser
->app_name()));
1373 windows_to_track
->insert(browser
->session_id().id());
1374 TabStripModel
* tab_strip
= browser
->tab_strip_model();
1375 for (int i
= 0; i
< tab_strip
->count(); ++i
) {
1376 WebContents
* tab
= tab_strip
->GetWebContentsAt(i
);
1378 BuildCommandsForTab(browser
->session_id(), tab
, i
,
1379 tab_strip
->IsTabPinned(i
),
1380 commands
, tab_to_available_range
);
1383 commands
->push_back(
1384 CreateSetSelectedTabInWindow(browser
->session_id(),
1385 browser
->tab_strip_model()->active_index()));
1388 void SessionService::BuildCommandsFromBrowsers(
1389 std::vector
<SessionCommand
*>* commands
,
1390 IdToRange
* tab_to_available_range
,
1391 std::set
<SessionID::id_type
>* windows_to_track
) {
1393 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
1394 Browser
* browser
= *it
;
1395 // Make sure the browser has tabs and a window. Browser's destructor
1396 // removes itself from the BrowserList. When a browser is closed the
1397 // destructor is not necessarily run immediately. This means it's possible
1398 // for us to get a handle to a browser that is about to be removed. If
1399 // the tab count is 0 or the window is NULL, the browser is about to be
1400 // deleted, so we ignore it.
1401 if (ShouldTrackBrowser(browser
) && browser
->tab_strip_model()->count() &&
1402 browser
->window()) {
1403 BuildCommandsForBrowser(browser
, commands
, tab_to_available_range
,
1409 void SessionService::ScheduleReset() {
1410 set_pending_reset(true);
1411 STLDeleteElements(&pending_commands());
1412 tab_to_available_range_
.clear();
1413 windows_tracking_
.clear();
1414 BuildCommandsFromBrowsers(&pending_commands(), &tab_to_available_range_
,
1415 &windows_tracking_
);
1416 if (!windows_tracking_
.empty()) {
1417 // We're lazily created on startup and won't get an initial batch of
1418 // SetWindowType messages. Set these here to make sure our state is correct.
1419 has_open_trackable_browsers_
= true;
1420 move_on_new_browser_
= true;
1425 bool SessionService::ReplacePendingCommand(SessionCommand
* command
) {
1426 // We optimize page navigations, which can happen quite frequently and
1427 // are expensive. And activation is like Highlander, there can only be one!
1428 if (command
->id() != kCommandUpdateTabNavigation
&&
1429 command
->id() != kCommandSetActiveWindow
) {
1432 for (std::vector
<SessionCommand
*>::reverse_iterator i
=
1433 pending_commands().rbegin(); i
!= pending_commands().rend(); ++i
) {
1434 SessionCommand
* existing_command
= *i
;
1435 if (command
->id() == kCommandUpdateTabNavigation
&&
1436 existing_command
->id() == kCommandUpdateTabNavigation
) {
1437 scoped_ptr
<Pickle
> command_pickle(command
->PayloadAsPickle());
1438 PickleIterator
iterator(*command_pickle
);
1439 SessionID::id_type command_tab_id
;
1440 int command_nav_index
;
1441 if (!command_pickle
->ReadInt(&iterator
, &command_tab_id
) ||
1442 !command_pickle
->ReadInt(&iterator
, &command_nav_index
)) {
1445 SessionID::id_type existing_tab_id
;
1446 int existing_nav_index
;
1448 // Creating a pickle like this means the Pickle references the data from
1449 // the command. Make sure we delete the pickle before the command, else
1450 // the pickle references deleted memory.
1451 scoped_ptr
<Pickle
> existing_pickle(existing_command
->PayloadAsPickle());
1452 iterator
= PickleIterator(*existing_pickle
);
1453 if (!existing_pickle
->ReadInt(&iterator
, &existing_tab_id
) ||
1454 !existing_pickle
->ReadInt(&iterator
, &existing_nav_index
)) {
1458 if (existing_tab_id
== command_tab_id
&&
1459 existing_nav_index
== command_nav_index
) {
1460 // existing_command is an update for the same tab/index pair. Replace
1461 // it with the new one. We need to add to the end of the list just in
1462 // case there is a prune command after the update command.
1463 delete existing_command
;
1464 pending_commands().erase(i
.base() - 1);
1465 pending_commands().push_back(command
);
1470 if (command
->id() == kCommandSetActiveWindow
&&
1471 existing_command
->id() == kCommandSetActiveWindow
) {
1473 delete existing_command
;
1480 void SessionService::ScheduleCommand(SessionCommand
* command
) {
1482 if (ReplacePendingCommand(command
))
1484 BaseSessionService::ScheduleCommand(command
);
1485 // Don't schedule a reset on tab closed/window closed. Otherwise we may
1486 // lose tabs/windows we want to restore from if we exit right after this.
1487 if (!pending_reset() && pending_window_close_ids_
.empty() &&
1488 commands_since_reset() >= kWritesPerReset
&&
1489 (command
->id() != kCommandTabClosed
&&
1490 command
->id() != kCommandWindowClosed
)) {
1495 void SessionService::CommitPendingCloses() {
1496 for (PendingTabCloseIDs::iterator i
= pending_tab_close_ids_
.begin();
1497 i
!= pending_tab_close_ids_
.end(); ++i
) {
1498 ScheduleCommand(CreateTabClosedCommand(*i
));
1500 pending_tab_close_ids_
.clear();
1502 for (PendingWindowCloseIDs::iterator i
= pending_window_close_ids_
.begin();
1503 i
!= pending_window_close_ids_
.end(); ++i
) {
1504 ScheduleCommand(CreateWindowClosedCommand(*i
));
1506 pending_window_close_ids_
.clear();
1509 bool SessionService::IsOnlyOneTabLeft() const {
1511 // We're testing, always return false.
1515 int window_count
= 0;
1516 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
1517 Browser
* browser
= *it
;
1518 const SessionID::id_type window_id
= browser
->session_id().id();
1519 if (ShouldTrackBrowser(browser
) &&
1520 window_closing_ids_
.find(window_id
) == window_closing_ids_
.end()) {
1521 if (++window_count
> 1)
1523 // By the time this is invoked the tab has been removed. As such, we use
1524 // > 0 here rather than > 1.
1525 if (browser
->tab_strip_model()->count() > 0)
1532 bool SessionService::HasOpenTrackableBrowsers(
1533 const SessionID
& window_id
) const {
1535 // We're testing, always return false.
1539 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
1540 Browser
* browser
= *it
;
1541 const SessionID::id_type browser_id
= browser
->session_id().id();
1542 if (browser_id
!= window_id
.id() &&
1543 window_closing_ids_
.find(browser_id
) == window_closing_ids_
.end() &&
1544 ShouldTrackBrowser(browser
)) {
1551 bool SessionService::ShouldTrackChangesToWindow(
1552 const SessionID
& window_id
) const {
1553 return windows_tracking_
.find(window_id
.id()) != windows_tracking_
.end();
1556 bool SessionService::ShouldTrackBrowser(Browser
* browser
) const {
1557 AppType app_type
= browser
->is_app() ? TYPE_APP
: TYPE_NORMAL
;
1558 return browser
->profile() == profile() &&
1559 should_track_changes_for_browser_type(browser
->type(), app_type
);
1562 bool SessionService::should_track_changes_for_browser_type(Browser::Type type
,
1564 #if defined(OS_CHROMEOS)
1565 // Restore app popups for chromeos alone.
1566 if (type
== Browser::TYPE_POPUP
&& app_type
== TYPE_APP
)
1570 return type
== Browser::TYPE_TABBED
||
1571 (type
== Browser::TYPE_POPUP
&& browser_defaults::kRestorePopups
);
1574 SessionService::WindowType
SessionService::WindowTypeForBrowserType(
1575 Browser::Type type
) {
1577 case Browser::TYPE_POPUP
:
1579 case Browser::TYPE_TABBED
:
1587 Browser::Type
SessionService::BrowserTypeForWindowType(WindowType type
) {
1590 return Browser::TYPE_POPUP
;
1593 return Browser::TYPE_TABBED
;
1597 void SessionService::RecordSessionUpdateHistogramData(int type
,
1598 base::TimeTicks
* last_updated_time
) {
1599 if (!last_updated_time
->is_null()) {
1600 base::TimeDelta delta
= base::TimeTicks::Now() - *last_updated_time
;
1601 // We're interested in frequent updates periods longer than
1603 bool use_long_period
= false;
1604 if (delta
>= save_delay_in_mins_
) {
1605 use_long_period
= true;
1608 case chrome::NOTIFICATION_SESSION_SERVICE_SAVED
:
1609 RecordUpdatedSaveTime(delta
, use_long_period
);
1610 RecordUpdatedSessionNavigationOrTab(delta
, use_long_period
);
1612 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED
:
1613 RecordUpdatedTabClosed(delta
, use_long_period
);
1614 RecordUpdatedSessionNavigationOrTab(delta
, use_long_period
);
1616 case content::NOTIFICATION_NAV_LIST_PRUNED
:
1617 RecordUpdatedNavListPruned(delta
, use_long_period
);
1618 RecordUpdatedSessionNavigationOrTab(delta
, use_long_period
);
1620 case content::NOTIFICATION_NAV_ENTRY_COMMITTED
:
1621 RecordUpdatedNavEntryCommit(delta
, use_long_period
);
1622 RecordUpdatedSessionNavigationOrTab(delta
, use_long_period
);
1625 NOTREACHED() << "Bad type sent to RecordSessionUpdateHistogramData";
1629 (*last_updated_time
) = base::TimeTicks::Now();
1632 void SessionService::RecordUpdatedTabClosed(base::TimeDelta delta
,
1633 bool use_long_period
) {
1634 std::string
name("SessionRestore.TabClosedPeriod");
1635 UMA_HISTOGRAM_CUSTOM_TIMES(name
,
1637 // 2500ms is the default save delay.
1638 save_delay_in_millis_
,
1639 save_delay_in_mins_
,
1641 if (use_long_period
) {
1642 std::string
long_name_("SessionRestore.TabClosedLongPeriod");
1643 UMA_HISTOGRAM_CUSTOM_TIMES(long_name_
,
1645 save_delay_in_mins_
,
1651 void SessionService::RecordUpdatedNavListPruned(base::TimeDelta delta
,
1652 bool use_long_period
) {
1653 std::string
name("SessionRestore.NavigationListPrunedPeriod");
1654 UMA_HISTOGRAM_CUSTOM_TIMES(name
,
1656 // 2500ms is the default save delay.
1657 save_delay_in_millis_
,
1658 save_delay_in_mins_
,
1660 if (use_long_period
) {
1661 std::string
long_name_("SessionRestore.NavigationListPrunedLongPeriod");
1662 UMA_HISTOGRAM_CUSTOM_TIMES(long_name_
,
1664 save_delay_in_mins_
,
1670 void SessionService::RecordUpdatedNavEntryCommit(base::TimeDelta delta
,
1671 bool use_long_period
) {
1672 std::string
name("SessionRestore.NavEntryCommittedPeriod");
1673 UMA_HISTOGRAM_CUSTOM_TIMES(name
,
1675 // 2500ms is the default save delay.
1676 save_delay_in_millis_
,
1677 save_delay_in_mins_
,
1679 if (use_long_period
) {
1680 std::string
long_name_("SessionRestore.NavEntryCommittedLongPeriod");
1681 UMA_HISTOGRAM_CUSTOM_TIMES(long_name_
,
1683 save_delay_in_mins_
,
1689 void SessionService::RecordUpdatedSessionNavigationOrTab(base::TimeDelta delta
,
1690 bool use_long_period
) {
1691 std::string
name("SessionRestore.NavOrTabUpdatePeriod");
1692 UMA_HISTOGRAM_CUSTOM_TIMES(name
,
1694 // 2500ms is the default save delay.
1695 save_delay_in_millis_
,
1696 save_delay_in_mins_
,
1698 if (use_long_period
) {
1699 std::string
long_name_("SessionRestore.NavOrTabUpdateLongPeriod");
1700 UMA_HISTOGRAM_CUSTOM_TIMES(long_name_
,
1702 save_delay_in_mins_
,
1708 void SessionService::RecordUpdatedSaveTime(base::TimeDelta delta
,
1709 bool use_long_period
) {
1710 std::string
name("SessionRestore.SavePeriod");
1711 UMA_HISTOGRAM_CUSTOM_TIMES(name
,
1713 // 2500ms is the default save delay.
1714 save_delay_in_millis_
,
1715 save_delay_in_mins_
,
1717 if (use_long_period
) {
1718 std::string
long_name_("SessionRestore.SaveLongPeriod");
1719 UMA_HISTOGRAM_CUSTOM_TIMES(long_name_
,
1721 save_delay_in_mins_
,
1727 void SessionService::TabInserted(WebContents
* contents
) {
1728 SessionTabHelper
* session_tab_helper
=
1729 SessionTabHelper::FromWebContents(contents
);
1730 if (!ShouldTrackChangesToWindow(session_tab_helper
->window_id()))
1732 SetTabWindow(session_tab_helper
->window_id(),
1733 session_tab_helper
->session_id());
1734 extensions::TabHelper
* extensions_tab_helper
=
1735 extensions::TabHelper::FromWebContents(contents
);
1736 if (extensions_tab_helper
&&
1737 extensions_tab_helper
->extension_app()) {
1738 SetTabExtensionAppID(
1739 session_tab_helper
->window_id(),
1740 session_tab_helper
->session_id(),
1741 extensions_tab_helper
->extension_app()->id());
1744 // Record the association between the SessionStorageNamespace and the
1747 // TODO(ajwong): This should be processing the whole map rather than
1748 // just the default. This in particular will not work for tabs with only
1749 // isolated apps which won't have a default partition.
1750 content::SessionStorageNamespace
* session_storage_namespace
=
1751 contents
->GetController().GetDefaultSessionStorageNamespace();
1752 ScheduleCommand(CreateSessionStorageAssociatedCommand(
1753 session_tab_helper
->session_id(),
1754 session_storage_namespace
->persistent_id()));
1755 session_storage_namespace
->SetShouldPersist(true);
1758 void SessionService::TabClosing(WebContents
* contents
) {
1759 // Allow the associated sessionStorage to get deleted; it won't be needed
1760 // in the session restore.
1761 content::SessionStorageNamespace
* session_storage_namespace
=
1762 contents
->GetController().GetDefaultSessionStorageNamespace();
1763 session_storage_namespace
->SetShouldPersist(false);
1764 SessionTabHelper
* session_tab_helper
=
1765 SessionTabHelper::FromWebContents(contents
);
1766 TabClosed(session_tab_helper
->window_id(),
1767 session_tab_helper
->session_id(),
1768 contents
->GetClosedByUserGesture());
1769 RecordSessionUpdateHistogramData(content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
1770 &last_updated_tab_closed_time_
);