Home | History | Annotate | Download | only in android
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/android/foreign_session_helper.h"
      6 
      7 #include <jni.h>
      8 
      9 #include "base/android/jni_string.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/prefs/scoped_user_pref_update.h"
     12 #include "chrome/browser/android/tab_android.h"
     13 #include "chrome/browser/chrome_notification_types.h"
     14 #include "chrome/browser/profiles/profile_android.h"
     15 #include "chrome/browser/sessions/session_restore.h"
     16 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
     17 #include "chrome/browser/sync/profile_sync_service.h"
     18 #include "chrome/browser/sync/profile_sync_service_factory.h"
     19 #include "chrome/browser/ui/android/tab_model/tab_model.h"
     20 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
     21 #include "chrome/common/pref_names.h"
     22 #include "chrome/common/url_constants.h"
     23 #include "content/public/browser/notification_source.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "jni/ForeignSessionHelper_jni.h"
     26 
     27 using base::android::ScopedJavaGlobalRef;
     28 using base::android::ScopedJavaLocalRef;
     29 using base::android::AttachCurrentThread;
     30 using base::android::ConvertUTF16ToJavaString;
     31 using base::android::ConvertUTF8ToJavaString;
     32 using base::android::ConvertJavaStringToUTF8;
     33 using browser_sync::OpenTabsUIDelegate;
     34 using browser_sync::SyncedSession;
     35 
     36 namespace {
     37 
     38 OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
     39   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
     40       GetForProfile(profile);
     41 
     42   // Only return the delegate if it exists and it is done syncing sessions.
     43   if (!service || !service->ShouldPushChanges())
     44     return NULL;
     45 
     46   return service->GetOpenTabsUIDelegate();
     47 }
     48 
     49 bool ShouldSkipTab(const SessionTab& session_tab) {
     50     if (session_tab.navigations.empty())
     51       return true;
     52 
     53     int selected_index = session_tab.normalized_navigation_index();
     54     const ::sessions::SerializedNavigationEntry& current_navigation =
     55         session_tab.navigations.at(selected_index);
     56 
     57     if (current_navigation.virtual_url().is_empty())
     58       return true;
     59 
     60     return false;
     61 }
     62 
     63 bool ShouldSkipWindow(const SessionWindow& window) {
     64   for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
     65       tab_it != window.tabs.end(); ++tab_it) {
     66     const SessionTab &session_tab = **tab_it;
     67     if (!ShouldSkipTab(session_tab))
     68       return false;
     69   }
     70   return true;
     71 }
     72 
     73 bool ShouldSkipSession(const browser_sync::SyncedSession& session) {
     74   for (SyncedSession::SyncedWindowMap::const_iterator it =
     75       session.windows.begin(); it != session.windows.end(); ++it) {
     76     const SessionWindow  &window = *(it->second);
     77     if (!ShouldSkipWindow(window))
     78       return false;
     79   }
     80   return true;
     81 }
     82 
     83 void CopyTabsToJava(
     84     JNIEnv* env,
     85     const SessionWindow& window,
     86     ScopedJavaLocalRef<jobject>& j_window) {
     87   for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
     88       tab_it != window.tabs.end(); ++tab_it) {
     89     const SessionTab &session_tab = **tab_it;
     90 
     91     if (ShouldSkipTab(session_tab))
     92       continue;
     93 
     94     int selected_index = session_tab.normalized_navigation_index();
     95     DCHECK(selected_index >= 0);
     96     DCHECK(selected_index < static_cast<int>(session_tab.navigations.size()));
     97 
     98     const ::sessions::SerializedNavigationEntry& current_navigation =
     99         session_tab.navigations.at(selected_index);
    100 
    101     GURL tab_url = current_navigation.virtual_url();
    102 
    103     Java_ForeignSessionHelper_pushTab(
    104         env, j_window.obj(),
    105         ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
    106         ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
    107         session_tab.timestamp.ToJavaTime(),
    108         session_tab.tab_id.id());
    109   }
    110 }
    111 
    112 void CopyWindowsToJava(
    113     JNIEnv* env,
    114     const SyncedSession& session,
    115     ScopedJavaLocalRef<jobject>& j_session) {
    116   for (SyncedSession::SyncedWindowMap::const_iterator it =
    117       session.windows.begin(); it != session.windows.end(); ++it) {
    118     const SessionWindow &window = *(it->second);
    119 
    120     if (ShouldSkipWindow(window))
    121       continue;
    122 
    123     ScopedJavaLocalRef<jobject> last_pushed_window;
    124     last_pushed_window.Reset(
    125         Java_ForeignSessionHelper_pushWindow(
    126             env, j_session.obj(),
    127             window.timestamp.ToJavaTime(),
    128             window.window_id.id()));
    129 
    130     CopyTabsToJava(env, window, last_pushed_window);
    131   }
    132 }
    133 
    134 }  // namespace
    135 
    136 static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
    137   ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
    138       ProfileAndroid::FromProfileAndroid(profile));
    139   return reinterpret_cast<intptr_t>(foreign_session_helper);
    140 }
    141 
    142 ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
    143     : profile_(profile) {
    144   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
    145       GetForProfile(profile);
    146   registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
    147                  content::Source<ProfileSyncService>(service));
    148   registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
    149                  content::Source<Profile>(profile));
    150   registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
    151                  content::Source<Profile>(profile));
    152 }
    153 
    154 ForeignSessionHelper::~ForeignSessionHelper() {
    155 }
    156 
    157 void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
    158   delete this;
    159 }
    160 
    161 jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) {
    162   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
    163       GetForProfile(profile_);
    164   return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
    165 }
    166 
    167 void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
    168                                                        jobject obj,
    169                                                        jobject callback) {
    170   callback_.Reset(env, callback);
    171 }
    172 
    173 void ForeignSessionHelper::Observe(
    174     int type, const content::NotificationSource& source,
    175     const content::NotificationDetails& details) {
    176   if (callback_.is_null())
    177     return;
    178 
    179   JNIEnv* env = AttachCurrentThread();
    180 
    181   switch (type) {
    182     case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
    183       // Tab sync is disabled, so clean up data about collapsed sessions.
    184       profile_->GetPrefs()->ClearPref(
    185           prefs::kNtpCollapsedForeignSessions);
    186       // Purposeful fall through.
    187     case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
    188     case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
    189       Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
    190       break;
    191     default:
    192       NOTREACHED();
    193   }
    194 }
    195 
    196 jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
    197                                                   jobject obj,
    198                                                   jobject result) {
    199   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
    200   if (!open_tabs)
    201     return false;
    202 
    203   std::vector<const browser_sync::SyncedSession*> sessions;
    204   if (!open_tabs->GetAllForeignSessions(&sessions))
    205     return false;
    206 
    207   // Use a pref to keep track of sessions that were collapsed by the user.
    208   // To prevent the pref from accumulating stale sessions, clear it each time
    209   // and only add back sessions that are still current.
    210   DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
    211                                    prefs::kNtpCollapsedForeignSessions);
    212   base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
    213   scoped_ptr<base::DictionaryValue> collapsed_sessions(
    214       pref_collapsed_sessions->DeepCopy());
    215   pref_collapsed_sessions->Clear();
    216 
    217   ScopedJavaLocalRef<jobject> last_pushed_session;
    218   ScopedJavaLocalRef<jobject> last_pushed_window;
    219 
    220   // Note: we don't own the SyncedSessions themselves.
    221   for (size_t i = 0; i < sessions.size(); ++i) {
    222     const browser_sync::SyncedSession &session = *(sessions[i]);
    223     if (ShouldSkipSession(session))
    224       continue;
    225 
    226     const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
    227 
    228     if (is_collapsed)
    229       pref_collapsed_sessions->SetBoolean(session.session_tag, true);
    230 
    231     last_pushed_session.Reset(
    232         Java_ForeignSessionHelper_pushSession(
    233             env,
    234             result,
    235             ConvertUTF8ToJavaString(env, session.session_tag).obj(),
    236             ConvertUTF8ToJavaString(env, session.session_name).obj(),
    237             session.device_type,
    238             session.modified_time.ToJavaTime()));
    239 
    240     CopyWindowsToJava(env, session, last_pushed_session);
    241   }
    242 
    243   return true;
    244 }
    245 
    246 jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
    247                                                      jobject obj,
    248                                                      jobject j_tab,
    249                                                      jstring session_tag,
    250                                                      jint session_tab_id,
    251                                                      jint j_disposition) {
    252   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
    253   if (!open_tabs) {
    254     LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
    255     return false;
    256   }
    257 
    258   const SessionTab* session_tab;
    259 
    260   if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
    261                                 session_tab_id,
    262                                 &session_tab)) {
    263     LOG(ERROR) << "Failed to load foreign tab.";
    264     return false;
    265   }
    266 
    267   if (session_tab->navigations.empty()) {
    268     LOG(ERROR) << "Foreign tab no longer has valid navigations.";
    269     return false;
    270   }
    271 
    272   TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
    273   if (!tab_android)
    274     return false;
    275   content::WebContents* web_contents = tab_android->web_contents();
    276   if (!web_contents)
    277     return false;
    278 
    279   WindowOpenDisposition disposition =
    280       static_cast<WindowOpenDisposition>(j_disposition);
    281   SessionRestore::RestoreForeignSessionTab(web_contents,
    282                                            *session_tab,
    283                                            disposition);
    284 
    285   return true;
    286 }
    287 
    288 void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
    289                                                 jstring session_tag) {
    290   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
    291   if (open_tabs)
    292     open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
    293 }
    294 
    295 // static
    296 bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
    297   return RegisterNativesImpl(env);
    298 }
    299