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/profile_sync_service.h"
17 #include "chrome/browser/sync/profile_sync_service_factory.h"
18 #include "chrome/browser/ui/android/tab_model/tab_model.h"
19 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/common/url_constants.h"
22 #include "components/sync_driver/open_tabs_ui_delegate.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "jni/ForeignSessionHelper_jni.h"
29 using base::android::ScopedJavaGlobalRef
;
30 using base::android::ScopedJavaLocalRef
;
31 using base::android::AttachCurrentThread
;
32 using base::android::ConvertUTF16ToJavaString
;
33 using base::android::ConvertUTF8ToJavaString
;
34 using base::android::ConvertJavaStringToUTF8
;
35 using sync_driver::OpenTabsUIDelegate
;
36 using sync_driver::SyncedSession
;
40 OpenTabsUIDelegate
* GetOpenTabsUIDelegate(Profile
* profile
) {
41 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
42 GetForProfile(profile
);
44 // Only return the delegate if it exists and it is done syncing sessions.
45 if (!service
|| !service
->IsSyncActive())
48 return service
->GetOpenTabsUIDelegate();
51 bool ShouldSkipTab(const sessions::SessionTab
& session_tab
) {
52 if (session_tab
.navigations
.empty())
55 int selected_index
= session_tab
.normalized_navigation_index();
56 const sessions::SerializedNavigationEntry
& current_navigation
=
57 session_tab
.navigations
.at(selected_index
);
59 if (current_navigation
.virtual_url().is_empty())
65 bool ShouldSkipWindow(const sessions::SessionWindow
& window
) {
66 for (std::vector
<sessions::SessionTab
*>::const_iterator tab_it
=
67 window
.tabs
.begin(); tab_it
!= window
.tabs
.end(); ++tab_it
) {
68 const sessions::SessionTab
&session_tab
= **tab_it
;
69 if (!ShouldSkipTab(session_tab
))
75 bool ShouldSkipSession(const sync_driver::SyncedSession
& session
) {
76 for (SyncedSession::SyncedWindowMap::const_iterator it
=
77 session
.windows
.begin(); it
!= session
.windows
.end(); ++it
) {
78 const sessions::SessionWindow
&window
= *(it
->second
);
79 if (!ShouldSkipWindow(window
))
87 const sessions::SessionTab
& tab
,
88 ScopedJavaLocalRef
<jobject
>& j_window
) {
89 int selected_index
= tab
.normalized_navigation_index();
90 DCHECK_GE(selected_index
, 0);
91 DCHECK_LT(selected_index
, static_cast<int>(tab
.navigations
.size()));
93 const sessions::SerializedNavigationEntry
& current_navigation
=
94 tab
.navigations
.at(selected_index
);
96 GURL tab_url
= current_navigation
.virtual_url();
98 Java_ForeignSessionHelper_pushTab(
100 ConvertUTF8ToJavaString(env
, tab_url
.spec()).obj(),
101 ConvertUTF16ToJavaString(env
, current_navigation
.title()).obj(),
102 tab
.timestamp
.ToJavaTime(),
106 void CopyWindowToJava(
108 const sessions::SessionWindow
& window
,
109 ScopedJavaLocalRef
<jobject
>& j_window
) {
110 for (std::vector
<sessions::SessionTab
*>::const_iterator tab_it
=
111 window
.tabs
.begin(); tab_it
!= window
.tabs
.end(); ++tab_it
) {
112 const sessions::SessionTab
&session_tab
= **tab_it
;
114 if (ShouldSkipTab(session_tab
))
117 CopyTabToJava(env
, session_tab
, j_window
);
121 void CopySessionToJava(
123 const SyncedSession
& session
,
124 ScopedJavaLocalRef
<jobject
>& j_session
) {
125 for (SyncedSession::SyncedWindowMap::const_iterator it
=
126 session
.windows
.begin(); it
!= session
.windows
.end(); ++it
) {
127 const sessions::SessionWindow
&window
= *(it
->second
);
129 if (ShouldSkipWindow(window
))
132 ScopedJavaLocalRef
<jobject
> last_pushed_window
;
133 last_pushed_window
.Reset(
134 Java_ForeignSessionHelper_pushWindow(
135 env
, j_session
.obj(),
136 window
.timestamp
.ToJavaTime(),
137 window
.window_id
.id()));
139 CopyWindowToJava(env
, window
, last_pushed_window
);
145 static jlong
Init(JNIEnv
* env
,
146 const JavaParamRef
<jclass
>& clazz
,
147 const JavaParamRef
<jobject
>& profile
) {
148 ForeignSessionHelper
* foreign_session_helper
= new ForeignSessionHelper(
149 ProfileAndroid::FromProfileAndroid(profile
));
150 return reinterpret_cast<intptr_t>(foreign_session_helper
);
153 ForeignSessionHelper::ForeignSessionHelper(Profile
* profile
)
154 : profile_(profile
) {
155 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
156 GetForProfile(profile
);
157 registrar_
.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
,
158 content::Source
<ProfileSyncService
>(service
));
159 registrar_
.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
,
160 content::Source
<Profile
>(profile
));
161 registrar_
.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED
,
162 content::Source
<Profile
>(profile
));
165 ForeignSessionHelper::~ForeignSessionHelper() {
168 void ForeignSessionHelper::Destroy(JNIEnv
* env
, jobject obj
) {
172 jboolean
ForeignSessionHelper::IsTabSyncEnabled(JNIEnv
* env
, jobject obj
) {
173 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
174 GetForProfile(profile_
);
175 return service
&& service
->GetActiveDataTypes().Has(syncer::PROXY_TABS
);
178 void ForeignSessionHelper::TriggerSessionSync(JNIEnv
* env
, jobject obj
) {
179 const syncer::ModelTypeSet
types(syncer::SESSIONS
);
180 content::NotificationService::current()->Notify(
181 chrome::NOTIFICATION_SYNC_REFRESH_LOCAL
,
182 content::Source
<Profile
>(profile_
),
183 content::Details
<const syncer::ModelTypeSet
>(&types
));
186 void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv
* env
,
189 callback_
.Reset(env
, callback
);
192 void ForeignSessionHelper::Observe(
193 int type
, const content::NotificationSource
& source
,
194 const content::NotificationDetails
& details
) {
195 if (callback_
.is_null())
198 JNIEnv
* env
= AttachCurrentThread();
201 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED
:
202 // Tab sync is disabled, so clean up data about collapsed sessions.
203 profile_
->GetPrefs()->ClearPref(
204 prefs::kNtpCollapsedForeignSessions
);
205 // Purposeful fall through.
206 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE
:
207 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED
:
208 Java_ForeignSessionCallback_onUpdated(env
, callback_
.obj());
215 jboolean
ForeignSessionHelper::GetForeignSessions(JNIEnv
* env
,
218 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
222 std::vector
<const sync_driver::SyncedSession
*> sessions
;
223 if (!open_tabs
->GetAllForeignSessions(&sessions
))
226 // Use a pref to keep track of sessions that were collapsed by the user.
227 // To prevent the pref from accumulating stale sessions, clear it each time
228 // and only add back sessions that are still current.
229 DictionaryPrefUpdate
pref_update(profile_
->GetPrefs(),
230 prefs::kNtpCollapsedForeignSessions
);
231 base::DictionaryValue
* pref_collapsed_sessions
= pref_update
.Get();
232 scoped_ptr
<base::DictionaryValue
> collapsed_sessions(
233 pref_collapsed_sessions
->DeepCopy());
234 pref_collapsed_sessions
->Clear();
236 ScopedJavaLocalRef
<jobject
> last_pushed_session
;
238 // Note: we don't own the SyncedSessions themselves.
239 for (size_t i
= 0; i
< sessions
.size(); ++i
) {
240 const sync_driver::SyncedSession
& session
= *(sessions
[i
]);
241 if (ShouldSkipSession(session
))
244 const bool is_collapsed
= collapsed_sessions
->HasKey(session
.session_tag
);
247 pref_collapsed_sessions
->SetBoolean(session
.session_tag
, true);
249 last_pushed_session
.Reset(
250 Java_ForeignSessionHelper_pushSession(
253 ConvertUTF8ToJavaString(env
, session
.session_tag
).obj(),
254 ConvertUTF8ToJavaString(env
, session
.session_name
).obj(),
256 session
.modified_time
.ToJavaTime()));
258 const std::string group_name
=
259 base::FieldTrialList::FindFullName("TabSyncByRecency");
260 if (group_name
== "Enabled") {
261 // Create a custom window with tabs from all windows included and ordered
262 // by recency (GetForeignSessionTabs will do ordering automatically).
263 std::vector
<const sessions::SessionTab
*> tabs
;
264 open_tabs
->GetForeignSessionTabs(session
.session_tag
, &tabs
);
265 ScopedJavaLocalRef
<jobject
> last_pushed_window(
266 Java_ForeignSessionHelper_pushWindow(
267 env
, last_pushed_session
.obj(),
268 session
.modified_time
.ToJavaTime(), 0));
269 for (const sessions::SessionTab
* tab
: tabs
) {
270 if (ShouldSkipTab(*tab
))
272 CopyTabToJava(env
, *tab
, last_pushed_window
);
275 // Push the full session, with tabs ordered by visual position.
276 CopySessionToJava(env
, session
, last_pushed_session
);
283 jboolean
ForeignSessionHelper::OpenForeignSessionTab(JNIEnv
* env
,
288 jint j_disposition
) {
289 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
291 LOG(ERROR
) << "Null OpenTabsUIDelegate returned.";
295 const sessions::SessionTab
* session_tab
;
297 if (!open_tabs
->GetForeignTab(ConvertJavaStringToUTF8(env
, session_tag
),
300 LOG(ERROR
) << "Failed to load foreign tab.";
304 if (session_tab
->navigations
.empty()) {
305 LOG(ERROR
) << "Foreign tab no longer has valid navigations.";
309 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, j_tab
);
312 content::WebContents
* web_contents
= tab_android
->web_contents();
316 WindowOpenDisposition disposition
=
317 static_cast<WindowOpenDisposition
>(j_disposition
);
318 SessionRestore::RestoreForeignSessionTab(web_contents
,
325 void ForeignSessionHelper::DeleteForeignSession(JNIEnv
* env
, jobject obj
,
326 jstring session_tag
) {
327 OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate(profile_
);
329 open_tabs
->DeleteForeignSession(ConvertJavaStringToUTF8(env
, session_tag
));
333 bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv
* env
) {
334 return RegisterNativesImpl(env
);