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