foreign_session_handler.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/foreign_session_handler.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/time_formatting.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_vector.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_number_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prefs/pref_service.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prefs/scoped_user_pref_update.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/session_restore.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/profile_sync_service.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/profile_sync_service_factory.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_ANDROID)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/search/other_device_menu_controller.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/session_favicon_source.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/web_ui_util.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_notification_types.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/time_format.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace browser_sync {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Maximum number of sessions we're going to display on the NTP
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const size_t kMaxSessionsToShow = 10;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Comparator function for use with std::sort that will sort sessions by
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// descending modified_time (i.e., most recent first).
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SortSessionsByRecency(const SyncedSession* s1, const SyncedSession* s2) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return s1->modified_time > s2->modified_time;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namepace
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ForeignSessionHandler::ForeignSessionHandler() {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::RegisterUserPrefs(PrefService* prefs) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefs->RegisterDictionaryPref(prefs::kNtpCollapsedForeignSessions,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                PrefService::UNSYNCABLE_PREF);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::OpenForeignSessionTab(
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::WebUI* web_ui,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& session_string_value,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SessionID::id_type window_num,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SessionID::id_type tab_id,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const WindowOpenDisposition& disposition) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionModelAssociator* associator = GetModelAssociator(web_ui);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!associator)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't actually care about |window_num|, this is just a sanity check.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LT(kInvalidId, window_num);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SessionTab* tab;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!associator->GetForeignTab(session_string_value, tab_id, &tab)) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to load foreign tab.";
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab->navigations.empty()) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Foreign tab no longer has valid navigations.";
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionRestore::RestoreForeignSessionTab(
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      web_ui->GetWebContents(), *tab, disposition);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::OpenForeignSessionWindows(
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::WebUI* web_ui,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& session_string_value,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SessionID::id_type window_num) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionModelAssociator* associator = GetModelAssociator(web_ui);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!associator)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<const SessionWindow*> windows;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Note: we don't own the ForeignSessions themselves.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!associator->GetForeignSession(session_string_value, &windows)) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "SessionModelAssociator.";
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<const SessionWindow*>::const_iterator iter_begin =
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      windows.begin() + (window_num == kInvalidId ? 0 : window_num);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<const SessionWindow*>::const_iterator iter_end =
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window_num == kInvalidId ?
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::vector<const SessionWindow*>::const_iterator(windows.end()) :
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      iter_begin + 1;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionRestore::RestoreForeignSessionWindows(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Profile::FromWebUI(web_ui), iter_begin, iter_end);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ForeignSessionHandler::SessionTabToValue(
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SessionTab& tab,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* dictionary) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab.navigations.empty())
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int selected_index = std::min(tab.current_navigation_index,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                static_cast<int>(tab.navigations.size() - 1));
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const TabNavigation& current_navigation =
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab.navigations.at(selected_index);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL tab_url = current_navigation.virtual_url();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_url == GURL(chrome::kChromeUINewTabURL))
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NewTabUI::SetUrlTitleAndDirection(dictionary, current_navigation.title(),
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    tab_url);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetString("type", "tab");
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetDouble("timestamp",
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        static_cast<double>(tab.timestamp.ToInternalValue()));
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jeremycho): This should probably be renamed to tabId to avoid
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // confusion with the ID corresponding to a session.  Investigate all the
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // places (C++ and JS) where this is being used.  (http://crbug.com/154865).
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetInteger("sessionId", tab.tab_id.id());
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SessionModelAssociator* ForeignSessionHandler::GetModelAssociator(
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::WebUI* web_ui) {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileSyncService* service =
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only return the associator if it exists and it is done syncing sessions.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (service && service->ShouldPushChanges())
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return service->GetSessionModelAssociator();
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::RegisterMessages() {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("deleteForeignSession",
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ForeignSessionHandler::HandleDeleteForeignSession,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("getForeignSessions",
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ForeignSessionHandler::HandleGetForeignSessions,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("openForeignSession",
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ForeignSessionHandler::HandleOpenForeignSession,
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("setForeignSessionCollapsed",
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ForeignSessionHandler::HandleSetForeignSessionCollapsed,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->RegisterMessageCallback("showOtherDeviceSessionPopup",
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ForeignSessionHandler::HandleShowOtherDeviceSessionPopup,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Unretained(this)));
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::Init() {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui());
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileSyncService* service =
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<ProfileSyncService>(service));
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the data source for synced favicons.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ChromeURLDataManager::AddDataSource(profile,
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionFaviconSource(profile));
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::Observe(
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ListValue list_value;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Tab sync is disabled, so clean up data about collapsed sessions.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Profile::FromWebUI(web_ui())->GetPrefs()->ClearPref(
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          prefs::kNtpCollapsedForeignSessions);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Fall through.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HandleGetForeignSessions(&list_value);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ForeignSessionHandler::IsTabSyncEnabled() {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromWebUI(web_ui());
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileSyncService* service =
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return service && service->GetPreferredDataTypes().Has(syncer::SESSIONS);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ForeignSessionHandler::FormatSessionTime(const base::Time& time) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return a time like "1 hour ago", "2 days ago", etc.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TimeFormat::TimeElapsed(base::Time::Now() - time);
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::HandleGetForeignSessions(const ListValue* args) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionModelAssociator* associator = GetModelAssociator(web_ui());
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<const SyncedSession*> sessions;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ListValue session_list;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (associator && associator->GetAllForeignSessions(&sessions)) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Sort sessions from most recent to least recent.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Use a pref to keep track of sessions that were collapsed by the user.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // To prevent the pref from accumulating stale sessions, clear it each time
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and only add back sessions that are still current.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryPrefUpdate pref_update(Profile::FromWebUI(web_ui())->GetPrefs(),
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     prefs::kNtpCollapsedForeignSessions);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* current_collapsed_sessions = pref_update.Get();
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<DictionaryValue> collapsed_sessions(
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_collapsed_sessions->DeepCopy());
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_collapsed_sessions->Clear();
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Note: we don't own the SyncedSessions themselves.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < sessions.size() && i < kMaxSessionsToShow; ++i) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const SyncedSession* session = sessions[i];
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& session_tag = session->session_tag;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scoped_ptr<DictionaryValue> session_data(new DictionaryValue());
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->SetString("tag", session_tag);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->SetString("name", session->session_name);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->SetString("deviceType", session->DeviceTypeAsString());
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->SetString("modifiedTime",
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              FormatSessionTime(session->modified_time));
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bool is_collapsed = collapsed_sessions->HasKey(session_tag);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->SetBoolean("collapsed", is_collapsed);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (is_collapsed)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_collapsed_sessions->SetBoolean(session_tag, true);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scoped_ptr<ListValue> window_list(new ListValue());
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (SyncedSession::SyncedWindowMap::const_iterator it =
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           session->windows.begin(); it != session->windows.end(); ++it) {
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SessionWindow* window = it->second;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scoped_ptr<DictionaryValue> window_data(new DictionaryValue());
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (SessionWindowToValue(*window, window_data.get()))
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          window_list->Append(window_data.release());
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_data->Set("windows", window_list.release());
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_list.Append(session_data.release());
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::FundamentalValue tab_sync_enabled(IsTabSyncEnabled());
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web_ui()->CallJavascriptFunction("ntp.setForeignSessions",
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   session_list,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   tab_sync_enabled);
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::HandleOpenForeignSession(const ListValue* args) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t num_args = args->GetSize();
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Expect either 1 or 8 args. For restoring an entire session, only
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // one argument is required -- the session tag. To restore a tab,
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the additional args required are the window id, the tab id,
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and 4 properties of the event object (button, altKey, ctrlKey,
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // metaKey, shiftKey) for determining how to open the tab.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_args != 8U && num_args != 1U) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "openForeignSession called with " << args->GetSize()
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << " arguments.";
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract the session tag (always provided).
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string session_string_value;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetString(0, &session_string_value)) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to extract session tag.";
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract window number.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string window_num_str;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int window_num = kInvalidId;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !base::StringToInt(window_num_str, &window_num))) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to extract window number.";
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract tab id.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string tab_id_str;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionID::id_type tab_id = kInvalidId;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_args >= 3 && (!args->GetString(2, &tab_id_str) ||
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !base::StringToInt(tab_id_str, &tab_id))) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to extract tab SessionID.";
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_id != kInvalidId) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WindowOpenDisposition disposition =
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        web_ui_util::GetDispositionFromClick(args, 3);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OpenForeignSessionTab(
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        web_ui(), session_string_value, window_num, tab_id, disposition);
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OpenForeignSessionWindows(web_ui(), session_string_value, window_num);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::HandleDeleteForeignSession(const ListValue* args) {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (args->GetSize() != 1U) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Wrong number of args to deleteForeignSession";
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the session tag argument (required).
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string session_tag;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetString(0, &session_tag)) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Unable to extract session tag";
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionModelAssociator* associator = GetModelAssociator(web_ui());
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (associator)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    associator->DeleteForeignSession(session_tag);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::HandleSetForeignSessionCollapsed(
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ListValue* args) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (args->GetSize() != 2U) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Wrong number of args to setForeignSessionCollapsed";
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the session tag argument (required).
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string session_tag;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetString(0, &session_tag)) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Unable to extract session tag";
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_collapsed;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetBoolean(1, &is_collapsed)) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Unable to extract boolean argument";
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Store session tags for collapsed sessions in a preference so that the
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // collapsed state persists.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_collapsed)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update.Get()->SetBoolean(session_tag, true);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update.Get()->Remove(session_tag, NULL);
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ForeignSessionHandler::HandleShowOtherDeviceSessionPopup(
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ListValue* args) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Exclude Android until an OtherDeviceMenu view is implemented for it.
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #if !defined(OS_ANDROID)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetSize() == 3U);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract the session tag.
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string session_string_value;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetString(0, &session_string_value));
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract horizontal coordinate of the click relative to the origin of the
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // screen coordinate system.
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double screen_x;
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetDouble(1, &screen_x));
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract vertical coordinate of the click relative to the origin of the
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // screen coordinate system.
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double screen_y;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(args->GetDouble(2, &screen_y));
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OtherDeviceMenuController* menu = new OtherDeviceMenuController(
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      web_ui(),
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_string_value, gfx::Point(screen_x, screen_y));
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->ShowMenu();
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #endif
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ForeignSessionHandler::SessionWindowToValue(
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SessionWindow& window,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* dictionary) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (window.tabs.empty()) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<ListValue> tab_values(new ListValue());
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calculate the last |modification_time| for all entries within a window.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time modification_time = window.timestamp;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < window.tabs.size(); ++i) {
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<DictionaryValue> tab_value(new DictionaryValue());
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (SessionTabToValue(*window.tabs[i], tab_value.get())) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modification_time = std::max(modification_time,
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   window.tabs[i]->timestamp);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_values->Append(tab_value.release());
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_values->GetSize() == 0)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetString("type", "window");
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetDouble("timestamp", modification_time.ToInternalValue());
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::TimeDelta last_synced = base::Time::Now() - modification_time;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If clock skew leads to a future time, or we last synced less than a minute
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ago, output "Just now".
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetString("userVisibleTimestamp",
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_synced < base::TimeDelta::FromMinutes(1) ?
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW) :
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          TimeFormat::TimeElapsed(last_synced));
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->SetInteger("sessionId", window.window_id.id());
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->Set("tabs", tab_values.release());
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace browser_sync
437