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.
5 #include "chrome/browser/android/foreign_session_helper.h"
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"
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
;
38 OpenTabsUIDelegate
* GetOpenTabsUIDelegate(Profile
* profile
) {
39 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
40 GetForProfile(profile
);
42 // Only return the delegate if it exists and it is done syncing sessions.
43 if (!service
|| !service
->ShouldPushChanges())
46 return service
->GetOpenTabsUIDelegate();
49 bool ShouldSkipTab(const SessionTab
& session_tab
) {
50 if (session_tab
.navigations
.empty())
53 int selected_index
= session_tab
.current_navigation_index
;
54 if (selected_index
< 0 ||
55 selected_index
>= static_cast<int>(session_tab
.navigations
.size()))
58 const ::sessions::SerializedNavigationEntry
& current_navigation
=
59 session_tab
.navigations
.at(selected_index
);
61 if (current_navigation
.virtual_url().is_empty())
67 bool ShouldSkipWindow(const SessionWindow
& window
) {
68 for (std::vector
<SessionTab
*>::const_iterator tab_it
= window
.tabs
.begin();
69 tab_it
!= window
.tabs
.end(); ++tab_it
) {
70 const SessionTab
&session_tab
= **tab_it
;
71 if (!ShouldSkipTab(session_tab
))
77 bool ShouldSkipSession(const browser_sync::SyncedSession
& session
) {
78 for (SyncedSession::SyncedWindowMap::const_iterator it
=
79 session
.windows
.begin(); it
!= session
.windows
.end(); ++it
) {
80 const SessionWindow
&window
= *(it
->second
);
81 if (!ShouldSkipWindow(window
))
89 const SessionWindow
& window
,
90 ScopedJavaLocalRef
<jobject
>& j_window
) {
91 for (std::vector
<SessionTab
*>::const_iterator tab_it
= window
.tabs
.begin();
92 tab_it
!= window
.tabs
.end(); ++tab_it
) {
93 const SessionTab
&session_tab
= **tab_it
;
95 if (ShouldSkipTab(session_tab
))
98 int selected_index
= session_tab
.current_navigation_index
;
99 DCHECK(selected_index
>= 0);
100 DCHECK(selected_index
< static_cast<int>(session_tab
.navigations
.size()));
102 const ::sessions::SerializedNavigationEntry
& current_navigation
=
103 session_tab
.navigations
.at(selected_index
);
105 GURL tab_url
= current_navigation
.virtual_url();
107 Java_ForeignSessionHelper_pushTab(
109 ConvertUTF8ToJavaString(env
, tab_url
.spec()).obj(),
110 ConvertUTF16ToJavaString(env
, current_navigation
.title()).obj(),
111 session_tab
.timestamp
.ToJavaTime(),
112 session_tab
.tab_id
.id());
116 void CopyWindowsToJava(
118 const SyncedSession
& session
,
119 ScopedJavaLocalRef
<jobject
>& j_session
) {
120 for (SyncedSession::SyncedWindowMap::const_iterator it
=
121 session
.windows
.begin(); it
!= session
.windows
.end(); ++it
) {
122 const SessionWindow
&window
= *(it
->second
);
124 if (ShouldSkipWindow(window
))
127 ScopedJavaLocalRef
<jobject
> last_pushed_window
;
128 last_pushed_window
.Reset(
129 Java_ForeignSessionHelper_pushWindow(
130 env
, j_session
.obj(),
131 window
.timestamp
.ToJavaTime(),
132 window
.window_id
.id()));
134 CopyTabsToJava(env
, window
, last_pushed_window
);
140 static jlong
Init(JNIEnv
* env
, jclass clazz
, jobject profile
) {
141 ForeignSessionHelper
* foreign_session_helper
= new ForeignSessionHelper(
142 ProfileAndroid::FromProfileAndroid(profile
));
143 return reinterpret_cast<intptr_t>(foreign_session_helper
);
146 ForeignSessionHelper::ForeignSessionHelper(Profile
* profile
)
147 : profile_(profile
) {
148 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
149 GetForProfile(profile
);
150 registrar_
.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
,
151 content::Source
<ProfileSyncService
>(service
));
152 registrar_
.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
,
153 content::Source
<Profile
>(profile
));
154 registrar_
.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED
,
155 content::Source
<Profile
>(profile
));
158 ForeignSessionHelper::~ForeignSessionHelper() {
161 void ForeignSessionHelper::Destroy(JNIEnv
* env
, jobject obj
) {
165 jboolean
ForeignSessionHelper::IsTabSyncEnabled(JNIEnv
* env
, jobject obj
) {
166 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
167 GetForProfile(profile_
);
168 return service
&& service
->GetActiveDataTypes().Has(syncer::PROXY_TABS
);
171 void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv
* env
,
174 callback_
.Reset(env
, callback
);
177 void ForeignSessionHelper::Observe(
178 int type
, const content::NotificationSource
& source
,
179 const content::NotificationDetails
& details
) {
180 if (callback_
.is_null())
183 JNIEnv
* env
= AttachCurrentThread();
186 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED
:
187 // Tab sync is disabled, so clean up data about collapsed sessions.
188 profile_
->GetPrefs()->ClearPref(
189 prefs::kNtpCollapsedForeignSessions
);
190 // Purposeful fall through.
191 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
:
192 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
:
193 Java_ForeignSessionCallback_onUpdated(env
, callback_
.obj());
200 jboolean
ForeignSessionHelper::GetForeignSessions(JNIEnv
* env
,
203 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
207 std::vector
<const browser_sync::SyncedSession
*> sessions
;
208 if (!open_tabs
->GetAllForeignSessions(&sessions
))
211 // Use a pref to keep track of sessions that were collapsed by the user.
212 // To prevent the pref from accumulating stale sessions, clear it each time
213 // and only add back sessions that are still current.
214 DictionaryPrefUpdate
pref_update(profile_
->GetPrefs(),
215 prefs::kNtpCollapsedForeignSessions
);
216 base::DictionaryValue
* pref_collapsed_sessions
= pref_update
.Get();
217 scoped_ptr
<base::DictionaryValue
> collapsed_sessions(
218 pref_collapsed_sessions
->DeepCopy());
219 pref_collapsed_sessions
->Clear();
221 ScopedJavaLocalRef
<jobject
> last_pushed_session
;
222 ScopedJavaLocalRef
<jobject
> last_pushed_window
;
224 // Note: we don't own the SyncedSessions themselves.
225 for (size_t i
= 0; i
< sessions
.size(); ++i
) {
226 const browser_sync::SyncedSession
&session
= *(sessions
[i
]);
227 if (ShouldSkipSession(session
))
230 const bool is_collapsed
= collapsed_sessions
->HasKey(session
.session_tag
);
233 pref_collapsed_sessions
->SetBoolean(session
.session_tag
, true);
235 last_pushed_session
.Reset(
236 Java_ForeignSessionHelper_pushSession(
239 ConvertUTF8ToJavaString(env
, session
.session_tag
).obj(),
240 ConvertUTF8ToJavaString(env
, session
.session_name
).obj(),
242 session
.modified_time
.ToJavaTime()));
244 CopyWindowsToJava(env
, session
, last_pushed_session
);
250 jboolean
ForeignSessionHelper::OpenForeignSessionTab(JNIEnv
* env
,
255 jint j_disposition
) {
256 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
258 LOG(ERROR
) << "Null OpenTabsUIDelegate returned.";
262 const SessionTab
* session_tab
;
264 if (!open_tabs
->GetForeignTab(ConvertJavaStringToUTF8(env
, session_tag
),
267 LOG(ERROR
) << "Failed to load foreign tab.";
271 if (session_tab
->navigations
.empty()) {
272 LOG(ERROR
) << "Foreign tab no longer has valid navigations.";
276 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, j_tab
);
279 content::WebContents
* web_contents
= tab_android
->web_contents();
283 WindowOpenDisposition disposition
=
284 static_cast<WindowOpenDisposition
>(j_disposition
);
285 SessionRestore::RestoreForeignSessionTab(web_contents
,
292 void ForeignSessionHelper::DeleteForeignSession(JNIEnv
* env
, jobject obj
,
293 jstring session_tag
) {
294 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
296 open_tabs
->DeleteForeignSession(ConvertJavaStringToUTF8(env
, session_tag
));
300 bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv
* env
) {
301 return RegisterNativesImpl(env
);