// 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