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