Chromium Code Reviews| Index: chrome/browser/android/foreign_session_helper.cc |
| diff --git a/chrome/browser/android/foreign_session_helper.cc b/chrome/browser/android/foreign_session_helper.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..223e91eeee12d09a929e6dfc50387cb2ff0af5a6 |
| --- /dev/null |
| +++ b/chrome/browser/android/foreign_session_helper.cc |
| @@ -0,0 +1,253 @@ |
| +// Copyright 2013 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/android/foreign_session_helper.h" |
| + |
| +#include <jni.h> |
| + |
| +#include "base/android/jni_string.h" |
| + |
| +#include "chrome/browser/chrome_notification_types.h" |
| +#include "chrome/browser/prefs/scoped_user_pref_update.h" |
| +#include "chrome/browser/profiles/profile_android.h" |
| +#include "chrome/browser/sync/glue/session_model_associator.h" |
| +#include "chrome/browser/sync/profile_sync_service.h" |
| +#include "chrome/browser/sync/profile_sync_service_factory.h" |
| +#include "chrome/browser/ui/android/tab_model/tab_model.h" |
| +#include "chrome/browser/ui/android/tab_model/tab_model_list.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "chrome/common/url_constants.h" |
| + |
| +#include "content/public/browser/notification_source.h" |
| +#include "content/public/browser/user_metrics.h" |
| +#include "content/public/browser/web_contents.h" |
| + |
| +#include "jni/ForeignSessionHelper_jni.h" |
| + |
| +using base::android::ScopedJavaGlobalRef; |
| +using base::android::AttachCurrentThread; |
| +using base::android::ConvertUTF16ToJavaString; |
| +using base::android::ConvertUTF8ToJavaString; |
| +using base::android::ConvertJavaStringToUTF8; |
| +using browser_sync::SessionModelAssociator; |
| +using browser_sync::SyncedSession; |
| + |
| +static jint Init(JNIEnv* env, jclass clazz, jobject profile) { |
| + ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper( |
| + ProfileAndroid::FromProfileAndroid(profile)); |
| + return reinterpret_cast<jint>(foreign_session_helper); |
| +} |
| + |
| +ForeignSessionHelper::ForeignSessionHelper(Profile* profile) { |
| + profile_ = profile; |
|
Ted C
2013/07/24 23:48:20
can you use an initializer list here instead?
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + callback_.Reset(); |
|
Ted C
2013/07/24 23:48:20
is this necessary?
Kibeom Kim (inactive)
2013/07/25 21:18:23
No :( Done.
|
| + |
| + ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance() |
| + ->GetForProfile(profile); |
|
Ted C
2013/07/24 23:48:20
having the -> on the previous line seems much more
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + |
| + registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE, |
| + content::Source<ProfileSyncService>(service)); |
| + registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED, |
| + content::Source<Profile>(profile)); |
| + registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED, |
| + content::Source<Profile>(profile)); |
| +} |
| + |
| +void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) { |
| + delete this; |
| +} |
| + |
| +jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) { |
| + ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance() |
| + ->GetForProfile(profile_); |
|
Ted C
2013/07/24 23:48:20
same comment as above
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS); |
| +} |
| + |
| +void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env, jobject obj, |
|
Ted C
2013/07/24 23:48:20
I think if all the params don't fit on one line, t
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + jobject callback) { |
| + callback_.Reset(env, callback); |
| +} |
| + |
| +void ForeignSessionHelper::Observe( |
| + int type, const content::NotificationSource& source, |
| + const content::NotificationDetails& details) { |
| + if (callback_.is_null()) |
| + return; |
| + |
| + JNIEnv* env = AttachCurrentThread(); |
| + |
| + switch (type) { |
| + case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED: |
| + // Tab sync is disabled, so clean up data about collapsed sessions. |
| + profile_->GetPrefs()->ClearPref( |
| + prefs::kNtpCollapsedForeignSessions); |
| + // Purposeful fall through. |
| + case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE: |
| + case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED: |
| + Java_OnForeignSessionCallback_onUpdated(env, callback_.obj()); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env, jobject obj, |
| + jobject result) { |
| + SessionModelAssociator* associator = GetSessionModelAssociator(); |
| + if (associator == NULL) |
|
Ted C
2013/07/24 23:48:20
if (!associator)
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + return false; |
| + |
| + std::vector<const browser_sync::SyncedSession*> sessions; |
| + if (!associator->GetAllForeignSessions(&sessions)) |
| + return false; |
| + |
| + // Use a pref to keep track of sessions that were collapsed by the user. |
| + // To prevent the pref from accumulating stale sessions, clear it each time |
| + // and only add back sessions that are still current. |
| + DictionaryPrefUpdate pref_update(profile_->GetPrefs(), |
| + prefs::kNtpCollapsedForeignSessions); |
| + DictionaryValue* current_collapsed_sessions = pref_update.Get(); |
| + scoped_ptr<DictionaryValue> collapsed_sessions( |
| + current_collapsed_sessions->DeepCopy()); |
| + current_collapsed_sessions->Clear(); |
| + |
| + // Note: we don't own the SyncedSessions themselves. |
| + for (size_t i = 0; i < sessions.size(); ++i) { |
| + const browser_sync::SyncedSession* session = sessions[i]; |
| + |
| + const bool is_collapsed = collapsed_sessions->HasKey(session->session_tag); |
| + |
| + if (is_collapsed) |
| + current_collapsed_sessions->SetBoolean(session->session_tag, true); |
| + |
| + Java_ForeignSessionHelper_pushSession( |
| + env, result, |
| + ConvertUTF8ToJavaString(env, session->session_tag).Release(), |
| + ConvertUTF8ToJavaString(env, session->session_name).Release(), |
| + ConvertUTF8ToJavaString(env, session->DeviceTypeAsString()).Release(), |
| + session->modified_time.ToInternalValue()); |
| + |
| + for (SyncedSession::SyncedWindowMap::const_iterator it = |
| + session->windows.begin(); it != session->windows.end(); ++it) { |
| + SessionWindow* window = it->second; |
| + |
| + if (window->tabs.empty()) { |
| + NOTREACHED(); |
| + continue; |
| + } |
| + |
| + Java_ForeignSessionHelper_pushWindow( |
| + env, result, |
| + window->timestamp.ToInternalValue(), |
| + window->window_id.id()); |
| + |
| + for (std::vector<SessionTab*>::iterator iit = window->tabs.begin(); |
|
Ted C
2013/07/24 23:48:20
Might consider having helper methods for the inner
Kibeom Kim (inactive)
2013/07/25 21:18:23
Done.
|
| + iit != window->tabs.end(); ++iit) { |
| + const SessionTab &tab = **iit; |
| + |
| + if (tab.navigations.empty()) |
| + continue; |
| + |
| + int selected_index = std::min( |
| + tab.current_navigation_index, |
| + static_cast<int>(tab.navigations.size() - 1)); |
| + const ::sessions::SerializedNavigationEntry& current_navigation = |
| + tab.navigations.at(selected_index); |
| + |
| + GURL tab_url = current_navigation.virtual_url(); |
| + if (tab_url == GURL(chrome::kChromeUINewTabURL)) |
| + continue; |
| + |
| + Java_ForeignSessionHelper_pushTab( |
| + env, result, |
| + ConvertUTF8ToJavaString(env, tab_url.spec()).Release(), |
| + ConvertUTF16ToJavaString(env, current_navigation.title()).Release(), |
| + tab.timestamp.ToInternalValue(), |
| + tab.tab_id.id()); |
| + } |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env, jobject obj, |
| + jstring session_tag, |
| + jint tab_id) { |
| + content::RecordComputedAction("MobileNTPForeignSession"); |
| + |
| + SessionModelAssociator* associator = GetSessionModelAssociator(); |
| + if (!associator) { |
| + LOG(ERROR) << "Null SessionModelAssociator returned."; |
| + return false; |
| + } |
| + |
| + const SessionTab* tab; |
| + |
| + if (!associator->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag), |
| + tab_id, &tab)) { |
| + LOG(ERROR) << "Failed to load foreign tab."; |
| + return false; |
| + } |
| + |
| + if (tab->navigations.empty()) { |
| + LOG(ERROR) << "Foreign tab no longer has valid navigations."; |
| + return false; |
| + } |
| + |
| + TabModel* tab_model = TabModelList::GetTabModelWithProfile(profile_); |
| + DCHECK(tab_model); |
| + if (!tab_model) |
| + return false; |
| + |
| + std::vector<content::NavigationEntry*> entries = |
| + sessions::SerializedNavigationEntry::ToNavigationEntries( |
| + tab->navigations, profile_); |
| + content::WebContents* new_web_contents = content::WebContents::Create( |
| + content::WebContents::CreateParams(profile_)); |
| + int selected_index = tab->normalized_navigation_index(); |
| + new_web_contents->GetController().Restore( |
| + selected_index, |
| + content::NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, |
| + &entries); |
| + tab_model->CreateTab(new_web_contents); |
| + |
| + return true; |
| +} |
| + |
| +void ForeignSessionHelper::SetForeignSessionCollapsed(JNIEnv* env, jobject obj, |
| + jstring session_tag, |
| + jboolean is_collapsed) { |
| + // Store session tags for collapsed sessions in a preference so that the |
| + // collapsed state persists. |
| + PrefService* prefs = profile_->GetPrefs(); |
| + DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions); |
| + if (is_collapsed) |
| + update.Get()->SetBoolean(ConvertJavaStringToUTF8(env, session_tag), true); |
| + else |
| + update.Get()->Remove(ConvertJavaStringToUTF8(env, session_tag), NULL); |
| +} |
| + |
| +void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj, |
| + jstring session_tag) { |
| + SessionModelAssociator* associator = GetSessionModelAssociator(); |
| + if (associator) |
| + associator->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag)); |
| +} |
| + |
| +SessionModelAssociator* ForeignSessionHelper::GetSessionModelAssociator() { |
| + ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance() |
| + ->GetForProfile(profile_); |
| + |
| + // Only return the associator if it exists and it is done syncing sessions. |
| + if (!service || !service->ShouldPushChanges()) |
| + return NULL; |
| + |
| + return service->GetSessionModelAssociator(); |
| +} |
| + |
| +// static |
| +bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) { |
| + return RegisterNativesImpl(env); |
| +} |