1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/session_restore/session_restore_api.h"
6
7#include <vector>
8
9#include "base/i18n/rtl.h"
10#include "base/lazy_instance.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/extensions/extension_function_registry.h"
14#include "chrome/browser/extensions/extension_tab_util.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/sessions/session_restore.h"
17#include "chrome/browser/sessions/tab_restore_service_delegate.h"
18#include "chrome/browser/sessions/tab_restore_service_factory.h"
19#include "chrome/browser/sync/glue/session_model_associator.h"
20#include "chrome/browser/sync/glue/synced_session.h"
21#include "chrome/browser/sync/profile_sync_service.h"
22#include "chrome/browser/sync/profile_sync_service_factory.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_finder.h"
25#include "chrome/browser/ui/host_desktop.h"
26#include "chrome/browser/ui/tabs/tab_strip_model.h"
27#include "content/public/browser/web_contents.h"
28#include "ui/base/layout.h"
29
30
31namespace {
32
33const unsigned int kMaxRecentlyClosedSessionResults = 25;
34const char kRecentlyClosedListEmpty[] =
35    "There are no recently closed sessions.";
36const char kInvalidSessionId[] = "Invalid session id.";
37const char kNoBrowserToRestoreSession[] =
38    "There are no browser windows to restore the session.";
39
40}  // namespace
41
42namespace extensions {
43
44namespace GetRecentlyClosed = api::session_restore::GetRecentlyClosed;
45namespace Restore = api::session_restore::Restore;
46namespace tabs = api::tabs;
47namespace windows = api::windows;
48namespace session_restore = api::session_restore;
49
50scoped_ptr<tabs::Tab> SessionRestoreGetRecentlyClosedFunction::CreateTabModel(
51    const TabRestoreService::Tab& tab, int selected_index) {
52  scoped_ptr<tabs::Tab> tab_struct(new tabs::Tab);
53  const sessions::SerializedNavigationEntry& current_navigation =
54      tab.navigations[tab.current_navigation_index];
55  GURL gurl = current_navigation.virtual_url();
56  std::string title = UTF16ToUTF8(current_navigation.title());
57
58  tab_struct->url.reset(new std::string(gurl.spec()));
59  tab_struct->title.reset(new std::string(title.empty() ? gurl.spec() : title));
60  tab_struct->index = tab.tabstrip_index;
61  tab_struct->pinned = tab.pinned;
62  tab_struct->id = tab.id;
63  tab_struct->window_id = tab.browser_id;
64  tab_struct->index = tab.tabstrip_index;
65  tab_struct->pinned = tab.pinned;
66  tab_struct->selected = tab.tabstrip_index == selected_index;
67  tab_struct->active = false;
68  tab_struct->highlighted = false;
69  tab_struct->incognito = false;
70  ExtensionTabUtil::ScrubTabForExtension(GetExtension(),
71                                         tab_struct.get());
72  return tab_struct.Pass();
73}
74
75scoped_ptr<windows::Window>
76    SessionRestoreGetRecentlyClosedFunction::CreateWindowModel(
77        const TabRestoreService::Window& window) {
78  scoped_ptr<windows::Window> window_struct(new windows::Window);
79  DCHECK(!window.tabs.empty());
80
81  scoped_ptr<std::vector<linked_ptr<tabs::Tab> > > tabs(
82      new std::vector<linked_ptr<tabs::Tab> >);
83  for (size_t i = 0; i < window.tabs.size(); ++i) {
84    tabs->push_back(make_linked_ptr(CreateTabModel(window.tabs[i],
85        window.selected_tab_index).release()));
86  }
87  window_struct->tabs.reset(tabs.release());
88  window_struct->incognito = false;
89  window_struct->always_on_top = false;
90  window_struct->focused = false;
91  window_struct->type = windows::Window::TYPE_NORMAL;
92  window_struct->state = windows::Window::STATE_NORMAL;
93  return window_struct.Pass();
94}
95
96scoped_ptr<session_restore::ClosedEntry>
97    SessionRestoreGetRecentlyClosedFunction::CreateEntryModel(
98        const TabRestoreService::Entry* entry) {
99  scoped_ptr<session_restore::ClosedEntry> entry_struct(
100      new session_restore::ClosedEntry);
101  switch (entry->type) {
102    case TabRestoreService::TAB:
103      entry_struct->tab.reset(CreateTabModel(
104          *static_cast<const TabRestoreService::Tab*>(entry), -1).release());
105      break;
106    case TabRestoreService::WINDOW:
107      entry_struct->window.reset(CreateWindowModel(
108          *static_cast<const TabRestoreService::Window*>(entry)).release());
109      break;
110    default:
111      NOTREACHED();
112  }
113  entry_struct->timestamp = entry->timestamp.ToTimeT();
114  entry_struct->id = entry->id;
115  return entry_struct.Pass();
116}
117
118bool SessionRestoreGetRecentlyClosedFunction::RunImpl() {
119  scoped_ptr<GetRecentlyClosed::Params> params(
120      GetRecentlyClosed::Params::Create(*args_));
121  EXTENSION_FUNCTION_VALIDATE(params.get());
122  unsigned int max_results = kMaxRecentlyClosedSessionResults;
123  if (params->options && params->options->max_results)
124    max_results = *params->options->max_results;
125  EXTENSION_FUNCTION_VALIDATE(max_results >= 0 &&
126                              max_results <= kMaxRecentlyClosedSessionResults);
127
128  std::vector<linked_ptr<session_restore::ClosedEntry> > result;
129  TabRestoreService* tab_restore_service =
130      TabRestoreServiceFactory::GetForProfile(profile());
131  DCHECK(tab_restore_service);
132
133  // List of entries. They are ordered from most to least recent.
134  // We prune the list to contain max 25 entries at any time and removes
135  // uninteresting entries.
136  TabRestoreService::Entries entries = tab_restore_service->entries();
137  for (TabRestoreService::Entries::const_iterator it = entries.begin();
138       it != entries.end() && result.size() < max_results; ++it) {
139    TabRestoreService::Entry* entry = *it;
140    if (!params->options || params->options->entry_type ==
141        GetRecentlyClosed::Params::Options::ENTRY_TYPE_NONE) {
142      // Include both tabs and windows if type is not defined.
143      result.push_back(make_linked_ptr(CreateEntryModel(entry).release()));
144    } else if (
145        (params->options->entry_type ==
146            GetRecentlyClosed::Params::Options::ENTRY_TYPE_TAB &&
147                entry->type == TabRestoreService::TAB) ||
148        (params->options->entry_type ==
149            GetRecentlyClosed::Params::Options::ENTRY_TYPE_WINDOW &&
150                entry->type == TabRestoreService::WINDOW)) {
151      result.push_back(make_linked_ptr(CreateEntryModel(entry).release()));
152    }
153  }
154
155  results_ = GetRecentlyClosed::Results::Create(result);
156  return true;
157}
158
159bool SessionRestoreRestoreFunction::RunImpl() {
160  scoped_ptr<Restore::Params> params(Restore::Params::Create(*args_));
161  EXTENSION_FUNCTION_VALIDATE(params.get());
162
163  Browser* browser =
164      chrome::FindBrowserWithProfile(profile(),
165                                     chrome::HOST_DESKTOP_TYPE_NATIVE);
166  if (!browser) {
167    error_ = kNoBrowserToRestoreSession;
168    return false;
169  }
170
171  TabRestoreService* tab_restore_service =
172      TabRestoreServiceFactory::GetForProfile(profile());
173  TabRestoreServiceDelegate* delegate =
174      TabRestoreServiceDelegate::FindDelegateForWebContents(
175          browser->tab_strip_model()->GetActiveWebContents());
176  DCHECK(delegate);
177  chrome::HostDesktopType host_desktop_type = browser->host_desktop_type();
178  TabRestoreService::Entries entries = tab_restore_service->entries();
179
180  if (entries.empty()) {
181    error_ = kRecentlyClosedListEmpty;
182    return false;
183  }
184
185  if (!params->id) {
186    tab_restore_service->RestoreMostRecentEntry(delegate, host_desktop_type);
187    return true;
188  }
189
190  // Check if the recently closed list contains an entry with the provided id.
191  bool is_valid_id = false;
192  for (TabRestoreService::Entries::iterator it = entries.begin();
193       it != entries.end(); ++it) {
194    if ((*it)->id == *params->id) {
195      is_valid_id = true;
196      break;
197    }
198
199    // For Window entries, see if the ID matches a tab. If so, report true for
200    // the window as the Entry.
201    if ((*it)->type == TabRestoreService::WINDOW) {
202      std::vector<TabRestoreService::Tab>& tabs =
203          static_cast<TabRestoreService::Window*>(*it)->tabs;
204      for (std::vector<TabRestoreService::Tab>::iterator tab_it = tabs.begin();
205           tab_it != tabs.end(); ++tab_it) {
206        if ((*tab_it).id == *params->id) {
207          is_valid_id = true;
208          break;
209        }
210      }
211    }
212  }
213
214  if (!is_valid_id) {
215    error_ = kInvalidSessionId;
216    return false;
217  }
218
219  tab_restore_service->RestoreEntryById(delegate, *params->id,
220      host_desktop_type, UNKNOWN);
221  return true;
222}
223
224SessionRestoreAPI::SessionRestoreAPI(Profile* profile) {
225}
226
227SessionRestoreAPI::~SessionRestoreAPI() {
228}
229
230static base::LazyInstance<ProfileKeyedAPIFactory<SessionRestoreAPI> >
231    g_factory = LAZY_INSTANCE_INITIALIZER;
232
233// static
234ProfileKeyedAPIFactory<SessionRestoreAPI>*
235    SessionRestoreAPI::GetFactoryInstance() {
236  return &g_factory.Get();
237}
238
239}  // namespace extensions
240