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/ui/webui/ntp/recently_closed_tabs_handler.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/metrics/histogram.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/sessions/tab_restore_service_delegate.h"
12#include "chrome/browser/sessions/tab_restore_service_factory.h"
13#include "chrome/browser/ui/host_desktop.h"
14#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
15#include "chrome/common/url_constants.h"
16#include "content/public/browser/web_contents.h"
17#include "content/public/browser/web_ui.h"
18#include "ui/base/webui/web_ui_util.h"
19
20namespace {
21
22void TabToValue(const TabRestoreService::Tab& tab,
23                base::DictionaryValue* dictionary) {
24  const sessions::SerializedNavigationEntry& current_navigation =
25      tab.navigations.at(tab.current_navigation_index);
26  NewTabUI::SetUrlTitleAndDirection(dictionary, current_navigation.title(),
27                                    current_navigation.virtual_url());
28  dictionary->SetString("type", "tab");
29  dictionary->SetDouble("timestamp", tab.timestamp.ToDoubleT());
30}
31
32void WindowToValue(const TabRestoreService::Window& window,
33                   base::DictionaryValue* dictionary) {
34  DCHECK(!window.tabs.empty());
35
36  scoped_ptr<base::ListValue> tab_values(new base::ListValue());
37  for (size_t i = 0; i < window.tabs.size(); ++i) {
38    base::DictionaryValue* tab_value = new base::DictionaryValue();
39    TabToValue(window.tabs[i], tab_value);
40    tab_values->Append(tab_value);
41  }
42
43  dictionary->SetString("type", "window");
44  dictionary->SetDouble("timestamp", window.timestamp.ToDoubleT());
45  dictionary->Set("tabs", tab_values.release());
46}
47
48}  // namespace
49
50void RecentlyClosedTabsHandler::RegisterMessages() {
51  web_ui()->RegisterMessageCallback("getRecentlyClosedTabs",
52      base::Bind(&RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs,
53                 base::Unretained(this)));
54  web_ui()->RegisterMessageCallback("reopenTab",
55      base::Bind(&RecentlyClosedTabsHandler::HandleReopenTab,
56                 base::Unretained(this)));
57  web_ui()->RegisterMessageCallback("clearRecentlyClosed",
58      base::Bind(&RecentlyClosedTabsHandler::HandleClearRecentlyClosed,
59                 base::Unretained(this)));
60}
61
62RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() {
63  if (tab_restore_service_)
64    tab_restore_service_->RemoveObserver(this);
65}
66
67void RecentlyClosedTabsHandler::HandleReopenTab(const base::ListValue* args) {
68  if (!tab_restore_service_)
69    return;
70
71  double session_to_restore = 0.0;
72  CHECK(args->GetDouble(0, &session_to_restore));
73
74  double index = -1.0;
75  CHECK(args->GetDouble(1, &index));
76
77  // There are actually less than 20 restore tab items displayed in the UI.
78  UMA_HISTOGRAM_ENUMERATION("NewTabPage.SessionRestore",
79                            static_cast<int>(index), 20);
80
81  TabRestoreServiceDelegate* delegate =
82      TabRestoreServiceDelegate::FindDelegateForWebContents(
83          web_ui()->GetWebContents());
84  if (!delegate)
85    return;
86  chrome::HostDesktopType host_desktop_type =
87      chrome::GetHostDesktopTypeForNativeView(
88          web_ui()->GetWebContents()->GetNativeView());
89  WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 2);
90  tab_restore_service_->RestoreEntryById(delegate,
91                                         static_cast<int>(session_to_restore),
92                                         host_desktop_type,
93                                         disposition);
94  // The current tab has been nuked at this point; don't touch any member
95  // variables.
96}
97
98void RecentlyClosedTabsHandler::HandleClearRecentlyClosed(
99    const base::ListValue* args) {
100  EnsureTabRestoreService();
101  if (tab_restore_service_)
102    tab_restore_service_->ClearEntries();
103}
104
105void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs(
106    const base::ListValue* args) {
107  EnsureTabRestoreService();
108  if (tab_restore_service_)
109    TabRestoreServiceChanged(tab_restore_service_);
110}
111
112void RecentlyClosedTabsHandler::TabRestoreServiceChanged(
113    TabRestoreService* service) {
114  base::ListValue list_value;
115  const int max_count = 10;
116  int added_count = 0;
117  // We filter the list of recently closed to only show 'interesting' entries,
118  // where an interesting entry is either a closed window or a closed tab
119  // whose selected navigation is not the new tab ui.
120  for (TabRestoreService::Entries::const_iterator it =
121           service->entries().begin();
122       it != service->entries().end() && added_count < max_count; ++it) {
123    TabRestoreService::Entry* entry = *it;
124    scoped_ptr<base::DictionaryValue> entry_dict(new base::DictionaryValue());
125    if (entry->type == TabRestoreService::TAB) {
126      TabToValue(*static_cast<TabRestoreService::Tab*>(entry),
127                 entry_dict.get());
128    } else  {
129      DCHECK_EQ(entry->type, TabRestoreService::WINDOW);
130      WindowToValue(*static_cast<TabRestoreService::Window*>(entry),
131                    entry_dict.get());
132    }
133
134    entry_dict->SetInteger("sessionId", entry->id);
135    list_value.Append(entry_dict.release());
136    ++added_count;
137  }
138
139  web_ui()->CallJavascriptFunction("ntp.setRecentlyClosedTabs", list_value);
140}
141
142void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed(
143    TabRestoreService* service) {
144  tab_restore_service_ = NULL;
145}
146
147void RecentlyClosedTabsHandler::EnsureTabRestoreService() {
148  if (tab_restore_service_)
149    return;
150
151  tab_restore_service_ =
152      TabRestoreServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
153
154  // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in
155  // Off the Record mode)
156  if (tab_restore_service_) {
157    // This does nothing if the tabs have already been loaded or they
158    // shouldn't be loaded.
159    tab_restore_service_->LoadTabsFromLastSession();
160    tab_restore_service_->AddObserver(this);
161  }
162}
163