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