Distiller feedback always in front of body element
[chromium-blink-merge.git] / components / sessions / session_service_commands.cc
bloba7f653d5aa82cf25fedbb9bb92c0249773021f23
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.
5 #include "components/sessions/session_service_commands.h"
7 #include <vector>
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"
15 namespace sessions {
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;
43 namespace {
45 // Various payload structures.
46 struct ClosedPayload {
47 SessionID::id_type id;
48 int64 close_time;
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;
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;
69 typedef SessionID::id_type ActiveWindowPayload;
71 struct IDAndIndexPayload {
72 SessionID::id_type id;
73 int32 index;
76 typedef IDAndIndexPayload TabIndexInWindowPayload;
78 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload;
80 typedef IDAndIndexPayload SelectedNavigationIndexPayload;
82 typedef IDAndIndexPayload SelectedTabInIndexPayload;
84 typedef IDAndIndexPayload WindowTypePayload;
86 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
88 struct PinnedStatePayload {
89 SessionID::id_type tab_id;
90 bool pinned_state;
93 struct LastActiveTimePayload {
94 SessionID::id_type tab_id;
95 int64 last_active_time;
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
112 typedef std::map<SessionID::id_type, SessionTab*> IdToSessionTab;
113 typedef std::map<SessionID::id_type, SessionWindow*> IdToSessionWindow;
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");
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;
136 case ui::SHOW_STATE_DEFAULT:
137 case ui::SHOW_STATE_INACTIVE:
138 return PERSISTED_SHOW_STATE_NORMAL;
140 case ui::SHOW_STATE_END:
141 break;
143 NOTREACHED();
144 return PERSISTED_SHOW_STATE_NORMAL;
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;
163 NOTREACHED();
164 return ui::SHOW_STATE_NORMAL;
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;
181 (*i)->selected_tab_index = new_index;
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;
196 return i->second;
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;
210 return i->second;
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.
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;
228 return navigations->end();
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();
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);
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);
274 ++i;
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++);
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());
304 } else {
305 // Never got a set tab index in window, or tabs are empty, nothing
306 // to do.
307 ++i;
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|.
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";
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;
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;
340 GetTab(payload[1], tabs)->window_id.set_id(payload[0]);
341 break;
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;
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;
362 case kCommandSetWindowBounds3: {
363 WindowBoundsPayload3 payload;
364 if (!command->GetPayload(&payload, sizeof(payload))) {
365 DVLOG(1) << "Failed reading command " << command->id();
366 return true;
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;
377 case kCommandSetTabIndexInWindow: {
378 TabIndexInWindowPayload payload;
379 if (!command->GetPayload(&payload, sizeof(payload))) {
380 DVLOG(1) << "Failed reading command " << command->id();
381 return true;
383 GetTab(payload.id, tabs)->tab_visual_index = payload.index;
384 break;
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;
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);
401 break;
404 case kCommandTabNavigationPathPrunedFromBack: {
405 TabNavigationPathPrunedFromBackPayload payload;
406 if (!command->GetPayload(&payload, sizeof(payload))) {
407 DVLOG(1) << "Failed reading command " << command->id();
408 return true;
410 SessionTab* tab = GetTab(payload.id, tabs);
411 tab->navigations.erase(
412 FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
413 tab->navigations.end());
414 break;
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;
424 SessionTab* tab = GetTab(payload.id, tabs);
426 // Update the selected navigation index.
427 tab->current_navigation_index =
428 std::max(-1, tab->current_navigation_index - payload.index);
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;
440 break;
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;
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;
463 case kCommandSetSelectedNavigationIndex: {
464 SelectedNavigationIndexPayload payload;
465 if (!command->GetPayload(&payload, sizeof(payload))) {
466 DVLOG(1) << "Failed reading command " << command->id();
467 return true;
469 GetTab(payload.id, tabs)->current_navigation_index = payload.index;
470 break;
473 case kCommandSetSelectedTabInIndex: {
474 SelectedTabInIndexPayload payload;
475 if (!command->GetPayload(&payload, sizeof(payload))) {
476 DVLOG(1) << "Failed reading command " << command->id();
477 return true;
479 GetWindow(payload.id, windows)->selected_tab_index = payload.index;
480 break;
483 case kCommandSetWindowType: {
484 WindowTypePayload payload;
485 if (!command->GetPayload(&payload, sizeof(payload))) {
486 DVLOG(1) << "Failed reading command " << command->id();
487 return true;
489 GetWindow(payload.id, windows)->is_constrained = false;
490 GetWindow(payload.id, windows)->type =
491 static_cast<SessionWindow::WindowType>(payload.index);
492 break;
495 case kCommandSetPinnedState: {
496 PinnedStatePayload payload;
497 if (!command->GetPayload(&payload, sizeof(payload))) {
498 DVLOG(1) << "Failed reading command " << command->id();
499 return true;
501 GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state;
502 break;
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;
511 GetWindow(window_id, windows)->app_name.swap(app_name);
512 break;
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;
525 GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
526 break;
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;
539 GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override);
540 break;
543 case kCommandSessionStorageAssociated: {
544 scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
545 SessionID::id_type command_tab_id;
546 std::string session_storage_persistent_id;
547 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;
557 case kCommandSetActiveWindow: {
558 ActiveWindowPayload payload;
559 if (!command->GetPayload(&payload, sizeof(payload))) {
560 DVLOG(1) << "Failed reading command " << command->id();
561 return true;
563 *active_window_id = payload;
564 break;
567 case kCommandLastActiveTime: {
568 LastActiveTimePayload payload;
569 if (!command->GetPayload(&payload, sizeof(payload))) {
570 DVLOG(1) << "Failed reading command " << command->id();
571 return true;
573 SessionTab* tab = GetTab(payload.tab_id, tabs);
574 tab->last_active_time =
575 base::TimeTicks::FromInternalValue(payload.last_active_time);
576 break;
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;
586 return true;
589 } // namespace
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;
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;
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;
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;
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;
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;
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;
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;
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;
705 scoped_ptr<SessionCommand> CreateSessionStorageAssociatedCommand(
706 const SessionID& tab_id,
707 const std::string& session_storage_persistent_id) {
708 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));
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;
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;
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;
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;
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);
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);
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);
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);
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;
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<Pickle> command_pickle((*command)->PayloadAsPickle());
810 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;
817 SessionID::id_type existing_tab_id;
818 int existing_nav_index;
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<Pickle> existing_pickle(existing_command->PayloadAsPickle());
824 iterator = PickleIterator(*existing_pickle);
825 if (!iterator.ReadInt(&existing_tab_id) ||
826 !iterator.ReadInt(&existing_nav_index)) {
827 return false;
830 if (existing_tab_id == command_tab_id &&
831 existing_nav_index == command_nav_index) {
832 // existing_command is an update for the same tab/index pair. Replace
833 // it with the new one. We need to add to the end of the list just in
834 // case there is a prune command after the update command.
835 base_session_service->EraseCommand(*(i.base() - 1));
836 base_session_service->AppendRebuildCommand((*command).Pass());
837 return true;
839 return false;
841 if ((*command)->id() == kCommandSetActiveWindow &&
842 existing_command->id() == kCommandSetActiveWindow) {
843 base_session_service->SwapCommand(existing_command, (*command).Pass());
844 return true;
847 return false;
850 bool IsClosingCommand(SessionCommand* command) {
851 return command->id() == kCommandTabClosed ||
852 command->id() == kCommandWindowClosed;
855 void RestoreSessionFromCommands(const ScopedVector<SessionCommand>& commands,
856 std::vector<SessionWindow*>* valid_windows,
857 SessionID::id_type* active_window_id) {
858 std::map<int, SessionTab*> tabs;
859 std::map<int, SessionWindow*> windows;
861 DVLOG(1) << "RestoreSessionFromCommands " << commands.size();
862 if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
863 AddTabsToWindows(&tabs, &windows);
864 SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows);
865 UpdateSelectedTabIndex(valid_windows);
867 STLDeleteValues(&tabs);
868 // Don't delete contents of windows, that is done by the caller as all
869 // valid windows are added to valid_windows.
872 } // namespace sessions