15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/tab_restore_service_delegate.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/tab_restore_service_factory.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_ui.h"
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/base/webui/web_ui_util.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabToValue(const TabRestoreService::Tab& tab,
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                base::DictionaryValue* dictionary) {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const sessions::SerializedNavigationEntry& current_navigation =
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab.navigations.at(tab.current_navigation_index);
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NewTabUI::SetUrlTitleAndDirection(dictionary, current_navigation.title(),
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    current_navigation.virtual_url());
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetString("type", "tab");
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetDouble("timestamp", tab.timestamp.ToDoubleT());
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WindowToValue(const TabRestoreService::Window& window,
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                   base::DictionaryValue* dictionary) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!window.tabs.empty());
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> tab_values(new base::ListValue());
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < window.tabs.size(); ++i) {
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::DictionaryValue* tab_value = new base::DictionaryValue();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabToValue(window.tabs[i], tab_value);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_values->Append(tab_value);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetString("type", "window");
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetDouble("timestamp", window.timestamp.ToDoubleT());
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->Set("tabs", tab_values.release());
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RecentlyClosedTabsHandler::RegisterMessages() {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("getRecentlyClosedTabs",
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("reopenTab",
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&RecentlyClosedTabsHandler::HandleReopenTab,
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("clearRecentlyClosed",
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&RecentlyClosedTabsHandler::HandleClearRecentlyClosed,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_restore_service_)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_restore_service_->RemoveObserver(this);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void RecentlyClosedTabsHandler::HandleReopenTab(const base::ListValue* args) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tab_restore_service_)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double session_to_restore = 0.0;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetDouble(0, &session_to_restore));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double index = -1.0;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetDouble(1, &index));
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There are actually less than 20 restore tab items displayed in the UI.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("NewTabPage.SessionRestore",
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            static_cast<int>(index), 20);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabRestoreServiceDelegate* delegate =
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabRestoreServiceDelegate::FindDelegateForWebContents(
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          web_ui()->GetWebContents());
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!delegate)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::HostDesktopType host_desktop_type =
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome::GetHostDesktopTypeForNativeView(
88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          web_ui()->GetWebContents()->GetNativeView());
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 2);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_restore_service_->RestoreEntryById(delegate,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         static_cast<int>(session_to_restore),
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         host_desktop_type,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         disposition);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current tab has been nuked at this point; don't touch any member
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // variables.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RecentlyClosedTabsHandler::HandleClearRecentlyClosed(
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::ListValue* args) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureTabRestoreService();
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_restore_service_)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_restore_service_->ClearEntries();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs(
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::ListValue* args) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureTabRestoreService();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_restore_service_)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabRestoreServiceChanged(tab_restore_service_);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RecentlyClosedTabsHandler::TabRestoreServiceChanged(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabRestoreService* service) {
1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  base::ListValue list_value;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_count = 10;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int added_count = 0;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We filter the list of recently closed to only show 'interesting' entries,
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // where an interesting entry is either a closed window or a closed tab
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // whose selected navigation is not the new tab ui.
120e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  for (TabRestoreService::Entries::const_iterator it =
121e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch           service->entries().begin();
122e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch       it != service->entries().end() && added_count < max_count; ++it) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabRestoreService::Entry* entry = *it;
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> entry_dict(new base::DictionaryValue());
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (entry->type == TabRestoreService::TAB) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabToValue(*static_cast<TabRestoreService::Tab*>(entry),
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 entry_dict.get());
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else  {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK_EQ(entry->type, TabRestoreService::WINDOW);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      WindowToValue(*static_cast<TabRestoreService::Window*>(entry),
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    entry_dict.get());
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    entry_dict->SetInteger("sessionId", entry->id);
135e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    list_value.Append(entry_dict.release());
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ++added_count;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
138e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
139e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  web_ui()->CallJavascriptFunction("ntp.setRecentlyClosedTabs", list_value);
140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch}
141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
142e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochvoid RecentlyClosedTabsHandler::TabRestoreServiceDestroyed(
143e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    TabRestoreService* service) {
144e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  tab_restore_service_ = NULL;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RecentlyClosedTabsHandler::EnsureTabRestoreService() {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_restore_service_)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_restore_service_ =
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabRestoreServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Off the Record mode)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_restore_service_) {
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This does nothing if the tabs have already been loaded or they
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // shouldn't be loaded.
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_restore_service_->LoadTabsFromLastSession();
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_restore_service_->AddObserver(this);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
163