OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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_commands.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/pickle.h" |
| 11 #include "chrome/browser/sessions/base_session_service_delegate.h" |
| 12 #include "chrome/browser/sessions/session_backend.h" |
| 13 #include "chrome/browser/sessions/session_command.h" |
| 14 #include "chrome/browser/sessions/session_types.h" |
| 15 #include "components/startup_metric_utils/startup_metric_utils.h" |
| 16 |
| 17 // Identifier for commands written to file. |
| 18 static const SessionCommand::id_type kCommandSetTabWindow = 0; |
| 19 // OBSOLETE Superseded by kCommandSetWindowBounds3. |
| 20 // static const SessionCommand::id_type kCommandSetWindowBounds = 1; |
| 21 static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2; |
| 22 static const SessionCommand::id_type |
| 23 kCommandTabNavigationPathPrunedFromBack = 5; |
| 24 static const SessionCommand::id_type kCommandUpdateTabNavigation = 6; |
| 25 static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7; |
| 26 static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8; |
| 27 static const SessionCommand::id_type kCommandSetWindowType = 9; |
| 28 // OBSOLETE Superseded by kCommandSetWindowBounds3. Except for data migration. |
| 29 // static const SessionCommand::id_type kCommandSetWindowBounds2 = 10; |
| 30 static const SessionCommand::id_type |
| 31 kCommandTabNavigationPathPrunedFromFront = 11; |
| 32 static const SessionCommand::id_type kCommandSetPinnedState = 12; |
| 33 static const SessionCommand::id_type kCommandSetExtensionAppID = 13; |
| 34 static const SessionCommand::id_type kCommandSetWindowBounds3 = 14; |
| 35 static const SessionCommand::id_type kCommandSetWindowAppName = 15; |
| 36 static const SessionCommand::id_type kCommandTabClosed = 16; |
| 37 static const SessionCommand::id_type kCommandWindowClosed = 17; |
| 38 static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 18; |
| 39 static const SessionCommand::id_type kCommandSessionStorageAssociated = 19; |
| 40 static const SessionCommand::id_type kCommandSetActiveWindow = 20; |
| 41 |
| 42 // Every kWritesPerReset commands triggers recreating the file. |
| 43 static const int kWritesPerReset = 250; |
| 44 |
| 45 namespace { |
| 46 |
| 47 // Various payload structures. |
| 48 struct ClosedPayload { |
| 49 SessionID::id_type id; |
| 50 int64 close_time; |
| 51 }; |
| 52 |
| 53 struct WindowBoundsPayload2 { |
| 54 SessionID::id_type window_id; |
| 55 int32 x; |
| 56 int32 y; |
| 57 int32 w; |
| 58 int32 h; |
| 59 bool is_maximized; |
| 60 }; |
| 61 |
| 62 struct WindowBoundsPayload3 { |
| 63 SessionID::id_type window_id; |
| 64 int32 x; |
| 65 int32 y; |
| 66 int32 w; |
| 67 int32 h; |
| 68 int32 show_state; |
| 69 }; |
| 70 |
| 71 typedef SessionID::id_type ActiveWindowPayload; |
| 72 |
| 73 struct IDAndIndexPayload { |
| 74 SessionID::id_type id; |
| 75 int32 index; |
| 76 }; |
| 77 |
| 78 typedef IDAndIndexPayload TabIndexInWindowPayload; |
| 79 |
| 80 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload; |
| 81 |
| 82 typedef IDAndIndexPayload SelectedNavigationIndexPayload; |
| 83 |
| 84 typedef IDAndIndexPayload SelectedTabInIndexPayload; |
| 85 |
| 86 typedef IDAndIndexPayload WindowTypePayload; |
| 87 |
| 88 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload; |
| 89 |
| 90 struct PinnedStatePayload { |
| 91 SessionID::id_type tab_id; |
| 92 bool pinned_state; |
| 93 }; |
| 94 |
| 95 // Persisted versions of ui::WindowShowState that are written to disk and can |
| 96 // never change. |
| 97 enum PersistedWindowShowState { |
| 98 // SHOW_STATE_DEFAULT (0) never persisted. |
| 99 PERSISTED_SHOW_STATE_NORMAL = 1, |
| 100 PERSISTED_SHOW_STATE_MINIMIZED = 2, |
| 101 PERSISTED_SHOW_STATE_MAXIMIZED = 3, |
| 102 // SHOW_STATE_INACTIVE (4) never persisted. |
| 103 PERSISTED_SHOW_STATE_FULLSCREEN = 5, |
| 104 PERSISTED_SHOW_STATE_DETACHED_DEPRECATED = 6, |
| 105 PERSISTED_SHOW_STATE_END = 6 |
| 106 }; |
| 107 |
| 108 // Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState |
| 109 // is changed. |
| 110 COMPILE_ASSERT(ui::SHOW_STATE_END == |
| 111 static_cast<ui::WindowShowState>(PERSISTED_SHOW_STATE_END), |
| 112 persisted_show_state_mismatch); |
| 113 |
| 114 // Returns the show state to store to disk based |state|. |
| 115 PersistedWindowShowState ShowStateToPersistedShowState( |
| 116 ui::WindowShowState state) { |
| 117 switch (state) { |
| 118 case ui::SHOW_STATE_NORMAL: |
| 119 return PERSISTED_SHOW_STATE_NORMAL; |
| 120 case ui::SHOW_STATE_MINIMIZED: |
| 121 return PERSISTED_SHOW_STATE_MINIMIZED; |
| 122 case ui::SHOW_STATE_MAXIMIZED: |
| 123 return PERSISTED_SHOW_STATE_MAXIMIZED; |
| 124 case ui::SHOW_STATE_FULLSCREEN: |
| 125 return PERSISTED_SHOW_STATE_FULLSCREEN; |
| 126 |
| 127 case ui::SHOW_STATE_DEFAULT: |
| 128 case ui::SHOW_STATE_INACTIVE: |
| 129 return PERSISTED_SHOW_STATE_NORMAL; |
| 130 |
| 131 case ui::SHOW_STATE_END: |
| 132 break; |
| 133 } |
| 134 NOTREACHED(); |
| 135 return PERSISTED_SHOW_STATE_NORMAL; |
| 136 } |
| 137 |
| 138 // Lints show state values when read back from persited disk. |
| 139 ui::WindowShowState PersistedShowStateToShowState(int state) { |
| 140 switch (state) { |
| 141 case PERSISTED_SHOW_STATE_NORMAL: |
| 142 return ui::SHOW_STATE_NORMAL; |
| 143 case PERSISTED_SHOW_STATE_MINIMIZED: |
| 144 return ui::SHOW_STATE_MINIMIZED; |
| 145 case PERSISTED_SHOW_STATE_MAXIMIZED: |
| 146 return ui::SHOW_STATE_MAXIMIZED; |
| 147 case PERSISTED_SHOW_STATE_FULLSCREEN: |
| 148 return ui::SHOW_STATE_FULLSCREEN; |
| 149 case PERSISTED_SHOW_STATE_DETACHED_DEPRECATED: |
| 150 return ui::SHOW_STATE_NORMAL; |
| 151 } |
| 152 NOTREACHED(); |
| 153 return ui::SHOW_STATE_NORMAL; |
| 154 } |
| 155 |
| 156 } // namespace |
| 157 |
| 158 // SessionService ------------------------------------------------------------- |
| 159 |
| 160 SessionServiceCommands::SessionServiceCommands( |
| 161 BaseSessionService::SessionType type, |
| 162 const base::FilePath& path, |
| 163 scoped_ptr<BaseSessionServiceDelegate> delegate) |
| 164 : BaseSessionService(type, path, delegate.Pass()), |
| 165 weak_factory_(this) { |
| 166 } |
| 167 |
| 168 SessionServiceCommands::~SessionServiceCommands() { |
| 169 // The owner is responsible to save upon exit. |
| 170 } |
| 171 |
| 172 base::CancelableTaskTracker::TaskId SessionServiceCommands::GetLastSession( |
| 173 const SessionCallback& callback, |
| 174 base::CancelableTaskTracker* tracker) { |
| 175 // OnGotSessionServiceCommands maps the SessionServiceCommands to browser stat
e, then run |
| 176 // the callback. |
| 177 return ScheduleGetLastSessionCommands( |
| 178 base::Bind(&SessionServiceCommands::OnGotSessionServiceCommands, |
| 179 weak_factory_.GetWeakPtr(), callback), |
| 180 tracker); |
| 181 } |
| 182 |
| 183 SessionCommand* SessionServiceCommands::CreateSetSelectedTabInWindow( |
| 184 const SessionID& window_id, |
| 185 int index) { |
| 186 SelectedTabInIndexPayload payload = { 0 }; |
| 187 payload.id = window_id.id(); |
| 188 payload.index = index; |
| 189 SessionCommand* command = new SessionCommand(kCommandSetSelectedTabInIndex, |
| 190 sizeof(payload)); |
| 191 memcpy(command->contents(), &payload, sizeof(payload)); |
| 192 return command; |
| 193 } |
| 194 |
| 195 SessionCommand* SessionServiceCommands::CreateSetTabWindow( |
| 196 const SessionID& window_id, |
| 197 const SessionID& tab_id) { |
| 198 SessionID::id_type payload[] = { window_id.id(), tab_id.id() }; |
| 199 SessionCommand* command = |
| 200 new SessionCommand(kCommandSetTabWindow, sizeof(payload)); |
| 201 memcpy(command->contents(), payload, sizeof(payload)); |
| 202 return command; |
| 203 } |
| 204 |
| 205 SessionCommand* SessionServiceCommands::CreateSetWindowBounds( |
| 206 const SessionID& window_id, |
| 207 const gfx::Rect& bounds, |
| 208 ui::WindowShowState show_state) { |
| 209 WindowBoundsPayload3 payload = { 0 }; |
| 210 payload.window_id = window_id.id(); |
| 211 payload.x = bounds.x(); |
| 212 payload.y = bounds.y(); |
| 213 payload.w = bounds.width(); |
| 214 payload.h = bounds.height(); |
| 215 payload.show_state = ShowStateToPersistedShowState(show_state); |
| 216 SessionCommand* command = new SessionCommand(kCommandSetWindowBounds3, |
| 217 sizeof(payload)); |
| 218 memcpy(command->contents(), &payload, sizeof(payload)); |
| 219 return command; |
| 220 } |
| 221 |
| 222 SessionCommand* SessionServiceCommands::CreateSetTabIndexInWindow( |
| 223 const SessionID& tab_id, |
| 224 int new_index) { |
| 225 TabIndexInWindowPayload payload = { 0 }; |
| 226 payload.id = tab_id.id(); |
| 227 payload.index = new_index; |
| 228 SessionCommand* command = |
| 229 new SessionCommand(kCommandSetTabIndexInWindow, sizeof(payload)); |
| 230 memcpy(command->contents(), &payload, sizeof(payload)); |
| 231 return command; |
| 232 } |
| 233 |
| 234 SessionCommand* SessionServiceCommands::CreateTabClosed( |
| 235 const SessionID::id_type tab_id) { |
| 236 ClosedPayload payload; |
| 237 // Because of what appears to be a compiler bug setting payload to {0} doesn't |
| 238 // set the padding to 0, resulting in Purify reporting an UMR when we write |
| 239 // the structure to disk. To avoid this we explicitly memset the struct. |
| 240 memset(&payload, 0, sizeof(payload)); |
| 241 payload.id = tab_id; |
| 242 payload.close_time = base::Time::Now().ToInternalValue(); |
| 243 SessionCommand* command = |
| 244 new SessionCommand(kCommandTabClosed, sizeof(payload)); |
| 245 memcpy(command->contents(), &payload, sizeof(payload)); |
| 246 return command; |
| 247 } |
| 248 |
| 249 SessionCommand* SessionServiceCommands::CreateWindowClosed( |
| 250 const SessionID::id_type window_id) { |
| 251 ClosedPayload payload; |
| 252 // See comment in CreateTabClosedCommand as to why we do this. |
| 253 memset(&payload, 0, sizeof(payload)); |
| 254 payload.id = window_id; |
| 255 payload.close_time = base::Time::Now().ToInternalValue(); |
| 256 SessionCommand* command = |
| 257 new SessionCommand(kCommandWindowClosed, sizeof(payload)); |
| 258 memcpy(command->contents(), &payload, sizeof(payload)); |
| 259 return command; |
| 260 } |
| 261 |
| 262 SessionCommand* SessionServiceCommands::CreateSetSelectedNavigationIndex( |
| 263 const SessionID& tab_id, |
| 264 int index) { |
| 265 SelectedNavigationIndexPayload payload = { 0 }; |
| 266 payload.id = tab_id.id(); |
| 267 payload.index = index; |
| 268 SessionCommand* command = new SessionCommand( |
| 269 kCommandSetSelectedNavigationIndex, sizeof(payload)); |
| 270 memcpy(command->contents(), &payload, sizeof(payload)); |
| 271 return command; |
| 272 } |
| 273 |
| 274 SessionCommand* SessionServiceCommands::CreateSetWindowType( |
| 275 const SessionID& window_id, |
| 276 SessionWindow::WindowType type) { |
| 277 WindowTypePayload payload = { 0 }; |
| 278 payload.id = window_id.id(); |
| 279 payload.index = static_cast<int32>(type); |
| 280 SessionCommand* command = new SessionCommand( |
| 281 kCommandSetWindowType, sizeof(payload)); |
| 282 memcpy(command->contents(), &payload, sizeof(payload)); |
| 283 return command; |
| 284 } |
| 285 |
| 286 SessionCommand* SessionServiceCommands::CreatePinnedState( |
| 287 const SessionID& tab_id, |
| 288 bool is_pinned) { |
| 289 PinnedStatePayload payload = { 0 }; |
| 290 payload.tab_id = tab_id.id(); |
| 291 payload.pinned_state = is_pinned; |
| 292 SessionCommand* command = |
| 293 new SessionCommand(kCommandSetPinnedState, sizeof(payload)); |
| 294 memcpy(command->contents(), &payload, sizeof(payload)); |
| 295 return command; |
| 296 } |
| 297 |
| 298 SessionCommand* SessionServiceCommands::CreateSessionStorageAssociated( |
| 299 const SessionID& tab_id, |
| 300 const std::string& session_storage_persistent_id) { |
| 301 Pickle pickle; |
| 302 pickle.WriteInt(tab_id.id()); |
| 303 pickle.WriteString(session_storage_persistent_id); |
| 304 return new SessionCommand(kCommandSessionStorageAssociated, pickle); |
| 305 } |
| 306 |
| 307 SessionCommand* SessionServiceCommands::CreateSetActiveWindow( |
| 308 const SessionID& window_id) { |
| 309 ActiveWindowPayload payload = 0; |
| 310 payload = window_id.id(); |
| 311 SessionCommand* command = |
| 312 new SessionCommand(kCommandSetActiveWindow, sizeof(payload)); |
| 313 memcpy(command->contents(), &payload, sizeof(payload)); |
| 314 return command; |
| 315 } |
| 316 |
| 317 SessionCommand* SessionServiceCommands::CreateTabNavigationPathPrunedFromBack( |
| 318 const SessionID& tab_id, |
| 319 int count) { |
| 320 TabNavigationPathPrunedFromBackPayload payload = { 0 }; |
| 321 payload.id = tab_id.id(); |
| 322 payload.index = count; |
| 323 SessionCommand* command = |
| 324 new SessionCommand(kCommandTabNavigationPathPrunedFromBack, |
| 325 sizeof(payload)); |
| 326 memcpy(command->contents(), &payload, sizeof(payload)); |
| 327 return command; |
| 328 } |
| 329 |
| 330 SessionCommand* SessionServiceCommands::CreateTabNavigationPathPrunedFromFront( |
| 331 const SessionID& tab_id, |
| 332 int count) { |
| 333 TabNavigationPathPrunedFromFrontPayload payload = { 0 }; |
| 334 payload.id = tab_id.id(); |
| 335 payload.index = count; |
| 336 SessionCommand* command = |
| 337 new SessionCommand(kCommandTabNavigationPathPrunedFromFront, |
| 338 sizeof(payload)); |
| 339 memcpy(command->contents(), &payload, sizeof(payload)); |
| 340 return command; |
| 341 } |
| 342 |
| 343 SessionCommand* SessionServiceCommands::CreateUpdateTabNavigation( |
| 344 const SessionID& tab_id, |
| 345 const sessions::SerializedNavigationEntry& navigation) { |
| 346 // TODO(skuhne): Check why this command needs to be called from the base. |
| 347 // thus: could eliminate the use of that class here. |
| 348 return CreateUpdateTabNavigationCommand( |
| 349 kCommandUpdateTabNavigation, |
| 350 tab_id.id(), |
| 351 navigation); |
| 352 } |
| 353 |
| 354 SessionCommand* SessionServiceCommands::CreateSetTabExtensionAppID( |
| 355 const SessionID& tab_id, |
| 356 const std::string& extension_id) { |
| 357 return CreateSetTabExtensionAppIDCommand( |
| 358 kCommandSetExtensionAppID, |
| 359 tab_id.id(), |
| 360 extension_id); |
| 361 } |
| 362 |
| 363 SessionCommand* SessionServiceCommands::CreateSetTabUserAgentOverride( |
| 364 const SessionID& tab_id, |
| 365 const std::string& user_agent_override) { |
| 366 return CreateSetTabUserAgentOverrideCommand( |
| 367 kCommandSetTabUserAgentOverride, |
| 368 tab_id.id(), |
| 369 user_agent_override); |
| 370 } |
| 371 |
| 372 SessionCommand* SessionServiceCommands::CreateSetWindowAppName( |
| 373 const SessionID& window_id, |
| 374 const std::string& app_name) { |
| 375 return CreateSetWindowAppNameCommand( |
| 376 kCommandSetWindowAppName, |
| 377 window_id.id(), |
| 378 app_name); |
| 379 } |
| 380 |
| 381 void SessionServiceCommands::ScheduleCommand(SessionCommand* command) { |
| 382 DCHECK(command); |
| 383 if (ReplacePendingCommand(command)) |
| 384 return; |
| 385 BaseSessionService::ScheduleCommand(command); |
| 386 // Don't schedule a reset on tab closed/window closed. Otherwise we may |
| 387 // lose tabs/windows we want to restore from if we exit right after this. |
| 388 if (commands_since_reset() >= kWritesPerReset && |
| 389 (command->id() != kCommandTabClosed && |
| 390 command->id() != kCommandWindowClosed)) { |
| 391 ScheduleResetCommands(); |
| 392 } |
| 393 } |
| 394 |
| 395 void SessionServiceCommands::OnGotSessionServiceCommands( |
| 396 const SessionCallback& callback, |
| 397 ScopedVector<SessionCommand> commands) { |
| 398 ScopedVector<SessionWindow> valid_windows; |
| 399 SessionID::id_type active_window_id = 0; |
| 400 |
| 401 RestoreSessionFromCommands( |
| 402 commands.get(), &valid_windows.get(), &active_window_id); |
| 403 callback.Run(valid_windows.Pass(), active_window_id); |
| 404 } |
| 405 |
| 406 void SessionServiceCommands::RestoreSessionFromCommands( |
| 407 const std::vector<SessionCommand*>& commands, |
| 408 std::vector<SessionWindow*>* valid_windows, |
| 409 SessionID::id_type* active_window_id) { |
| 410 std::map<int, SessionTab*> tabs; |
| 411 std::map<int, SessionWindow*> windows; |
| 412 |
| 413 VLOG(1) << "RestoreSessionFromCommands " << commands.size(); |
| 414 if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) { |
| 415 AddTabsToWindows(&tabs, &windows); |
| 416 SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows); |
| 417 UpdateSelectedTabIndex(valid_windows); |
| 418 } |
| 419 STLDeleteValues(&tabs); |
| 420 // Don't delete contents of windows, that is done by the caller as all |
| 421 // valid windows are added to valid_windows. |
| 422 } |
| 423 |
| 424 void SessionServiceCommands::UpdateSelectedTabIndex( |
| 425 std::vector<SessionWindow*>* windows) { |
| 426 for (std::vector<SessionWindow*>::const_iterator i = windows->begin(); |
| 427 i != windows->end(); ++i) { |
| 428 // See note in SessionWindow as to why we do this. |
| 429 int new_index = 0; |
| 430 for (std::vector<SessionTab*>::const_iterator j = (*i)->tabs.begin(); |
| 431 j != (*i)->tabs.end(); ++j) { |
| 432 if ((*j)->tab_visual_index == (*i)->selected_tab_index) { |
| 433 new_index = static_cast<int>(j - (*i)->tabs.begin()); |
| 434 break; |
| 435 } |
| 436 } |
| 437 (*i)->selected_tab_index = new_index; |
| 438 } |
| 439 } |
| 440 |
| 441 SessionWindow* SessionServiceCommands::GetWindow(SessionID::id_type window_id, |
| 442 IdToSessionWindow* windows) { |
| 443 std::map<int, SessionWindow*>::iterator i = windows->find(window_id); |
| 444 if (i == windows->end()) { |
| 445 SessionWindow* window = new SessionWindow(); |
| 446 window->window_id.set_id(window_id); |
| 447 (*windows)[window_id] = window; |
| 448 return window; |
| 449 } |
| 450 return i->second; |
| 451 } |
| 452 |
| 453 SessionTab* SessionServiceCommands::GetTab(SessionID::id_type tab_id, |
| 454 IdToSessionTab* tabs) { |
| 455 DCHECK(tabs); |
| 456 std::map<int, SessionTab*>::iterator i = tabs->find(tab_id); |
| 457 if (i == tabs->end()) { |
| 458 SessionTab* tab = new SessionTab(); |
| 459 tab->tab_id.set_id(tab_id); |
| 460 (*tabs)[tab_id] = tab; |
| 461 return tab; |
| 462 } |
| 463 return i->second; |
| 464 } |
| 465 |
| 466 std::vector<sessions::SerializedNavigationEntry>::iterator |
| 467 SessionServiceCommands::FindClosestNavigationWithIndex( |
| 468 std::vector<sessions::SerializedNavigationEntry>* navigations, |
| 469 int index) { |
| 470 DCHECK(navigations); |
| 471 for (std::vector<sessions::SerializedNavigationEntry>::iterator |
| 472 i = navigations->begin(); i != navigations->end(); ++i) { |
| 473 if (i->index() >= index) |
| 474 return i; |
| 475 } |
| 476 return navigations->end(); |
| 477 } |
| 478 |
| 479 // Function used in sorting windows. Sorting is done based on window id. As |
| 480 // window ids increment for each new window, this effectively sorts by creation |
| 481 // time. |
| 482 static bool WindowOrderSortFunction(const SessionWindow* w1, |
| 483 const SessionWindow* w2) { |
| 484 return w1->window_id.id() < w2->window_id.id(); |
| 485 } |
| 486 |
| 487 // Compares the two tabs based on visual index. |
| 488 static bool TabVisualIndexSortFunction(const SessionTab* t1, |
| 489 const SessionTab* t2) { |
| 490 const int delta = t1->tab_visual_index - t2->tab_visual_index; |
| 491 return delta == 0 ? (t1->tab_id.id() < t2->tab_id.id()) : (delta < 0); |
| 492 } |
| 493 |
| 494 void SessionServiceCommands::SortTabsBasedOnVisualOrderAndPrune( |
| 495 std::map<int, SessionWindow*>* windows, |
| 496 std::vector<SessionWindow*>* valid_windows) { |
| 497 std::map<int, SessionWindow*>::iterator i = windows->begin(); |
| 498 while (i != windows->end()) { |
| 499 SessionWindow* window = i->second; |
| 500 AppType app_type = window->app_name.empty() ? TYPE_NORMAL : TYPE_APP; |
| 501 if (window->tabs.empty() || window->is_constrained || |
| 502 !ShouldTrackChangesOfWindowType(window->type, app_type)) { |
| 503 delete window; |
| 504 windows->erase(i++); |
| 505 } else { |
| 506 // Valid window; sort the tabs and add it to the list of valid windows. |
| 507 std::sort(window->tabs.begin(), window->tabs.end(), |
| 508 &TabVisualIndexSortFunction); |
| 509 // Otherwise, add the window such that older windows appear first. |
| 510 if (valid_windows->empty()) { |
| 511 valid_windows->push_back(window); |
| 512 } else { |
| 513 valid_windows->insert( |
| 514 std::upper_bound(valid_windows->begin(), valid_windows->end(), |
| 515 window, &WindowOrderSortFunction), |
| 516 window); |
| 517 } |
| 518 ++i; |
| 519 } |
| 520 } |
| 521 } |
| 522 |
| 523 void SessionServiceCommands::AddTabsToWindows(std::map<int, SessionTab*>* tabs, |
| 524 std::map<int, SessionWindow*>* windows) { |
| 525 VLOG(1) << "AddTabsToWindws"; |
| 526 VLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size(); |
| 527 std::map<int, SessionTab*>::iterator i = tabs->begin(); |
| 528 while (i != tabs->end()) { |
| 529 SessionTab* tab = i->second; |
| 530 if (tab->window_id.id() && !tab->navigations.empty()) { |
| 531 SessionWindow* window = GetWindow(tab->window_id.id(), windows); |
| 532 window->tabs.push_back(tab); |
| 533 tabs->erase(i++); |
| 534 |
| 535 // See note in SessionTab as to why we do this. |
| 536 std::vector<sessions::SerializedNavigationEntry>::iterator j = |
| 537 FindClosestNavigationWithIndex(&(tab->navigations), |
| 538 tab->current_navigation_index); |
| 539 if (j == tab->navigations.end()) { |
| 540 tab->current_navigation_index = |
| 541 static_cast<int>(tab->navigations.size() - 1); |
| 542 } else { |
| 543 tab->current_navigation_index = |
| 544 static_cast<int>(j - tab->navigations.begin()); |
| 545 } |
| 546 } else { |
| 547 // Never got a set tab index in window, or tabs are empty, nothing |
| 548 // to do. |
| 549 ++i; |
| 550 } |
| 551 } |
| 552 } |
| 553 |
| 554 bool SessionServiceCommands::CreateTabsAndWindows( |
| 555 const std::vector<SessionCommand*>& data, |
| 556 std::map<int, SessionTab*>* tabs, |
| 557 std::map<int, SessionWindow*>* windows, |
| 558 SessionID::id_type* active_window_id) { |
| 559 // If the file is corrupt (command with wrong size, or unknown command), we |
| 560 // still return true and attempt to restore what we we can. |
| 561 VLOG(1) << "CreateTabsAndWindows"; |
| 562 |
| 563 startup_metric_utils::ScopedSlowStartupUMA |
| 564 scoped_timer("Startup.SlowStartupSessionServiceCreateTabsAndWindows"); |
| 565 |
| 566 for (std::vector<SessionCommand*>::const_iterator i = data.begin(); |
| 567 i != data.end(); ++i) { |
| 568 const SessionCommand::id_type kCommandSetWindowBounds2 = 10; |
| 569 const SessionCommand* command = *i; |
| 570 |
| 571 VLOG(1) << "Read command " << (int) command->id(); |
| 572 switch (command->id()) { |
| 573 case kCommandSetTabWindow: { |
| 574 SessionID::id_type payload[2]; |
| 575 if (!command->GetPayload(payload, sizeof(payload))) { |
| 576 VLOG(1) << "Failed reading command " << command->id(); |
| 577 return true; |
| 578 } |
| 579 GetTab(payload[1], tabs)->window_id.set_id(payload[0]); |
| 580 break; |
| 581 } |
| 582 |
| 583 // This is here for forward migration only. New data is saved with |
| 584 // |kCommandSetWindowBounds3|. |
| 585 case kCommandSetWindowBounds2: { |
| 586 WindowBoundsPayload2 payload; |
| 587 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 588 VLOG(1) << "Failed reading command " << command->id(); |
| 589 return true; |
| 590 } |
| 591 GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x, |
| 592 payload.y, |
| 593 payload.w, |
| 594 payload.h); |
| 595 GetWindow(payload.window_id, windows)->show_state = |
| 596 payload.is_maximized ? |
| 597 ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL; |
| 598 break; |
| 599 } |
| 600 |
| 601 case kCommandSetWindowBounds3: { |
| 602 WindowBoundsPayload3 payload; |
| 603 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 604 VLOG(1) << "Failed reading command " << command->id(); |
| 605 return true; |
| 606 } |
| 607 GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x, |
| 608 payload.y, |
| 609 payload.w, |
| 610 payload.h); |
| 611 GetWindow(payload.window_id, windows)->show_state = |
| 612 PersistedShowStateToShowState(payload.show_state); |
| 613 break; |
| 614 } |
| 615 |
| 616 case kCommandSetTabIndexInWindow: { |
| 617 TabIndexInWindowPayload payload; |
| 618 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 619 VLOG(1) << "Failed reading command " << command->id(); |
| 620 return true; |
| 621 } |
| 622 GetTab(payload.id, tabs)->tab_visual_index = payload.index; |
| 623 break; |
| 624 } |
| 625 |
| 626 case kCommandTabClosed: |
| 627 case kCommandWindowClosed: { |
| 628 ClosedPayload payload; |
| 629 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 630 VLOG(1) << "Failed reading command " << command->id(); |
| 631 return true; |
| 632 } |
| 633 if (command->id() == kCommandTabClosed) { |
| 634 delete GetTab(payload.id, tabs); |
| 635 tabs->erase(payload.id); |
| 636 } else { |
| 637 delete GetWindow(payload.id, windows); |
| 638 windows->erase(payload.id); |
| 639 } |
| 640 break; |
| 641 } |
| 642 |
| 643 case kCommandTabNavigationPathPrunedFromBack: { |
| 644 TabNavigationPathPrunedFromBackPayload payload; |
| 645 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 646 VLOG(1) << "Failed reading command " << command->id(); |
| 647 return true; |
| 648 } |
| 649 SessionTab* tab = GetTab(payload.id, tabs); |
| 650 tab->navigations.erase( |
| 651 FindClosestNavigationWithIndex(&(tab->navigations), payload.index), |
| 652 tab->navigations.end()); |
| 653 break; |
| 654 } |
| 655 |
| 656 case kCommandTabNavigationPathPrunedFromFront: { |
| 657 TabNavigationPathPrunedFromFrontPayload payload; |
| 658 if (!command->GetPayload(&payload, sizeof(payload)) || |
| 659 payload.index <= 0) { |
| 660 VLOG(1) << "Failed reading command " << command->id(); |
| 661 return true; |
| 662 } |
| 663 SessionTab* tab = GetTab(payload.id, tabs); |
| 664 |
| 665 // Update the selected navigation index. |
| 666 tab->current_navigation_index = |
| 667 std::max(-1, tab->current_navigation_index - payload.index); |
| 668 |
| 669 // And update the index of existing navigations. |
| 670 for (std::vector<sessions::SerializedNavigationEntry>::iterator |
| 671 i = tab->navigations.begin(); |
| 672 i != tab->navigations.end();) { |
| 673 i->set_index(i->index() - payload.index); |
| 674 if (i->index() < 0) |
| 675 i = tab->navigations.erase(i); |
| 676 else |
| 677 ++i; |
| 678 } |
| 679 break; |
| 680 } |
| 681 |
| 682 case kCommandUpdateTabNavigation: { |
| 683 sessions::SerializedNavigationEntry navigation; |
| 684 SessionID::id_type tab_id; |
| 685 if (!RestoreUpdateTabNavigationCommand( |
| 686 *command, &navigation, &tab_id)) { |
| 687 VLOG(1) << "Failed reading command " << command->id(); |
| 688 return true; |
| 689 } |
| 690 SessionTab* tab = GetTab(tab_id, tabs); |
| 691 std::vector<sessions::SerializedNavigationEntry>::iterator i = |
| 692 FindClosestNavigationWithIndex(&(tab->navigations), |
| 693 navigation.index()); |
| 694 if (i != tab->navigations.end() && i->index() == navigation.index()) |
| 695 *i = navigation; |
| 696 else |
| 697 tab->navigations.insert(i, navigation); |
| 698 break; |
| 699 } |
| 700 |
| 701 case kCommandSetSelectedNavigationIndex: { |
| 702 SelectedNavigationIndexPayload payload; |
| 703 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 704 VLOG(1) << "Failed reading command " << command->id(); |
| 705 return true; |
| 706 } |
| 707 GetTab(payload.id, tabs)->current_navigation_index = payload.index; |
| 708 break; |
| 709 } |
| 710 |
| 711 case kCommandSetSelectedTabInIndex: { |
| 712 SelectedTabInIndexPayload payload; |
| 713 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 714 VLOG(1) << "Failed reading command " << command->id(); |
| 715 return true; |
| 716 } |
| 717 GetWindow(payload.id, windows)->selected_tab_index = payload.index; |
| 718 break; |
| 719 } |
| 720 |
| 721 case kCommandSetWindowType: { |
| 722 WindowTypePayload payload; |
| 723 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 724 VLOG(1) << "Failed reading command " << command->id(); |
| 725 return true; |
| 726 } |
| 727 GetWindow(payload.id, windows)->is_constrained = false; |
| 728 GetWindow(payload.id, windows)->type = |
| 729 static_cast<SessionWindow::WindowType>(payload.index); |
| 730 break; |
| 731 } |
| 732 |
| 733 case kCommandSetPinnedState: { |
| 734 PinnedStatePayload payload; |
| 735 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 736 VLOG(1) << "Failed reading command " << command->id(); |
| 737 return true; |
| 738 } |
| 739 GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state; |
| 740 break; |
| 741 } |
| 742 |
| 743 case kCommandSetWindowAppName: { |
| 744 SessionID::id_type window_id; |
| 745 std::string app_name; |
| 746 if (!RestoreSetWindowAppNameCommand(*command, &window_id, &app_name)) |
| 747 return true; |
| 748 |
| 749 GetWindow(window_id, windows)->app_name.swap(app_name); |
| 750 break; |
| 751 } |
| 752 |
| 753 case kCommandSetExtensionAppID: { |
| 754 SessionID::id_type tab_id; |
| 755 std::string extension_app_id; |
| 756 if (!RestoreSetTabExtensionAppIDCommand( |
| 757 *command, &tab_id, &extension_app_id)) { |
| 758 VLOG(1) << "Failed reading command " << command->id(); |
| 759 return true; |
| 760 } |
| 761 |
| 762 GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id); |
| 763 break; |
| 764 } |
| 765 |
| 766 case kCommandSetTabUserAgentOverride: { |
| 767 SessionID::id_type tab_id; |
| 768 std::string user_agent_override; |
| 769 if (!RestoreSetTabUserAgentOverrideCommand( |
| 770 *command, &tab_id, &user_agent_override)) { |
| 771 return true; |
| 772 } |
| 773 |
| 774 GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override); |
| 775 break; |
| 776 } |
| 777 |
| 778 case kCommandSessionStorageAssociated: { |
| 779 scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle()); |
| 780 SessionID::id_type command_tab_id; |
| 781 std::string session_storage_persistent_id; |
| 782 PickleIterator iter(*command_pickle.get()); |
| 783 if (!command_pickle->ReadInt(&iter, &command_tab_id) || |
| 784 !command_pickle->ReadString(&iter, &session_storage_persistent_id)) |
| 785 return true; |
| 786 // Associate the session storage back. |
| 787 GetTab(command_tab_id, tabs)->session_storage_persistent_id = |
| 788 session_storage_persistent_id; |
| 789 break; |
| 790 } |
| 791 |
| 792 case kCommandSetActiveWindow: { |
| 793 ActiveWindowPayload payload; |
| 794 if (!command->GetPayload(&payload, sizeof(payload))) { |
| 795 VLOG(1) << "Failed reading command " << command->id(); |
| 796 return true; |
| 797 } |
| 798 *active_window_id = payload; |
| 799 break; |
| 800 } |
| 801 |
| 802 default: |
| 803 // TODO(skuhne): This might call back into a callback handler to extend |
| 804 // the command set for specific implementations. |
| 805 VLOG(1) << "Failed reading an unknown command " << command->id(); |
| 806 return true; |
| 807 } |
| 808 } |
| 809 return true; |
| 810 } |
| 811 |
| 812 bool SessionServiceCommands::ReplacePendingCommand(SessionCommand* command) { |
| 813 // We optimize page navigations, which can happen quite frequently and |
| 814 // are expensive. And activation is like Highlander, there can only be one! |
| 815 if (command->id() != kCommandUpdateTabNavigation && |
| 816 command->id() != kCommandSetActiveWindow) { |
| 817 return false; |
| 818 } |
| 819 for (std::vector<SessionCommand*>::reverse_iterator i = |
| 820 pending_commands().rbegin(); i != pending_commands().rend(); ++i) { |
| 821 SessionCommand* existing_command = *i; |
| 822 if (command->id() == kCommandUpdateTabNavigation && |
| 823 existing_command->id() == kCommandUpdateTabNavigation) { |
| 824 scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle()); |
| 825 PickleIterator iterator(*command_pickle); |
| 826 SessionID::id_type command_tab_id; |
| 827 int command_nav_index; |
| 828 if (!command_pickle->ReadInt(&iterator, &command_tab_id) || |
| 829 !command_pickle->ReadInt(&iterator, &command_nav_index)) { |
| 830 return false; |
| 831 } |
| 832 SessionID::id_type existing_tab_id; |
| 833 int existing_nav_index; |
| 834 { |
| 835 // Creating a pickle like this means the Pickle references the data from |
| 836 // the command. Make sure we delete the pickle before the command, else |
| 837 // the pickle references deleted memory. |
| 838 scoped_ptr<Pickle> existing_pickle(existing_command->PayloadAsPickle()); |
| 839 iterator = PickleIterator(*existing_pickle); |
| 840 if (!existing_pickle->ReadInt(&iterator, &existing_tab_id) || |
| 841 !existing_pickle->ReadInt(&iterator, &existing_nav_index)) { |
| 842 return false; |
| 843 } |
| 844 } |
| 845 if (existing_tab_id == command_tab_id && |
| 846 existing_nav_index == command_nav_index) { |
| 847 // existing_command is an update for the same tab/index pair. Replace |
| 848 // it with the new one. We need to add to the end of the list just in |
| 849 // case there is a prune command after the update command. |
| 850 delete existing_command; |
| 851 pending_commands().erase(i.base() - 1); |
| 852 pending_commands().push_back(command); |
| 853 return true; |
| 854 } |
| 855 return false; |
| 856 } |
| 857 if (command->id() == kCommandSetActiveWindow && |
| 858 existing_command->id() == kCommandSetActiveWindow) { |
| 859 *i = command; |
| 860 delete existing_command; |
| 861 return true; |
| 862 } |
| 863 } |
| 864 return false; |
| 865 } |
OLD | NEW |