blob: e14fbc5fcc685c258eef1a3f88ba1f1fbf909ac5 [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"
maxbogue5c8d0e22014-11-06 01:00:0123#include "content/public/browser/notification_details.h"
24#include "content/public/browser/notification_service.h"
[email protected]5365e52c2013-07-31 23:07:1725#include "content/public/browser/notification_source.h"
26#include "content/public/browser/web_contents.h"
27#include "jni/ForeignSessionHelper_jni.h"
28
29using base::android::ScopedJavaGlobalRef;
30using base::android::ScopedJavaLocalRef;
31using base::android::AttachCurrentThread;
32using base::android::ConvertUTF16ToJavaString;
33using base::android::ConvertUTF8ToJavaString;
34using base::android::ConvertJavaStringToUTF8;
[email protected]a9f56622013-11-21 19:37:0635using browser_sync::OpenTabsUIDelegate;
[email protected]5365e52c2013-07-31 23:07:1736using browser_sync::SyncedSession;
37
38namespace {
39
[email protected]a9f56622013-11-21 19:37:0640OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
[email protected]5365e52c2013-07-31 23:07:1741 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
42 GetForProfile(profile);
43
[email protected]a9f56622013-11-21 19:37:0644 // Only return the delegate if it exists and it is done syncing sessions.
maxbogue192b3602015-06-04 00:33:5545 if (!service || !service->IsSyncActive())
[email protected]5365e52c2013-07-31 23:07:1746 return NULL;
47
[email protected]a9f56622013-11-21 19:37:0648 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1749}
50
skuhneb7409dcf2014-11-14 04:06:5551bool ShouldSkipTab(const sessions::SessionTab& session_tab) {
[email protected]48bf1572014-08-12 01:31:0952 if (session_tab.navigations.empty())
53 return true;
54
maxbogue4157d2b2014-08-27 21:56:4855 int selected_index = session_tab.normalized_navigation_index();
[email protected]48bf1572014-08-12 01:31:0956 const ::sessions::SerializedNavigationEntry& current_navigation =
57 session_tab.navigations.at(selected_index);
58
59 if (current_navigation.virtual_url().is_empty())
60 return true;
61
62 return false;
63}
64
skuhneb7409dcf2014-11-14 04:06:5565bool 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;
[email protected]48bf1572014-08-12 01:31:0969 if (!ShouldSkipTab(session_tab))
70 return false;
71 }
72 return true;
73}
74
75bool ShouldSkipSession(const browser_sync::SyncedSession& session) {
76 for (SyncedSession::SyncedWindowMap::const_iterator it =
77 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:5578 const sessions::SessionWindow &window = *(it->second);
[email protected]48bf1572014-08-12 01:31:0979 if (!ShouldSkipWindow(window))
80 return false;
81 }
82 return true;
83}
84
[email protected]5365e52c2013-07-31 23:07:1785void CopyTabsToJava(
86 JNIEnv* env,
skuhneb7409dcf2014-11-14 04:06:5587 const sessions::SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:1788 ScopedJavaLocalRef<jobject>& j_window) {
skuhneb7409dcf2014-11-14 04:06:5589 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
90 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
91 const sessions::SessionTab &session_tab = **tab_it;
[email protected]5365e52c2013-07-31 23:07:1792
[email protected]48bf1572014-08-12 01:31:0993 if (ShouldSkipTab(session_tab))
94 continue;
95
maxbogue4157d2b2014-08-27 21:56:4896 int selected_index = session_tab.normalized_navigation_index();
[email protected]48bf1572014-08-12 01:31:0997 DCHECK(selected_index >= 0);
98 DCHECK(selected_index < static_cast<int>(session_tab.navigations.size()));
[email protected]9744a15e2013-09-23 20:59:0899
[email protected]5365e52c2013-07-31 23:07:17100 const ::sessions::SerializedNavigationEntry& current_navigation =
[email protected]816939d2013-11-13 00:57:39101 session_tab.navigations.at(selected_index);
[email protected]5365e52c2013-07-31 23:07:17102
103 GURL tab_url = current_navigation.virtual_url();
[email protected]5365e52c2013-07-31 23:07:17104
105 Java_ForeignSessionHelper_pushTab(
106 env, j_window.obj(),
[email protected]c2a6ac32014-04-27 00:18:09107 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
108 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
[email protected]816939d2013-11-13 00:57:39109 session_tab.timestamp.ToJavaTime(),
110 session_tab.tab_id.id());
[email protected]5365e52c2013-07-31 23:07:17111 }
112}
113
114void CopyWindowsToJava(
115 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08116 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17117 ScopedJavaLocalRef<jobject>& j_session) {
118 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08119 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:55120 const sessions::SessionWindow &window = *(it->second);
[email protected]9744a15e2013-09-23 20:59:08121
[email protected]48bf1572014-08-12 01:31:09122 if (ShouldSkipWindow(window))
123 continue;
124
[email protected]5365e52c2013-07-31 23:07:17125 ScopedJavaLocalRef<jobject> last_pushed_window;
126 last_pushed_window.Reset(
127 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08128 env, j_session.obj(),
129 window.timestamp.ToJavaTime(),
130 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17131
132 CopyTabsToJava(env, window, last_pushed_window);
133 }
134}
135
136} // namespace
137
[email protected]d557b6042013-11-21 16:34:31138static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
[email protected]5365e52c2013-07-31 23:07:17139 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
140 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31141 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17142}
143
144ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
145 : profile_(profile) {
146 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
147 GetForProfile(profile);
[email protected]5365e52c2013-07-31 23:07:17148 registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
149 content::Source<ProfileSyncService>(service));
150 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
151 content::Source<Profile>(profile));
152 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
153 content::Source<Profile>(profile));
154}
155
156ForeignSessionHelper::~ForeignSessionHelper() {
157}
158
159void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
160 delete this;
161}
162
163jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) {
164 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
165 GetForProfile(profile_);
166 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
167}
168
maxbogue5c8d0e22014-11-06 01:00:01169void ForeignSessionHelper::TriggerSessionSync(JNIEnv* env, jobject obj) {
170 const syncer::ModelTypeSet types(syncer::SESSIONS);
171 content::NotificationService::current()->Notify(
172 chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
173 content::Source<Profile>(profile_),
174 content::Details<const syncer::ModelTypeSet>(&types));
175}
176
[email protected]5365e52c2013-07-31 23:07:17177void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
178 jobject obj,
179 jobject callback) {
180 callback_.Reset(env, callback);
181}
182
183void ForeignSessionHelper::Observe(
184 int type, const content::NotificationSource& source,
185 const content::NotificationDetails& details) {
186 if (callback_.is_null())
187 return;
188
189 JNIEnv* env = AttachCurrentThread();
190
191 switch (type) {
192 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
193 // Tab sync is disabled, so clean up data about collapsed sessions.
194 profile_->GetPrefs()->ClearPref(
195 prefs::kNtpCollapsedForeignSessions);
196 // Purposeful fall through.
197 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
198 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
199 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
200 break;
201 default:
202 NOTREACHED();
203 }
204}
205
206jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
207 jobject obj,
208 jobject result) {
[email protected]a9f56622013-11-21 19:37:06209 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
210 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17211 return false;
212
213 std::vector<const browser_sync::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06214 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17215 return false;
216
217 // Use a pref to keep track of sessions that were collapsed by the user.
218 // To prevent the pref from accumulating stale sessions, clear it each time
219 // and only add back sessions that are still current.
220 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
221 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30222 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
223 scoped_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17224 pref_collapsed_sessions->DeepCopy());
225 pref_collapsed_sessions->Clear();
226
227 ScopedJavaLocalRef<jobject> last_pushed_session;
228 ScopedJavaLocalRef<jobject> last_pushed_window;
229
230 // Note: we don't own the SyncedSessions themselves.
231 for (size_t i = 0; i < sessions.size(); ++i) {
[email protected]9744a15e2013-09-23 20:59:08232 const browser_sync::SyncedSession &session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09233 if (ShouldSkipSession(session))
234 continue;
[email protected]5365e52c2013-07-31 23:07:17235
[email protected]9744a15e2013-09-23 20:59:08236 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17237
238 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08239 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17240
241 last_pushed_session.Reset(
242 Java_ForeignSessionHelper_pushSession(
243 env,
244 result,
[email protected]c2a6ac32014-04-27 00:18:09245 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
246 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08247 session.device_type,
248 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17249
250 CopyWindowsToJava(env, session, last_pushed_session);
251 }
252
253 return true;
254}
255
[email protected]816939d2013-11-13 00:57:39256jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
257 jobject obj,
258 jobject j_tab,
259 jstring session_tag,
260 jint session_tab_id,
261 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06262 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
263 if (!open_tabs) {
264 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39265 return false;
266 }
267
skuhneb7409dcf2014-11-14 04:06:55268 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39269
[email protected]a9f56622013-11-21 19:37:06270 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
271 session_tab_id,
272 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39273 LOG(ERROR) << "Failed to load foreign tab.";
274 return false;
275 }
276
277 if (session_tab->navigations.empty()) {
278 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
279 return false;
280 }
281
282 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
283 if (!tab_android)
284 return false;
285 content::WebContents* web_contents = tab_android->web_contents();
286 if (!web_contents)
287 return false;
288
289 WindowOpenDisposition disposition =
290 static_cast<WindowOpenDisposition>(j_disposition);
291 SessionRestore::RestoreForeignSessionTab(web_contents,
292 *session_tab,
293 disposition);
294
295 return true;
296}
297
[email protected]5365e52c2013-07-31 23:07:17298void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
299 jstring session_tag) {
[email protected]a9f56622013-11-21 19:37:06300 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
301 if (open_tabs)
302 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17303}
304
305// static
306bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
307 return RegisterNativesImpl(env);
308}