blob: 57f3405f12a5f8950b446db4dd9f11cd10f410ed [file] [log] [blame]
[email protected]5365e52c2013-07-31 23:07:171// 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"
[email protected]816939d2013-11-13 00:57:3910#include "base/prefs/pref_service.h"
[email protected]9eec53fe2013-10-30 20:21:1711#include "base/prefs/scoped_user_pref_update.h"
[email protected]816939d2013-11-13 00:57:3912#include "chrome/browser/android/tab_android.h"
[email protected]5365e52c2013-07-31 23:07:1713#include "chrome/browser/chrome_notification_types.h"
[email protected]5365e52c2013-07-31 23:07:1714#include "chrome/browser/profiles/profile_android.h"
[email protected]816939d2013-11-13 00:57:3915#include "chrome/browser/sessions/session_restore.h"
[email protected]a9f56622013-11-21 19:37:0616#include "chrome/browser/sync/open_tabs_ui_delegate.h"
[email protected]5365e52c2013-07-31 23:07:1717#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
27using base::android::ScopedJavaGlobalRef;
28using base::android::ScopedJavaLocalRef;
29using base::android::AttachCurrentThread;
30using base::android::ConvertUTF16ToJavaString;
31using base::android::ConvertUTF8ToJavaString;
32using base::android::ConvertJavaStringToUTF8;
[email protected]a9f56622013-11-21 19:37:0633using browser_sync::OpenTabsUIDelegate;
[email protected]5365e52c2013-07-31 23:07:1734using browser_sync::SyncedSession;
35
36namespace {
37
[email protected]a9f56622013-11-21 19:37:0638OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
[email protected]5365e52c2013-07-31 23:07:1739 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
40 GetForProfile(profile);
41
[email protected]a9f56622013-11-21 19:37:0642 // Only return the delegate if it exists and it is done syncing sessions.
[email protected]5365e52c2013-07-31 23:07:1743 if (!service || !service->ShouldPushChanges())
44 return NULL;
45
[email protected]a9f56622013-11-21 19:37:0646 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1747}
48
[email protected]48bf1572014-08-12 01:31:0949bool ShouldSkipTab(const SessionTab& session_tab) {
50 if (session_tab.navigations.empty())
51 return true;
52
maxbogue4157d2b2014-08-27 21:56:4853 int selected_index = session_tab.normalized_navigation_index();
[email protected]48bf1572014-08-12 01:31:0954 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
63bool 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
73bool 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
[email protected]5365e52c2013-07-31 23:07:1783void CopyTabsToJava(
84 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:0885 const SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:1786 ScopedJavaLocalRef<jobject>& j_window) {
[email protected]9744a15e2013-09-23 20:59:0887 for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
88 tab_it != window.tabs.end(); ++tab_it) {
[email protected]816939d2013-11-13 00:57:3989 const SessionTab &session_tab = **tab_it;
[email protected]5365e52c2013-07-31 23:07:1790
[email protected]48bf1572014-08-12 01:31:0991 if (ShouldSkipTab(session_tab))
92 continue;
93
maxbogue4157d2b2014-08-27 21:56:4894 int selected_index = session_tab.normalized_navigation_index();
[email protected]48bf1572014-08-12 01:31:0995 DCHECK(selected_index >= 0);
96 DCHECK(selected_index < static_cast<int>(session_tab.navigations.size()));
[email protected]9744a15e2013-09-23 20:59:0897
[email protected]5365e52c2013-07-31 23:07:1798 const ::sessions::SerializedNavigationEntry& current_navigation =
[email protected]816939d2013-11-13 00:57:3999 session_tab.navigations.at(selected_index);
[email protected]5365e52c2013-07-31 23:07:17100
101 GURL tab_url = current_navigation.virtual_url();
[email protected]5365e52c2013-07-31 23:07:17102
103 Java_ForeignSessionHelper_pushTab(
104 env, j_window.obj(),
[email protected]c2a6ac32014-04-27 00:18:09105 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
106 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
[email protected]816939d2013-11-13 00:57:39107 session_tab.timestamp.ToJavaTime(),
108 session_tab.tab_id.id());
[email protected]5365e52c2013-07-31 23:07:17109 }
110}
111
112void CopyWindowsToJava(
113 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08114 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17115 ScopedJavaLocalRef<jobject>& j_session) {
116 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08117 session.windows.begin(); it != session.windows.end(); ++it) {
118 const SessionWindow &window = *(it->second);
119
[email protected]48bf1572014-08-12 01:31:09120 if (ShouldSkipWindow(window))
121 continue;
122
[email protected]5365e52c2013-07-31 23:07:17123 ScopedJavaLocalRef<jobject> last_pushed_window;
124 last_pushed_window.Reset(
125 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08126 env, j_session.obj(),
127 window.timestamp.ToJavaTime(),
128 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17129
130 CopyTabsToJava(env, window, last_pushed_window);
131 }
132}
133
134} // namespace
135
[email protected]d557b6042013-11-21 16:34:31136static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
[email protected]5365e52c2013-07-31 23:07:17137 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
138 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31139 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17140}
141
142ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
143 : profile_(profile) {
144 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
145 GetForProfile(profile);
[email protected]5365e52c2013-07-31 23:07:17146 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
154ForeignSessionHelper::~ForeignSessionHelper() {
155}
156
157void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
158 delete this;
159}
160
161jboolean 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
167void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
168 jobject obj,
169 jobject callback) {
170 callback_.Reset(env, callback);
171}
172
173void 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
196jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
197 jobject obj,
198 jobject result) {
[email protected]a9f56622013-11-21 19:37:06199 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
200 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17201 return false;
202
203 std::vector<const browser_sync::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06204 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17205 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);
[email protected]5bcdd99d2013-12-23 18:28:30212 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
213 scoped_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17214 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) {
[email protected]9744a15e2013-09-23 20:59:08222 const browser_sync::SyncedSession &session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09223 if (ShouldSkipSession(session))
224 continue;
[email protected]5365e52c2013-07-31 23:07:17225
[email protected]9744a15e2013-09-23 20:59:08226 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17227
228 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08229 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17230
231 last_pushed_session.Reset(
232 Java_ForeignSessionHelper_pushSession(
233 env,
234 result,
[email protected]c2a6ac32014-04-27 00:18:09235 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
236 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08237 session.device_type,
238 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17239
240 CopyWindowsToJava(env, session, last_pushed_session);
241 }
242
243 return true;
244}
245
[email protected]816939d2013-11-13 00:57:39246jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
247 jobject obj,
248 jobject j_tab,
249 jstring session_tag,
250 jint session_tab_id,
251 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06252 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
253 if (!open_tabs) {
254 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39255 return false;
256 }
257
258 const SessionTab* session_tab;
259
[email protected]a9f56622013-11-21 19:37:06260 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
261 session_tab_id,
262 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39263 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
[email protected]5365e52c2013-07-31 23:07:17288void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
289 jstring session_tag) {
[email protected]a9f56622013-11-21 19:37:06290 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
291 if (open_tabs)
292 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17293}
294
295// static
296bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
297 return RegisterNativesImpl(env);
298}