// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/foreign_session_handler.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/memory/scoped_vector.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/ui/webui/new_tab_ui.h"
#include "content/common/notification_details.h"
#include "content/common/notification_service.h"
#include "chrome/common/url_constants.h"
namespace browser_sync {
// Maximum number of session we're going to display on the NTP
static const int kMaxSessionsToShow = 10;
// Invalid value, used to note that we don't have a tab or window number.
static const int kInvalidId = -1;
ForeignSessionHandler::ForeignSessionHandler() {
Init();
}
void ForeignSessionHandler::RegisterMessages() {
web_ui_->RegisterMessageCallback("getForeignSessions",
NewCallback(this,
&ForeignSessionHandler::HandleGetForeignSessions));
web_ui_->RegisterMessageCallback("openForeignSession",
NewCallback(this,
&ForeignSessionHandler::HandleOpenForeignSession));
}
void ForeignSessionHandler::Init() {
registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
NotificationService::AllSources());
registrar_.Add(this, NotificationType::FOREIGN_SESSION_UPDATED,
NotificationService::AllSources());
registrar_.Add(this, NotificationType::FOREIGN_SESSION_DISABLED,
NotificationService::AllSources());
}
void ForeignSessionHandler::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
ListValue list_value;
switch (type.value) {
case NotificationType::SYNC_CONFIGURE_DONE:
case NotificationType::FOREIGN_SESSION_UPDATED:
HandleGetForeignSessions(&list_value);
break;
case NotificationType::FOREIGN_SESSION_DISABLED:
// Calling foreignSessions with empty list will automatically hide
// foreign session section.
web_ui_->CallJavascriptFunction("foreignSessions", list_value);
break;
default:
NOTREACHED();
}
}
SessionModelAssociator* ForeignSessionHandler::GetModelAssociator() {
ProfileSyncService* service = web_ui_->GetProfile()->GetProfileSyncService();
if (service == NULL)
return NULL;
// We only want to set the model associator if there is one, and it is done
// syncing sessions.
SessionModelAssociator* model_associator = service->
GetSessionModelAssociator();
if (model_associator == NULL ||
!service->ShouldPushChanges()) {
return NULL;
}
return web_ui_->GetProfile()->GetProfileSyncService()->
GetSessionModelAssociator();
}
void ForeignSessionHandler::HandleGetForeignSessions(const ListValue* args) {
SessionModelAssociator* associator = GetModelAssociator();
std::vector<const ForeignSession*> sessions;
if (associator == NULL) {
// Called before associator created, exit.
return;
}
// Note: we don't own the ForeignSessions themselves.
if (!associator->GetAllForeignSessions(&sessions)) {
LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
"SessionModelAssociator.";
return;
}
int added_count = 0;
ListValue session_list;
for (std::vector<const ForeignSession*>::const_iterator i =
sessions.begin(); i != sessions.end() &&
added_count < kMaxSessionsToShow; ++i) {
const ForeignSession* foreign_session = *i;
scoped_ptr<ListValue> window_list(new ListValue());
for (std::vector<SessionWindow*>::const_iterator it =
foreign_session->windows.begin(); it != foreign_session->windows.end();
++it) {
SessionWindow* window = *it;
scoped_ptr<DictionaryValue> window_data(new DictionaryValue());
if (SessionWindowToValue(*window, window_data.get())) {
window_data->SetString("sessionTag",
foreign_session->foreign_session_tag);
// Give ownership to |list_value|.
window_list->Append(window_data.release());
}
}
added_count++;
// Give ownership to |session_list|.
session_list.Append(window_list.release());
}
web_ui_->CallJavascriptFunction("foreignSessions", session_list);
}
void ForeignSessionHandler::HandleOpenForeignSession(
const ListValue* args) {
size_t num_args = args->GetSize();
if (num_args > 3U || num_args == 0) {
LOG(ERROR) << "openForeignWindow called with only " << args->GetSize()
<< " arguments.";
return;
}
// Extract the machine tag (always provided).
std::string session_string_value;
if (!args->GetString(0, &session_string_value)) {
LOG(ERROR) << "Failed to extract session tag.";
return;
}
// Extract window number.
std::string window_num_str;
int window_num = kInvalidId;
if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
!base::StringToInt(window_num_str, &window_num))) {
LOG(ERROR) << "Failed to extract window number.";
return;
}
// Extract tab id.
std::string tab_id_str;
SessionID::id_type tab_id = kInvalidId;
if (num_args == 3 && (!args->GetString(2, &tab_id_str) ||
!base::StringToInt(tab_id_str, &tab_id))) {
LOG(ERROR) << "Failed to extract tab SessionID.";
return;
}
SessionModelAssociator* associator = GetModelAssociator();
if (tab_id != kInvalidId) {
// We don't actually care about |window_num|, this is just a sanity check.
DCHECK_LT(kInvalidId, window_num);
const SessionTab* tab;
if (!associator->GetForeignTab(session_string_value, tab_id, &tab)) {
LOG(ERROR) << "Failed to load foreign tab.";
return;
}
SessionRestore::RestoreForeignSessionTab(web_ui_->GetProfile(), *tab);
} else {
std::vector<SessionWindow*> windows;
// Note: we don't own the ForeignSessions themselves.
if (!associator->GetForeignSession(session_string_value, &windows)) {
LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
"SessionModelAssociator.";
return;
}
std::vector<SessionWindow*>::const_iterator iter_begin = windows.begin() +
((window_num == kInvalidId) ? 0 : window_num);
std::vector<SessionWindow*>::const_iterator iter_end =
((window_num == kInvalidId) ?
std::vector<SessionWindow*>::const_iterator(windows.end()) :
iter_begin+1);
SessionRestore::RestoreForeignSessionWindows(web_ui_->GetProfile(),
iter_begin,
iter_end);
}
}
bool ForeignSessionHandler::SessionTabToValue(
const SessionTab& tab,
DictionaryValue* dictionary) {
if (tab.navigations.empty())
return false;
int selected_index = tab.current_navigation_index;
selected_index = std::max(
0,
std::min(selected_index,
static_cast<int>(tab.navigations.size() - 1)));
const TabNavigation& current_navigation =
tab.navigations.at(selected_index);
if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL))
return false;
NewTabUI::SetURLTitleAndDirection(dictionary, current_navigation.title(),
current_navigation.virtual_url());
dictionary->SetString("type", "tab");
dictionary->SetDouble("timestamp",
static_cast<double>(tab.timestamp.ToInternalValue()));
dictionary->SetInteger("sessionId", tab.tab_id.id());
return true;
}
bool ForeignSessionHandler::SessionWindowToValue(
const SessionWindow& window,
DictionaryValue* dictionary) {
if (window.tabs.empty()) {
NOTREACHED();
return false;
}
scoped_ptr<ListValue> tab_values(new ListValue());
for (size_t i = 0; i < window.tabs.size(); ++i) {
scoped_ptr<DictionaryValue> tab_value(new DictionaryValue());
if (SessionTabToValue(*window.tabs[i], tab_value.get()))
tab_values->Append(tab_value.release());
}
if (tab_values->GetSize() == 0)
return false;
dictionary->SetString("type", "window");
dictionary->SetDouble("timestamp",
static_cast<double>(window.timestamp.ToInternalValue()));
dictionary->SetInteger("sessionId", window.window_id.id());
dictionary->Set("tabs", tab_values.release());
return true;
}
} // namespace browser_sync