| OLD | NEW |
| (Empty) | |
| 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/persistent_tab_restore_service.h" |
| 6 |
| 7 #include <cstring> // memcpy |
| 8 #include <vector> |
| 9 |
| 10 #include "base/basictypes.h" |
| 11 #include "base/bind.h" |
| 12 #include "base/file_path.h" |
| 13 #include "base/logging.h" |
| 14 #include "base/memory/scoped_vector.h" |
| 15 #include "base/stl_util.h" |
| 16 #include "base/time.h" |
| 17 #include "chrome/browser/sessions/session_command.h" |
| 18 #include "content/public/browser/session_storage_namespace.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 // Only written if the tab is pinned. |
| 23 typedef bool PinnedStatePayload; |
| 24 |
| 25 typedef int32 RestoredEntryPayload; |
| 26 |
| 27 // Payload used for the start of a tab close. This is the old struct that is |
| 28 // used for backwards compat when it comes to reading the session files. |
| 29 struct SelectedNavigationInTabPayload { |
| 30 SessionID::id_type id; |
| 31 int32 index; |
| 32 }; |
| 33 |
| 34 // Payload used for the start of a window close. This is the old struct that is |
| 35 // used for backwards compat when it comes to reading the session files. This |
| 36 // struct must be POD, because we memset the contents. |
| 37 struct WindowPayload { |
| 38 SessionID::id_type window_id; |
| 39 int32 selected_tab_index; |
| 40 int32 num_tabs; |
| 41 }; |
| 42 |
| 43 // Payload used for the start of a window close. This struct must be POD, |
| 44 // because we memset the contents. |
| 45 struct WindowPayload2 : WindowPayload { |
| 46 int64 timestamp; |
| 47 }; |
| 48 |
| 49 // Payload used for the start of a tab close. |
| 50 struct SelectedNavigationInTabPayload2 : SelectedNavigationInTabPayload { |
| 51 int64 timestamp; |
| 52 }; |
| 53 |
| 54 // Used to indicate what has loaded. |
| 55 enum LoadState { |
| 56 // Indicates we haven't loaded anything. |
| 57 NOT_LOADED = 1 << 0, |
| 58 |
| 59 // Indicates we've asked for the last sessions and tabs but haven't gotten |
| 60 // the result back yet. |
| 61 LOADING = 1 << 2, |
| 62 |
| 63 // Indicates we finished loading the last tabs (but not necessarily the |
| 64 // last session). |
| 65 LOADED_LAST_TABS = 1 << 3, |
| 66 |
| 67 // Indicates we finished loading the last session (but not necessarily the |
| 68 // last tabs). |
| 69 LOADED_LAST_SESSION = 1 << 4 |
| 70 }; |
| 71 |
| 72 // Identifier for commands written to file. |
| 73 // The ordering in the file is as follows: |
| 74 // . When the user closes a tab a command of type |
| 75 // kCommandSelectedNavigationInTab is written identifying the tab and |
| 76 // the selected index, then a kCommandPinnedState command if the tab was |
| 77 // pinned and kCommandSetExtensionAppID if the tab has an app id and |
| 78 // the user agent override if it was using one. This is |
| 79 // followed by any number of kCommandUpdateTabNavigation commands (1 per |
| 80 // navigation entry). |
| 81 // . When the user closes a window a kCommandSelectedNavigationInTab command |
| 82 // is written out and followed by n tab closed sequences (as previoulsy |
| 83 // described). |
| 84 // . When the user restores an entry a command of type kCommandRestoredEntry |
| 85 // is written. |
| 86 const SessionCommand::id_type kCommandUpdateTabNavigation = 1; |
| 87 const SessionCommand::id_type kCommandRestoredEntry = 2; |
| 88 const SessionCommand::id_type kCommandWindow = 3; |
| 89 const SessionCommand::id_type kCommandSelectedNavigationInTab = 4; |
| 90 const SessionCommand::id_type kCommandPinnedState = 5; |
| 91 const SessionCommand::id_type kCommandSetExtensionAppID = 6; |
| 92 const SessionCommand::id_type kCommandSetWindowAppName = 7; |
| 93 const SessionCommand::id_type kCommandSetTabUserAgentOverride = 8; |
| 94 |
| 95 // Number of entries (not commands) before we clobber the file and write |
| 96 // everything. |
| 97 const int kEntriesPerReset = 40; |
| 98 |
| 99 SessionCommand* CreateRestoredEntryCommand(SessionID::id_type entry_id) { |
| 100 RestoredEntryPayload payload = entry_id; |
| 101 SessionCommand* command = |
| 102 new SessionCommand(kCommandRestoredEntry, sizeof(payload)); |
| 103 memcpy(command->contents(), &payload, sizeof(payload)); |
| 104 return command; |
| 105 } |
| 106 |
| 107 SessionCommand* CreateSelectedNavigationInTabCommand(SessionID::id_type tab_id, |
| 108 int32 index, |
| 109 base::Time timestamp) { |
| 110 SelectedNavigationInTabPayload2 payload; |
| 111 payload.id = tab_id; |
| 112 payload.index = index; |
| 113 payload.timestamp = timestamp.ToInternalValue(); |
| 114 SessionCommand* command = |
| 115 new SessionCommand(kCommandSelectedNavigationInTab, sizeof(payload)); |
| 116 memcpy(command->contents(), &payload, sizeof(payload)); |
| 117 return command; |
| 118 } |
| 119 |
| 120 SessionCommand* CreateWindowCommand(SessionID::id_type id, |
| 121 int selected_tab_index, |
| 122 int num_tabs, |
| 123 base::Time timestamp) { |
| 124 WindowPayload2 payload; |
| 125 // |timestamp| is aligned on a 16 byte boundary, leaving 4 bytes of |
| 126 // uninitialized memory in the struct. |
| 127 memset(&payload, 0, sizeof(payload)); |
| 128 payload.window_id = id; |
| 129 payload.selected_tab_index = selected_tab_index; |
| 130 payload.num_tabs = num_tabs; |
| 131 payload.timestamp = timestamp.ToInternalValue(); |
| 132 |
| 133 SessionCommand* command = |
| 134 new SessionCommand(kCommandWindow, sizeof(payload)); |
| 135 memcpy(command->contents(), &payload, sizeof(payload)); |
| 136 return command; |
| 137 } |
| 138 |
| 139 |
| 140 } // namespace |
| 141 |
| 142 PersistentTabRestoreService::PersistentTabRestoreService( |
| 143 Profile* profile, |
| 144 TimeFactory* time_factory) |
| 145 : BaseSessionService(BaseSessionService::TAB_RESTORE, profile, |
| 146 FilePath()), |
| 147 InMemoryTabRestoreService(profile, time_factory), |
| 148 entries_to_write_(0), |
| 149 entries_written_(0), |
| 150 load_state_(NOT_LOADED) { |
| 151 } |
| 152 |
| 153 PersistentTabRestoreService::~PersistentTabRestoreService() { |
| 154 STLDeleteElements(&staging_entries_); |
| 155 } |
| 156 |
| 157 void PersistentTabRestoreService::Save() { |
| 158 int to_write_count = std::min(entries_to_write_, |
| 159 static_cast<int>(entries().size())); |
| 160 entries_to_write_ = 0; |
| 161 if (entries_written_ + to_write_count > kEntriesPerReset) { |
| 162 to_write_count = entries().size(); |
| 163 set_pending_reset(true); |
| 164 } |
| 165 if (to_write_count) { |
| 166 // Write the to_write_count most recently added entries out. The most |
| 167 // recently added entry is at the front, so we use a reverse iterator to |
| 168 // write in the order the entries were added. |
| 169 Entries::const_reverse_iterator i = entries().rbegin(); |
| 170 DCHECK(static_cast<size_t>(to_write_count) <= entries().size()); |
| 171 std::advance(i, entries().size() - static_cast<int>(to_write_count)); |
| 172 for (; i != entries().rend(); ++i) { |
| 173 Entry* entry = *i; |
| 174 if (entry->type == TAB) { |
| 175 Tab* tab = static_cast<Tab*>(entry); |
| 176 int selected_index = GetSelectedNavigationIndexToPersist(*tab); |
| 177 if (selected_index != -1) |
| 178 ScheduleCommandsForTab(*tab, selected_index); |
| 179 } else { |
| 180 ScheduleCommandsForWindow(*static_cast<Window*>(entry)); |
| 181 } |
| 182 entries_written_++; |
| 183 } |
| 184 } |
| 185 if (pending_reset()) |
| 186 entries_written_ = 0; |
| 187 BaseSessionService::Save(); |
| 188 } |
| 189 |
| 190 bool PersistentTabRestoreService::IsLoaded() const { |
| 191 return !(load_state_ & (NOT_LOADED | LOADING)); |
| 192 } |
| 193 |
| 194 void PersistentTabRestoreService::Shutdown() { |
| 195 if (backend()) |
| 196 Save(); |
| 197 } |
| 198 |
| 199 void PersistentTabRestoreService::OnClearEntries() { |
| 200 const Entries& entries = TabRestoreService::entries(); |
| 201 // Mark all the tabs as closed so that we don't attempt to restore them. |
| 202 for (Entries::const_iterator i = entries.begin(); i != entries.end(); ++i) |
| 203 ScheduleCommand(CreateRestoredEntryCommand((*i)->id)); |
| 204 |
| 205 entries_to_write_ = 0; |
| 206 |
| 207 // Schedule a pending reset so that we nuke the file on next write. |
| 208 set_pending_reset(true); |
| 209 |
| 210 // Schedule a command, otherwise if there are no pending commands Save does |
| 211 // nothing. |
| 212 ScheduleCommand(CreateRestoredEntryCommand(1)); |
| 213 } |
| 214 |
| 215 void PersistentTabRestoreService::OnRestoreEntryById( |
| 216 SessionID::id_type id, |
| 217 Entries::const_iterator entry_iterator) { |
| 218 const Entries& entries = TabRestoreService::entries(); |
| 219 size_t index = 0; |
| 220 for (Entries::const_iterator j = entries.begin(); |
| 221 j != entry_iterator && j != entries.end(); |
| 222 ++j, ++index) {} |
| 223 if (static_cast<int>(index) < entries_to_write_) |
| 224 entries_to_write_--; |
| 225 |
| 226 ScheduleCommand(CreateRestoredEntryCommand(id)); |
| 227 } |
| 228 |
| 229 void PersistentTabRestoreService::OnAddEntry() { |
| 230 // Start the save timer, when it fires we'll generate the commands. |
| 231 StartSaveTimer(); |
| 232 entries_to_write_++; |
| 233 } |
| 234 |
| 235 int PersistentTabRestoreService::GetSelectedNavigationIndexToPersist( |
| 236 const Tab& tab) { |
| 237 const std::vector<TabNavigation>& navigations = tab.navigations; |
| 238 int selected_index = tab.current_navigation_index; |
| 239 int max_index = static_cast<int>(navigations.size()); |
| 240 |
| 241 // Find the first navigation to persist. We won't persist the selected |
| 242 // navigation if ShouldTrackEntry returns false. |
| 243 while (selected_index >= 0 && |
| 244 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { |
| 245 selected_index--; |
| 246 } |
| 247 |
| 248 if (selected_index != -1) |
| 249 return selected_index; |
| 250 |
| 251 // Couldn't find a navigation to persist going back, go forward. |
| 252 selected_index = tab.current_navigation_index + 1; |
| 253 while (selected_index < max_index && |
| 254 !ShouldTrackEntry(navigations[selected_index].virtual_url())) { |
| 255 selected_index++; |
| 256 } |
| 257 |
| 258 return (selected_index == max_index) ? -1 : selected_index; |
| 259 } |
| 260 |
| 261 void PersistentTabRestoreService::ScheduleCommandsForTab(const Tab& tab, |
| 262 int selected_index) { |
| 263 const std::vector<TabNavigation>& navigations = tab.navigations; |
| 264 int max_index = static_cast<int>(navigations.size()); |
| 265 |
| 266 // Determine the first navigation we'll persist. |
| 267 int valid_count_before_selected = 0; |
| 268 int first_index_to_persist = selected_index; |
| 269 for (int i = selected_index - 1; i >= 0 && |
| 270 valid_count_before_selected < max_persist_navigation_count; --i) { |
| 271 if (ShouldTrackEntry(navigations[i].virtual_url())) { |
| 272 first_index_to_persist = i; |
| 273 valid_count_before_selected++; |
| 274 } |
| 275 } |
| 276 |
| 277 // Write the command that identifies the selected tab. |
| 278 ScheduleCommand( |
| 279 CreateSelectedNavigationInTabCommand(tab.id, |
| 280 valid_count_before_selected, |
| 281 tab.timestamp)); |
| 282 |
| 283 if (tab.pinned) { |
| 284 PinnedStatePayload payload = true; |
| 285 SessionCommand* command = |
| 286 new SessionCommand(kCommandPinnedState, sizeof(payload)); |
| 287 memcpy(command->contents(), &payload, sizeof(payload)); |
| 288 ScheduleCommand(command); |
| 289 } |
| 290 |
| 291 if (!tab.extension_app_id.empty()) { |
| 292 ScheduleCommand( |
| 293 CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID, tab.id, |
| 294 tab.extension_app_id)); |
| 295 } |
| 296 |
| 297 if (!tab.user_agent_override.empty()) { |
| 298 ScheduleCommand( |
| 299 CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride, |
| 300 tab.id, tab.user_agent_override)); |
| 301 } |
| 302 |
| 303 // Then write the navigations. |
| 304 for (int i = first_index_to_persist, wrote_count = 0; |
| 305 i < max_index && wrote_count < 2 * max_persist_navigation_count; ++i) { |
| 306 if (ShouldTrackEntry(navigations[i].virtual_url())) { |
| 307 ScheduleCommand( |
| 308 CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation, tab.id, |
| 309 navigations[i])); |
| 310 } |
| 311 } |
| 312 } |
| 313 |
| 314 void PersistentTabRestoreService::ScheduleCommandsForWindow( |
| 315 const Window& window) { |
| 316 DCHECK(!window.tabs.empty()); |
| 317 int selected_tab = window.selected_tab_index; |
| 318 int valid_tab_count = 0; |
| 319 int real_selected_tab = selected_tab; |
| 320 for (size_t i = 0; i < window.tabs.size(); ++i) { |
| 321 if (GetSelectedNavigationIndexToPersist(window.tabs[i]) != -1) { |
| 322 valid_tab_count++; |
| 323 } else if (static_cast<int>(i) < selected_tab) { |
| 324 real_selected_tab--; |
| 325 } |
| 326 } |
| 327 if (valid_tab_count == 0) |
| 328 return; // No tabs to persist. |
| 329 |
| 330 ScheduleCommand( |
| 331 CreateWindowCommand(window.id, |
| 332 std::min(real_selected_tab, valid_tab_count - 1), |
| 333 valid_tab_count, |
| 334 window.timestamp)); |
| 335 |
| 336 if (!window.app_name.empty()) { |
| 337 ScheduleCommand( |
| 338 CreateSetWindowAppNameCommand(kCommandSetWindowAppName, |
| 339 window.id, |
| 340 window.app_name)); |
| 341 } |
| 342 |
| 343 for (size_t i = 0; i < window.tabs.size(); ++i) { |
| 344 int selected_index = GetSelectedNavigationIndexToPersist(window.tabs[i]); |
| 345 if (selected_index != -1) |
| 346 ScheduleCommandsForTab(window.tabs[i], selected_index); |
| 347 } |
| 348 } |
| 349 |
| 350 void PersistentTabRestoreService::OnGotPreviousSession( |
| 351 Handle handle, |
| 352 std::vector<SessionWindow*>* windows, |
| 353 SessionID::id_type ignored_active_window) { |
| 354 std::vector<Entry*> entries; |
| 355 CreateEntriesFromWindows(windows, &entries); |
| 356 // Previous session tabs go first. |
| 357 staging_entries_.insert(staging_entries_.begin(), entries.begin(), |
| 358 entries.end()); |
| 359 load_state_ |= LOADED_LAST_SESSION; |
| 360 LoadStateChanged(); |
| 361 } |
| 362 |
| 363 void PersistentTabRestoreService::OnGotLastSessionCommands( |
| 364 Handle handle, |
| 365 scoped_refptr<InternalGetCommandsRequest> request) { |
| 366 std::vector<Entry*> entries; |
| 367 CreateEntriesFromCommands(request, &entries); |
| 368 // Closed tabs always go to the end. |
| 369 staging_entries_.insert(staging_entries_.end(), entries.begin(), |
| 370 entries.end()); |
| 371 load_state_ |= LOADED_LAST_TABS; |
| 372 LoadStateChanged(); |
| 373 } |
| 374 |
| 375 void PersistentTabRestoreService::CreateEntriesFromCommands( |
| 376 scoped_refptr<InternalGetCommandsRequest> request, |
| 377 std::vector<Entry*>* loaded_entries) { |
| 378 if (request->canceled() || entries().size() == kMaxEntries) |
| 379 return; |
| 380 |
| 381 std::vector<SessionCommand*>& commands = request->commands; |
| 382 // Iterate through the commands populating entries and id_to_entry. |
| 383 ScopedVector<Entry> entries; |
| 384 IDToEntry id_to_entry; |
| 385 // If non-null we're processing the navigations of this tab. |
| 386 Tab* current_tab = NULL; |
| 387 // If non-null we're processing the tabs of this window. |
| 388 Window* current_window = NULL; |
| 389 // If > 0, we've gotten a window command but not all the tabs yet. |
| 390 int pending_window_tabs = 0; |
| 391 for (std::vector<SessionCommand*>::const_iterator i = commands.begin(); |
| 392 i != commands.end(); ++i) { |
| 393 const SessionCommand& command = *(*i); |
| 394 switch (command.id()) { |
| 395 case kCommandRestoredEntry: { |
| 396 if (pending_window_tabs > 0) { |
| 397 // Should never receive a restored command while waiting for all the |
| 398 // tabs in a window. |
| 399 return; |
| 400 } |
| 401 |
| 402 current_tab = NULL; |
| 403 current_window = NULL; |
| 404 |
| 405 RestoredEntryPayload payload; |
| 406 if (!command.GetPayload(&payload, sizeof(payload))) |
| 407 return; |
| 408 RemoveEntryByID(payload, &id_to_entry, &(entries.get())); |
| 409 break; |
| 410 } |
| 411 |
| 412 case kCommandWindow: { |
| 413 WindowPayload2 payload; |
| 414 if (pending_window_tabs > 0) { |
| 415 // Should never receive a window command while waiting for all the |
| 416 // tabs in a window. |
| 417 return; |
| 418 } |
| 419 |
| 420 // Try the new payload first |
| 421 if (!command.GetPayload(&payload, sizeof(payload))) { |
| 422 // then the old payload |
| 423 WindowPayload old_payload; |
| 424 if (!command.GetPayload(&old_payload, sizeof(old_payload))) |
| 425 return; |
| 426 |
| 427 // Copy the old payload data to the new payload. |
| 428 payload.window_id = old_payload.window_id; |
| 429 payload.selected_tab_index = old_payload.selected_tab_index; |
| 430 payload.num_tabs = old_payload.num_tabs; |
| 431 // Since we don't have a time use time 0 which is used to mark as an |
| 432 // unknown timestamp. |
| 433 payload.timestamp = 0; |
| 434 } |
| 435 |
| 436 pending_window_tabs = payload.num_tabs; |
| 437 if (pending_window_tabs <= 0) { |
| 438 // Should always have at least 1 tab. Likely indicates corruption. |
| 439 return; |
| 440 } |
| 441 |
| 442 RemoveEntryByID(payload.window_id, &id_to_entry, &(entries.get())); |
| 443 |
| 444 current_window = new Window(); |
| 445 current_window->selected_tab_index = payload.selected_tab_index; |
| 446 current_window->timestamp = |
| 447 base::Time::FromInternalValue(payload.timestamp); |
| 448 entries.push_back(current_window); |
| 449 id_to_entry[payload.window_id] = current_window; |
| 450 break; |
| 451 } |
| 452 |
| 453 case kCommandSelectedNavigationInTab: { |
| 454 SelectedNavigationInTabPayload2 payload; |
| 455 if (!command.GetPayload(&payload, sizeof(payload))) { |
| 456 SelectedNavigationInTabPayload old_payload; |
| 457 if (!command.GetPayload(&old_payload, sizeof(old_payload))) |
| 458 return; |
| 459 payload.id = old_payload.id; |
| 460 payload.index = old_payload.index; |
| 461 // Since we don't have a time use time 0 which is used to mark as an |
| 462 // unknown timestamp. |
| 463 payload.timestamp = 0; |
| 464 } |
| 465 |
| 466 if (pending_window_tabs > 0) { |
| 467 if (!current_window) { |
| 468 // We should have created a window already. |
| 469 NOTREACHED(); |
| 470 return; |
| 471 } |
| 472 current_window->tabs.resize(current_window->tabs.size() + 1); |
| 473 current_tab = &(current_window->tabs.back()); |
| 474 if (--pending_window_tabs == 0) |
| 475 current_window = NULL; |
| 476 } else { |
| 477 RemoveEntryByID(payload.id, &id_to_entry, &(entries.get())); |
| 478 current_tab = new Tab(); |
| 479 id_to_entry[payload.id] = current_tab; |
| 480 current_tab->timestamp = |
| 481 base::Time::FromInternalValue(payload.timestamp); |
| 482 entries.push_back(current_tab); |
| 483 } |
| 484 current_tab->current_navigation_index = payload.index; |
| 485 break; |
| 486 } |
| 487 |
| 488 case kCommandUpdateTabNavigation: { |
| 489 if (!current_tab) { |
| 490 // Should be in a tab when we get this. |
| 491 return; |
| 492 } |
| 493 current_tab->navigations.resize(current_tab->navigations.size() + 1); |
| 494 SessionID::id_type tab_id; |
| 495 if (!RestoreUpdateTabNavigationCommand( |
| 496 command, ¤t_tab->navigations.back(), &tab_id)) { |
| 497 return; |
| 498 } |
| 499 break; |
| 500 } |
| 501 |
| 502 case kCommandPinnedState: { |
| 503 if (!current_tab) { |
| 504 // Should be in a tab when we get this. |
| 505 return; |
| 506 } |
| 507 // NOTE: payload doesn't matter. kCommandPinnedState is only written if |
| 508 // tab is pinned. |
| 509 current_tab->pinned = true; |
| 510 break; |
| 511 } |
| 512 |
| 513 case kCommandSetWindowAppName: { |
| 514 if (!current_window) { |
| 515 // We should have created a window already. |
| 516 NOTREACHED(); |
| 517 return; |
| 518 } |
| 519 |
| 520 SessionID::id_type window_id; |
| 521 std::string app_name; |
| 522 if (!RestoreSetWindowAppNameCommand(command, &window_id, &app_name)) |
| 523 return; |
| 524 |
| 525 current_window->app_name.swap(app_name); |
| 526 break; |
| 527 } |
| 528 |
| 529 case kCommandSetExtensionAppID: { |
| 530 if (!current_tab) { |
| 531 // Should be in a tab when we get this. |
| 532 return; |
| 533 } |
| 534 SessionID::id_type tab_id; |
| 535 std::string extension_app_id; |
| 536 if (!RestoreSetTabExtensionAppIDCommand(command, &tab_id, |
| 537 &extension_app_id)) { |
| 538 return; |
| 539 } |
| 540 current_tab->extension_app_id.swap(extension_app_id); |
| 541 break; |
| 542 } |
| 543 |
| 544 case kCommandSetTabUserAgentOverride: { |
| 545 if (!current_tab) { |
| 546 // Should be in a tab when we get this. |
| 547 return; |
| 548 } |
| 549 SessionID::id_type tab_id; |
| 550 std::string user_agent_override; |
| 551 if (!RestoreSetTabUserAgentOverrideCommand(command, &tab_id, |
| 552 &user_agent_override)) { |
| 553 return; |
| 554 } |
| 555 current_tab->user_agent_override.swap(user_agent_override); |
| 556 break; |
| 557 } |
| 558 |
| 559 default: |
| 560 // Unknown type, usually indicates corruption of file. Ignore it. |
| 561 return; |
| 562 } |
| 563 } |
| 564 |
| 565 // If there was corruption some of the entries won't be valid. |
| 566 ValidateAndDeleteEmptyEntries(&(entries.get())); |
| 567 |
| 568 loaded_entries->swap(entries.get()); |
| 569 } |
| 570 |
| 571 void PersistentTabRestoreService::CreateEntriesFromWindows( |
| 572 std::vector<SessionWindow*>* windows, |
| 573 std::vector<Entry*>* entries) { |
| 574 for (size_t i = 0; i < windows->size(); ++i) { |
| 575 scoped_ptr<Window> window(new Window()); |
| 576 if (ConvertSessionWindowToWindow((*windows)[i], window.get())) |
| 577 entries->push_back(window.release()); |
| 578 } |
| 579 } |
| 580 |
| 581 void PersistentTabRestoreService::RemoveEntryByID( |
| 582 SessionID::id_type id, |
| 583 IDToEntry* id_to_entry, |
| 584 std::vector<TabRestoreService::Entry*>* entries) { |
| 585 // Look for the entry in the map. If it is present, erase it from both |
| 586 // collections and return. |
| 587 IDToEntry::iterator i = id_to_entry->find(id); |
| 588 if (i != id_to_entry->end()) { |
| 589 entries->erase(std::find(entries->begin(), entries->end(), i->second)); |
| 590 delete i->second; |
| 591 id_to_entry->erase(i); |
| 592 return; |
| 593 } |
| 594 |
| 595 // Otherwise, loop over all items in the map and see if any of the Windows |
| 596 // have Tabs with the |id|. |
| 597 for (IDToEntry::iterator i = id_to_entry->begin(); i != id_to_entry->end(); |
| 598 ++i) { |
| 599 if (i->second->type == TabRestoreService::WINDOW) { |
| 600 TabRestoreService::Window* window = |
| 601 static_cast<TabRestoreService::Window*>(i->second); |
| 602 std::vector<TabRestoreService::Tab>::iterator j = window->tabs.begin(); |
| 603 for ( ; j != window->tabs.end(); ++j) { |
| 604 // If the ID matches one of this window's tabs, remove it from the list. |
| 605 if ((*j).id == id) { |
| 606 window->tabs.erase(j); |
| 607 return; |
| 608 } |
| 609 } |
| 610 } |
| 611 } |
| 612 } |
| 613 |
| 614 // static |
| 615 void PersistentTabRestoreService::ValidateAndDeleteEmptyEntries( |
| 616 std::vector<Entry*>* entries) { |
| 617 std::vector<Entry*> valid_entries; |
| 618 std::vector<Entry*> invalid_entries; |
| 619 |
| 620 // Iterate from the back so that we keep the most recently closed entries. |
| 621 for (std::vector<Entry*>::reverse_iterator i = entries->rbegin(); |
| 622 i != entries->rend(); ++i) { |
| 623 if (ValidateEntry(*i)) |
| 624 valid_entries.push_back(*i); |
| 625 else |
| 626 invalid_entries.push_back(*i); |
| 627 } |
| 628 // NOTE: at this point the entries are ordered with newest at the front. |
| 629 entries->swap(valid_entries); |
| 630 |
| 631 // Delete the remaining entries. |
| 632 STLDeleteElements(&invalid_entries); |
| 633 } |
| 634 |
| 635 void PersistentTabRestoreService::LoadTabsFromLastSession() { |
| 636 if (load_state_ != NOT_LOADED || entries().size() == kMaxEntries) |
| 637 return; |
| 638 |
| 639 #if !defined(ENABLE_SESSION_SERVICE) |
| 640 // If sessions are not stored in the SessionService, default to |
| 641 // |LOADED_LAST_SESSION| state. |
| 642 load_state_ = LOADING | LOADED_LAST_SESSION; |
| 643 #else |
| 644 load_state_ = LOADING; |
| 645 |
| 646 SessionService* session_service = |
| 647 SessionServiceFactory::GetForProfile(profile_); |
| 648 Profile::ExitType exit_type = profile()->GetLastSessionExitType(); |
| 649 if (!profile()->restored_last_session() && session_service && |
| 650 (exit_type == Profile::EXIT_CRASHED || |
| 651 exit_type == Profile::EXIT_SESSION_ENDED)) { |
| 652 // The previous session crashed and wasn't restored, or was a forced |
| 653 // shutdown. Both of which won't have notified us of the browser close so |
| 654 // that we need to load the windows from session service (which will have |
| 655 // saved them). |
| 656 session_service->GetLastSession( |
| 657 &crash_consumer_, |
| 658 base::Bind(&PersistentTabRestoreService::OnGotPreviousSession, |
| 659 base::Unretained(this))); |
| 660 } else { |
| 661 load_state_ |= LOADED_LAST_SESSION; |
| 662 } |
| 663 #endif |
| 664 |
| 665 // Request the tabs closed in the last session. If the last session crashed, |
| 666 // this won't contain the tabs/window that were open at the point of the |
| 667 // crash (the call to GetLastSession above requests those). |
| 668 ScheduleGetLastSessionCommands( |
| 669 new InternalGetCommandsRequest( |
| 670 base::Bind(&PersistentTabRestoreService::OnGotLastSessionCommands, |
| 671 base::Unretained(this))), |
| 672 &load_consumer_); |
| 673 } |
| 674 |
| 675 void PersistentTabRestoreService::LoadStateChanged() { |
| 676 if ((load_state_ & (LOADED_LAST_TABS | LOADED_LAST_SESSION)) != |
| 677 (LOADED_LAST_TABS | LOADED_LAST_SESSION)) { |
| 678 // Still waiting on previous session or previous tabs. |
| 679 return; |
| 680 } |
| 681 |
| 682 // We're done loading. |
| 683 load_state_ ^= LOADING; |
| 684 |
| 685 const size_t entries_size = entries().size(); |
| 686 if (staging_entries_.empty() || entries_size >= kMaxEntries) { |
| 687 STLDeleteElements(&staging_entries_); |
| 688 return; |
| 689 } |
| 690 |
| 691 if (staging_entries_.size() + entries_size > kMaxEntries) { |
| 692 // If we add all the staged entries we'll end up with more than |
| 693 // kMaxEntries. Delete entries such that we only end up with |
| 694 // at most kMaxEntries. |
| 695 int surplus = kMaxEntries - entries_size; |
| 696 CHECK_LE(0, surplus); |
| 697 CHECK_GE(static_cast<int>(staging_entries_.size()), surplus); |
| 698 STLDeleteContainerPointers( |
| 699 staging_entries_.begin() + (kMaxEntries - entries_size), |
| 700 staging_entries_.end()); |
| 701 staging_entries_.erase( |
| 702 staging_entries_.begin() + (kMaxEntries - entries_size), |
| 703 staging_entries_.end()); |
| 704 } |
| 705 |
| 706 // And add them. |
| 707 for (size_t i = 0; i < staging_entries_.size(); ++i) { |
| 708 staging_entries_[i]->from_last_session = true; |
| 709 AddEntry(staging_entries_[i], false, false); |
| 710 } |
| 711 |
| 712 // AddEntry takes ownership of the entry, need to clear out entries so that |
| 713 // it doesn't delete them. |
| 714 staging_entries_.clear(); |
| 715 |
| 716 // Make it so we rewrite all the tabs. We need to do this otherwise we won't |
| 717 // correctly write out the entries when Save is invoked (Save starts from |
| 718 // the front, not the end and we just added the entries to the end). |
| 719 entries_to_write_ = staging_entries_.size(); |
| 720 |
| 721 PruneEntries(); |
| 722 NotifyTabsChanged(); |
| 723 } |
| OLD | NEW |