[MacViews] Implement size constraints for app windows.
[chromium-blink-merge.git] / components / sessions / session_service_commands.cc
blob3b5f8a14ca5fc93bbdc46a9e2f1ab401270e6a25
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;
42 namespace {
44 // Various payload structures.
45 struct ClosedPayload {
46 SessionID::id_type id;
47 int64 close_time;
50 struct WindowBoundsPayload2 {
51 SessionID::id_type window_id;
52 int32 x;
53 int32 y;
54 int32 w;
55 int32 h;
56 bool is_maximized;
59 struct WindowBoundsPayload3 {
60 SessionID::id_type window_id;
61 int32 x;
62 int32 y;
63 int32 w;
64 int32 h;
65 int32 show_state;
68 typedef SessionID::id_type ActiveWindowPayload;
70 struct IDAndIndexPayload {
71 SessionID::id_type id;
72 int32 index;
75 typedef IDAndIndexPayload TabIndexInWindowPayload;
77 typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload;
79 typedef IDAndIndexPayload SelectedNavigationIndexPayload;
81 typedef IDAndIndexPayload SelectedTabInIndexPayload;
83 typedef IDAndIndexPayload WindowTypePayload;
85 typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload;
87 struct PinnedStatePayload {
88 SessionID::id_type tab_id;
89 bool pinned_state;
92 // Persisted versions of ui::WindowShowState that are written to disk and can
93 // never change.
94 enum PersistedWindowShowState {
95 // SHOW_STATE_DEFAULT (0) never persisted.
96 PERSISTED_SHOW_STATE_NORMAL = 1,
97 PERSISTED_SHOW_STATE_MINIMIZED = 2,
98 PERSISTED_SHOW_STATE_MAXIMIZED = 3,
99 // SHOW_STATE_INACTIVE (4) never persisted.
100 PERSISTED_SHOW_STATE_FULLSCREEN = 5,
101 PERSISTED_SHOW_STATE_DETACHED_DEPRECATED = 6,
102 PERSISTED_SHOW_STATE_END = 6
105 typedef std::map<SessionID::id_type, SessionTab*> IdToSessionTab;
106 typedef std::map<SessionID::id_type, SessionWindow*> IdToSessionWindow;
108 // Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState
109 // is changed.
110 static_assert(ui::SHOW_STATE_END ==
111 static_cast<ui::WindowShowState>(PERSISTED_SHOW_STATE_END),
112 "SHOW_STATE_END must equal PERSISTED_SHOW_STATE_END");
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;
127 case ui::SHOW_STATE_DEFAULT:
128 case ui::SHOW_STATE_INACTIVE:
129 return PERSISTED_SHOW_STATE_NORMAL;
131 case ui::SHOW_STATE_END:
132 break;
134 NOTREACHED();
135 return PERSISTED_SHOW_STATE_NORMAL;
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;
152 NOTREACHED();
153 return ui::SHOW_STATE_NORMAL;
156 // Iterates through the vector updating the selected_tab_index of each
157 // SessionWindow based on the actual tabs that were restored.
158 void UpdateSelectedTabIndex(std::vector<SessionWindow*>* windows) {
159 for (std::vector<SessionWindow*>::const_iterator i = windows->begin();
160 i != windows->end(); ++i) {
161 // See note in SessionWindow as to why we do this.
162 int new_index = 0;
163 for (std::vector<SessionTab*>::const_iterator j = (*i)->tabs.begin();
164 j != (*i)->tabs.end(); ++j) {
165 if ((*j)->tab_visual_index == (*i)->selected_tab_index) {
166 new_index = static_cast<int>(j - (*i)->tabs.begin());
167 break;
170 (*i)->selected_tab_index = new_index;
174 // Returns the window in windows with the specified id. If a window does
175 // not exist, one is created.
176 SessionWindow* GetWindow(SessionID::id_type window_id,
177 IdToSessionWindow* windows) {
178 std::map<int, SessionWindow*>::iterator i = windows->find(window_id);
179 if (i == windows->end()) {
180 SessionWindow* window = new SessionWindow();
181 window->window_id.set_id(window_id);
182 (*windows)[window_id] = window;
183 return window;
185 return i->second;
188 // Returns the tab with the specified id in tabs. If a tab does not exist,
189 // it is created.
190 SessionTab* GetTab(SessionID::id_type tab_id, IdToSessionTab* tabs) {
191 DCHECK(tabs);
192 std::map<int, SessionTab*>::iterator i = tabs->find(tab_id);
193 if (i == tabs->end()) {
194 SessionTab* tab = new SessionTab();
195 tab->tab_id.set_id(tab_id);
196 (*tabs)[tab_id] = tab;
197 return tab;
199 return i->second;
202 // Returns an iterator into navigations pointing to the navigation whose
203 // index matches |index|. If no navigation index matches |index|, the first
204 // navigation with an index > |index| is returned.
206 // This assumes the navigations are ordered by index in ascending order.
207 std::vector<sessions::SerializedNavigationEntry>::iterator
208 FindClosestNavigationWithIndex(
209 std::vector<sessions::SerializedNavigationEntry>* navigations,
210 int index) {
211 DCHECK(navigations);
212 for (std::vector<sessions::SerializedNavigationEntry>::iterator
213 i = navigations->begin(); i != navigations->end(); ++i) {
214 if (i->index() >= index)
215 return i;
217 return navigations->end();
220 // Function used in sorting windows. Sorting is done based on window id. As
221 // window ids increment for each new window, this effectively sorts by creation
222 // time.
223 static bool WindowOrderSortFunction(const SessionWindow* w1,
224 const SessionWindow* w2) {
225 return w1->window_id.id() < w2->window_id.id();
228 // Compares the two tabs based on visual index.
229 static bool TabVisualIndexSortFunction(const SessionTab* t1,
230 const SessionTab* t2) {
231 const int delta = t1->tab_visual_index - t2->tab_visual_index;
232 return delta == 0 ? (t1->tab_id.id() < t2->tab_id.id()) : (delta < 0);
235 // Does the following:
236 // . Deletes and removes any windows with no tabs. NOTE: constrained windows
237 // that have been dragged out are of type browser. As such, this preserves any
238 // dragged out constrained windows (aka popups that have been dragged out).
239 // . Sorts the tabs in windows with valid tabs based on the tabs
240 // visual order, and adds the valid windows to windows.
241 void SortTabsBasedOnVisualOrderAndPrune(
242 std::map<int, SessionWindow*>* windows,
243 std::vector<SessionWindow*>* valid_windows) {
244 std::map<int, SessionWindow*>::iterator i = windows->begin();
245 while (i != windows->end()) {
246 SessionWindow* window = i->second;
247 if (window->tabs.empty() || window->is_constrained) {
248 delete window;
249 windows->erase(i++);
250 } else {
251 // Valid window; sort the tabs and add it to the list of valid windows.
252 std::sort(window->tabs.begin(), window->tabs.end(),
253 &TabVisualIndexSortFunction);
254 // Otherwise, add the window such that older windows appear first.
255 if (valid_windows->empty()) {
256 valid_windows->push_back(window);
257 } else {
258 valid_windows->insert(
259 std::upper_bound(valid_windows->begin(), valid_windows->end(),
260 window, &WindowOrderSortFunction),
261 window);
263 ++i;
268 // Adds tabs to their parent window based on the tab's window_id. This
269 // ignores tabs with no navigations.
270 void AddTabsToWindows(std::map<int, SessionTab*>* tabs,
271 std::map<int, SessionWindow*>* windows) {
272 DVLOG(1) << "AddTabsToWindws";
273 DVLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
274 std::map<int, SessionTab*>::iterator i = tabs->begin();
275 while (i != tabs->end()) {
276 SessionTab* tab = i->second;
277 if (tab->window_id.id() && !tab->navigations.empty()) {
278 SessionWindow* window = GetWindow(tab->window_id.id(), windows);
279 window->tabs.push_back(tab);
280 tabs->erase(i++);
282 // See note in SessionTab as to why we do this.
283 std::vector<sessions::SerializedNavigationEntry>::iterator j =
284 FindClosestNavigationWithIndex(&(tab->navigations),
285 tab->current_navigation_index);
286 if (j == tab->navigations.end()) {
287 tab->current_navigation_index =
288 static_cast<int>(tab->navigations.size() - 1);
289 } else {
290 tab->current_navigation_index =
291 static_cast<int>(j - tab->navigations.begin());
293 } else {
294 // Never got a set tab index in window, or tabs are empty, nothing
295 // to do.
296 ++i;
301 // Creates tabs and windows from the commands specified in |data|. The created
302 // tabs and windows are added to |tabs| and |windows| respectively, with the
303 // id of the active window set in |active_window_id|. It is up to the caller
304 // to delete the tabs and windows added to |tabs| and |windows|.
306 // This does NOT add any created SessionTabs to SessionWindow.tabs, that is
307 // done by AddTabsToWindows.
308 bool CreateTabsAndWindows(const ScopedVector<SessionCommand>& data,
309 std::map<int, SessionTab*>* tabs,
310 std::map<int, SessionWindow*>* windows,
311 SessionID::id_type* active_window_id) {
312 // If the file is corrupt (command with wrong size, or unknown command), we
313 // still return true and attempt to restore what we we can.
314 DVLOG(1) << "CreateTabsAndWindows";
316 for (std::vector<SessionCommand*>::const_iterator i = data.begin();
317 i != data.end(); ++i) {
318 const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
319 const SessionCommand* command = *i;
321 DVLOG(1) << "Read command " << (int) command->id();
322 switch (command->id()) {
323 case kCommandSetTabWindow: {
324 SessionID::id_type payload[2];
325 if (!command->GetPayload(payload, sizeof(payload))) {
326 DVLOG(1) << "Failed reading command " << command->id();
327 return true;
329 GetTab(payload[1], tabs)->window_id.set_id(payload[0]);
330 break;
333 // This is here for forward migration only. New data is saved with
334 // |kCommandSetWindowBounds3|.
335 case kCommandSetWindowBounds2: {
336 WindowBoundsPayload2 payload;
337 if (!command->GetPayload(&payload, sizeof(payload))) {
338 DVLOG(1) << "Failed reading command " << command->id();
339 return true;
341 GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
342 payload.y,
343 payload.w,
344 payload.h);
345 GetWindow(payload.window_id, windows)->show_state =
346 payload.is_maximized ?
347 ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL;
348 break;
351 case kCommandSetWindowBounds3: {
352 WindowBoundsPayload3 payload;
353 if (!command->GetPayload(&payload, sizeof(payload))) {
354 DVLOG(1) << "Failed reading command " << command->id();
355 return true;
357 GetWindow(payload.window_id, windows)->bounds.SetRect(payload.x,
358 payload.y,
359 payload.w,
360 payload.h);
361 GetWindow(payload.window_id, windows)->show_state =
362 PersistedShowStateToShowState(payload.show_state);
363 break;
366 case kCommandSetTabIndexInWindow: {
367 TabIndexInWindowPayload payload;
368 if (!command->GetPayload(&payload, sizeof(payload))) {
369 DVLOG(1) << "Failed reading command " << command->id();
370 return true;
372 GetTab(payload.id, tabs)->tab_visual_index = payload.index;
373 break;
376 case kCommandTabClosed:
377 case kCommandWindowClosed: {
378 ClosedPayload payload;
379 if (!command->GetPayload(&payload, sizeof(payload))) {
380 DVLOG(1) << "Failed reading command " << command->id();
381 return true;
383 if (command->id() == kCommandTabClosed) {
384 delete GetTab(payload.id, tabs);
385 tabs->erase(payload.id);
386 } else {
387 delete GetWindow(payload.id, windows);
388 windows->erase(payload.id);
390 break;
393 case kCommandTabNavigationPathPrunedFromBack: {
394 TabNavigationPathPrunedFromBackPayload payload;
395 if (!command->GetPayload(&payload, sizeof(payload))) {
396 DVLOG(1) << "Failed reading command " << command->id();
397 return true;
399 SessionTab* tab = GetTab(payload.id, tabs);
400 tab->navigations.erase(
401 FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
402 tab->navigations.end());
403 break;
406 case kCommandTabNavigationPathPrunedFromFront: {
407 TabNavigationPathPrunedFromFrontPayload payload;
408 if (!command->GetPayload(&payload, sizeof(payload)) ||
409 payload.index <= 0) {
410 DVLOG(1) << "Failed reading command " << command->id();
411 return true;
413 SessionTab* tab = GetTab(payload.id, tabs);
415 // Update the selected navigation index.
416 tab->current_navigation_index =
417 std::max(-1, tab->current_navigation_index - payload.index);
419 // And update the index of existing navigations.
420 for (std::vector<sessions::SerializedNavigationEntry>::iterator
421 i = tab->navigations.begin();
422 i != tab->navigations.end();) {
423 i->set_index(i->index() - payload.index);
424 if (i->index() < 0)
425 i = tab->navigations.erase(i);
426 else
427 ++i;
429 break;
432 case kCommandUpdateTabNavigation: {
433 sessions::SerializedNavigationEntry navigation;
434 SessionID::id_type tab_id;
435 if (!RestoreUpdateTabNavigationCommand(*command,
436 &navigation,
437 &tab_id)) {
438 DVLOG(1) << "Failed reading command " << command->id();
439 return true;
441 SessionTab* tab = GetTab(tab_id, tabs);
442 std::vector<sessions::SerializedNavigationEntry>::iterator i =
443 FindClosestNavigationWithIndex(&(tab->navigations),
444 navigation.index());
445 if (i != tab->navigations.end() && i->index() == navigation.index())
446 *i = navigation;
447 else
448 tab->navigations.insert(i, navigation);
449 break;
452 case kCommandSetSelectedNavigationIndex: {
453 SelectedNavigationIndexPayload payload;
454 if (!command->GetPayload(&payload, sizeof(payload))) {
455 DVLOG(1) << "Failed reading command " << command->id();
456 return true;
458 GetTab(payload.id, tabs)->current_navigation_index = payload.index;
459 break;
462 case kCommandSetSelectedTabInIndex: {
463 SelectedTabInIndexPayload payload;
464 if (!command->GetPayload(&payload, sizeof(payload))) {
465 DVLOG(1) << "Failed reading command " << command->id();
466 return true;
468 GetWindow(payload.id, windows)->selected_tab_index = payload.index;
469 break;
472 case kCommandSetWindowType: {
473 WindowTypePayload payload;
474 if (!command->GetPayload(&payload, sizeof(payload))) {
475 DVLOG(1) << "Failed reading command " << command->id();
476 return true;
478 GetWindow(payload.id, windows)->is_constrained = false;
479 GetWindow(payload.id, windows)->type =
480 static_cast<SessionWindow::WindowType>(payload.index);
481 break;
484 case kCommandSetPinnedState: {
485 PinnedStatePayload payload;
486 if (!command->GetPayload(&payload, sizeof(payload))) {
487 DVLOG(1) << "Failed reading command " << command->id();
488 return true;
490 GetTab(payload.tab_id, tabs)->pinned = payload.pinned_state;
491 break;
494 case kCommandSetWindowAppName: {
495 SessionID::id_type window_id;
496 std::string app_name;
497 if (!RestoreSetWindowAppNameCommand(*command, &window_id, &app_name))
498 return true;
500 GetWindow(window_id, windows)->app_name.swap(app_name);
501 break;
504 case kCommandSetExtensionAppID: {
505 SessionID::id_type tab_id;
506 std::string extension_app_id;
507 if (!RestoreSetTabExtensionAppIDCommand(*command,
508 &tab_id,
509 &extension_app_id)) {
510 DVLOG(1) << "Failed reading command " << command->id();
511 return true;
514 GetTab(tab_id, tabs)->extension_app_id.swap(extension_app_id);
515 break;
518 case kCommandSetTabUserAgentOverride: {
519 SessionID::id_type tab_id;
520 std::string user_agent_override;
521 if (!RestoreSetTabUserAgentOverrideCommand(
522 *command,
523 &tab_id,
524 &user_agent_override)) {
525 return true;
528 GetTab(tab_id, tabs)->user_agent_override.swap(user_agent_override);
529 break;
532 case kCommandSessionStorageAssociated: {
533 scoped_ptr<Pickle> command_pickle(command->PayloadAsPickle());
534 SessionID::id_type command_tab_id;
535 std::string session_storage_persistent_id;
536 PickleIterator iter(*command_pickle.get());
537 if (!iter.ReadInt(&command_tab_id) ||
538 !iter.ReadString(&session_storage_persistent_id))
539 return true;
540 // Associate the session storage back.
541 GetTab(command_tab_id, tabs)->session_storage_persistent_id =
542 session_storage_persistent_id;
543 break;
546 case kCommandSetActiveWindow: {
547 ActiveWindowPayload payload;
548 if (!command->GetPayload(&payload, sizeof(payload))) {
549 DVLOG(1) << "Failed reading command " << command->id();
550 return true;
552 *active_window_id = payload;
553 break;
556 default:
557 // TODO(skuhne): This might call back into a callback handler to extend
558 // the command set for specific implementations.
559 DVLOG(1) << "Failed reading an unknown command " << command->id();
560 return true;
563 return true;
566 } // namespace
568 scoped_ptr<SessionCommand> CreateSetSelectedTabInWindowCommand(
569 const SessionID& window_id,
570 int index) {
571 SelectedTabInIndexPayload payload = { 0 };
572 payload.id = window_id.id();
573 payload.index = index;
574 scoped_ptr<SessionCommand> command(
575 new SessionCommand(kCommandSetSelectedTabInIndex, sizeof(payload)));
576 memcpy(command->contents(), &payload, sizeof(payload));
577 return command;
580 scoped_ptr<SessionCommand> CreateSetTabWindowCommand(const SessionID& window_id,
581 const SessionID& tab_id) {
582 SessionID::id_type payload[] = { window_id.id(), tab_id.id() };
583 scoped_ptr<SessionCommand> command(
584 new SessionCommand(kCommandSetTabWindow, sizeof(payload)));
585 memcpy(command->contents(), payload, sizeof(payload));
586 return command;
589 scoped_ptr<SessionCommand> CreateSetWindowBoundsCommand(
590 const SessionID& window_id,
591 const gfx::Rect& bounds,
592 ui::WindowShowState show_state) {
593 WindowBoundsPayload3 payload = { 0 };
594 payload.window_id = window_id.id();
595 payload.x = bounds.x();
596 payload.y = bounds.y();
597 payload.w = bounds.width();
598 payload.h = bounds.height();
599 payload.show_state = ShowStateToPersistedShowState(show_state);
600 scoped_ptr<SessionCommand> command(
601 new SessionCommand(kCommandSetWindowBounds3, sizeof(payload)));
602 memcpy(command->contents(), &payload, sizeof(payload));
603 return command;
606 scoped_ptr<SessionCommand> CreateSetTabIndexInWindowCommand(
607 const SessionID& tab_id,
608 int new_index) {
609 TabIndexInWindowPayload payload = { 0 };
610 payload.id = tab_id.id();
611 payload.index = new_index;
612 scoped_ptr<SessionCommand> command(
613 new SessionCommand(kCommandSetTabIndexInWindow, sizeof(payload)));
614 memcpy(command->contents(), &payload, sizeof(payload));
615 return command;
618 scoped_ptr<SessionCommand> CreateTabClosedCommand(
619 const SessionID::id_type tab_id) {
620 ClosedPayload payload;
621 // Because of what appears to be a compiler bug setting payload to {0} doesn't
622 // set the padding to 0, resulting in Purify reporting an UMR when we write
623 // the structure to disk. To avoid this we explicitly memset the struct.
624 memset(&payload, 0, sizeof(payload));
625 payload.id = tab_id;
626 payload.close_time = base::Time::Now().ToInternalValue();
627 scoped_ptr<SessionCommand> command(
628 new SessionCommand(kCommandTabClosed, sizeof(payload)));
629 memcpy(command->contents(), &payload, sizeof(payload));
630 return command;
633 scoped_ptr<SessionCommand> CreateWindowClosedCommand(
634 const SessionID::id_type window_id) {
635 ClosedPayload payload;
636 // See comment in CreateTabClosedCommand as to why we do this.
637 memset(&payload, 0, sizeof(payload));
638 payload.id = window_id;
639 payload.close_time = base::Time::Now().ToInternalValue();
640 scoped_ptr<SessionCommand> command(
641 new SessionCommand(kCommandWindowClosed, sizeof(payload)));
642 memcpy(command->contents(), &payload, sizeof(payload));
643 return command;
646 scoped_ptr<SessionCommand> CreateSetSelectedNavigationIndexCommand(
647 const SessionID& tab_id,
648 int index) {
649 SelectedNavigationIndexPayload payload = { 0 };
650 payload.id = tab_id.id();
651 payload.index = index;
652 scoped_ptr<SessionCommand> command(
653 new SessionCommand(kCommandSetSelectedNavigationIndex, sizeof(payload)));
654 memcpy(command->contents(), &payload, sizeof(payload));
655 return command;
658 scoped_ptr<SessionCommand> CreateSetWindowTypeCommand(
659 const SessionID& window_id,
660 SessionWindow::WindowType type) {
661 WindowTypePayload payload = { 0 };
662 payload.id = window_id.id();
663 payload.index = static_cast<int32>(type);
664 scoped_ptr<SessionCommand> command(
665 new SessionCommand( kCommandSetWindowType, sizeof(payload)));
666 memcpy(command->contents(), &payload, sizeof(payload));
667 return command;
670 scoped_ptr<SessionCommand> CreatePinnedStateCommand(
671 const SessionID& tab_id,
672 bool is_pinned) {
673 PinnedStatePayload payload = { 0 };
674 payload.tab_id = tab_id.id();
675 payload.pinned_state = is_pinned;
676 scoped_ptr<SessionCommand> command(
677 new SessionCommand(kCommandSetPinnedState, sizeof(payload)));
678 memcpy(command->contents(), &payload, sizeof(payload));
679 return command;
682 scoped_ptr<SessionCommand> CreateSessionStorageAssociatedCommand(
683 const SessionID& tab_id,
684 const std::string& session_storage_persistent_id) {
685 Pickle pickle;
686 pickle.WriteInt(tab_id.id());
687 pickle.WriteString(session_storage_persistent_id);
688 return scoped_ptr<SessionCommand>(
689 new SessionCommand(kCommandSessionStorageAssociated, pickle));
692 scoped_ptr<SessionCommand> CreateSetActiveWindowCommand(
693 const SessionID& window_id) {
694 ActiveWindowPayload payload = 0;
695 payload = window_id.id();
696 scoped_ptr<SessionCommand> command(
697 new SessionCommand(kCommandSetActiveWindow, sizeof(payload)));
698 memcpy(command->contents(), &payload, sizeof(payload));
699 return command;
702 scoped_ptr<SessionCommand> CreateTabNavigationPathPrunedFromBackCommand(
703 const SessionID& tab_id,
704 int count) {
705 TabNavigationPathPrunedFromBackPayload payload = { 0 };
706 payload.id = tab_id.id();
707 payload.index = count;
708 scoped_ptr<SessionCommand> command(
709 new SessionCommand(kCommandTabNavigationPathPrunedFromBack,
710 sizeof(payload)));
711 memcpy(command->contents(), &payload, sizeof(payload));
712 return command;
715 scoped_ptr<SessionCommand> CreateTabNavigationPathPrunedFromFrontCommand(
716 const SessionID& tab_id,
717 int count) {
718 TabNavigationPathPrunedFromFrontPayload payload = { 0 };
719 payload.id = tab_id.id();
720 payload.index = count;
721 scoped_ptr<SessionCommand> command(
722 new SessionCommand(kCommandTabNavigationPathPrunedFromFront,
723 sizeof(payload)));
724 memcpy(command->contents(), &payload, sizeof(payload));
725 return command;
728 scoped_ptr<SessionCommand> CreateUpdateTabNavigationCommand(
729 const SessionID& tab_id,
730 const sessions::SerializedNavigationEntry& navigation) {
731 return CreateUpdateTabNavigationCommand(kCommandUpdateTabNavigation,
732 tab_id.id(),
733 navigation);
736 scoped_ptr<SessionCommand> CreateSetTabExtensionAppIDCommand(
737 const SessionID& tab_id,
738 const std::string& extension_id) {
739 return CreateSetTabExtensionAppIDCommand(kCommandSetExtensionAppID,
740 tab_id.id(),
741 extension_id);
744 scoped_ptr<SessionCommand> CreateSetTabUserAgentOverrideCommand(
745 const SessionID& tab_id,
746 const std::string& user_agent_override) {
747 return CreateSetTabUserAgentOverrideCommand(kCommandSetTabUserAgentOverride,
748 tab_id.id(),
749 user_agent_override);
752 scoped_ptr<SessionCommand> CreateSetWindowAppNameCommand(
753 const SessionID& window_id,
754 const std::string& app_name) {
755 return CreateSetWindowAppNameCommand(kCommandSetWindowAppName,
756 window_id.id(),
757 app_name);
760 bool ReplacePendingCommand(BaseSessionService* base_session_service,
761 scoped_ptr<SessionCommand>* command) {
762 // We optimize page navigations, which can happen quite frequently and
763 // is expensive. And activation is like Highlander, there can only be one!
764 if ((*command)->id() != kCommandUpdateTabNavigation &&
765 (*command)->id() != kCommandSetActiveWindow) {
766 return false;
768 for (ScopedVector<SessionCommand>::const_reverse_iterator i =
769 base_session_service->pending_commands().rbegin();
770 i != base_session_service->pending_commands().rend(); ++i) {
771 SessionCommand* existing_command = *i;
772 if ((*command)->id() == kCommandUpdateTabNavigation &&
773 existing_command->id() == kCommandUpdateTabNavigation) {
774 scoped_ptr<Pickle> command_pickle((*command)->PayloadAsPickle());
775 PickleIterator iterator(*command_pickle);
776 SessionID::id_type command_tab_id;
777 int command_nav_index;
778 if (!iterator.ReadInt(&command_tab_id) ||
779 !iterator.ReadInt(&command_nav_index)) {
780 return false;
782 SessionID::id_type existing_tab_id;
783 int existing_nav_index;
785 // Creating a pickle like this means the Pickle references the data from
786 // the command. Make sure we delete the pickle before the command, else
787 // the pickle references deleted memory.
788 scoped_ptr<Pickle> existing_pickle(existing_command->PayloadAsPickle());
789 iterator = PickleIterator(*existing_pickle);
790 if (!iterator.ReadInt(&existing_tab_id) ||
791 !iterator.ReadInt(&existing_nav_index)) {
792 return false;
795 if (existing_tab_id == command_tab_id &&
796 existing_nav_index == command_nav_index) {
797 // existing_command is an update for the same tab/index pair. Replace
798 // it with the new one. We need to add to the end of the list just in
799 // case there is a prune command after the update command.
800 base_session_service->EraseCommand(*(i.base() - 1));
801 base_session_service->AppendRebuildCommand((*command).Pass());
802 return true;
804 return false;
806 if ((*command)->id() == kCommandSetActiveWindow &&
807 existing_command->id() == kCommandSetActiveWindow) {
808 base_session_service->SwapCommand(existing_command, (*command).Pass());
809 return true;
812 return false;
815 bool IsClosingCommand(SessionCommand* command) {
816 return command->id() == kCommandTabClosed ||
817 command->id() == kCommandWindowClosed;
820 void RestoreSessionFromCommands(const ScopedVector<SessionCommand>& commands,
821 std::vector<SessionWindow*>* valid_windows,
822 SessionID::id_type* active_window_id) {
823 std::map<int, SessionTab*> tabs;
824 std::map<int, SessionWindow*> windows;
826 DVLOG(1) << "RestoreSessionFromCommands " << commands.size();
827 if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
828 AddTabsToWindows(&tabs, &windows);
829 SortTabsBasedOnVisualOrderAndPrune(&windows, valid_windows);
830 UpdateSelectedTabIndex(valid_windows);
832 STLDeleteValues(&tabs);
833 // Don't delete contents of windows, that is done by the caller as all
834 // valid windows are added to valid_windows.
837 } // namespace sessions