blob: 9d30b53cfe2130a983e58f70d9502850711692e2 [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]5365e52c2013-07-31 23:07:1716#include "chrome/browser/sync/profile_sync_service_factory.h"
17#include "chrome/browser/ui/android/tab_model/tab_model.h"
18#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
19#include "chrome/common/pref_names.h"
20#include "chrome/common/url_constants.h"
blundell7282b512015-11-09 07:21:1121#include "components/browser_sync/browser/profile_sync_service.h"
maxbogue8ef25082015-11-16 19:09:5822#include "components/sync_sessions/open_tabs_ui_delegate.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;
droger6a118632015-06-23 17:59:4535using sync_driver::OpenTabsUIDelegate;
36using sync_driver::SyncedSession;
[email protected]5365e52c2013-07-31 23:07:1737
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();
zeae8f0c131d2015-08-04 23:20:2756 const sessions::SerializedNavigationEntry& current_navigation =
[email protected]48bf1572014-08-12 01:31:0957 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
droger6a118632015-06-23 17:59:4575bool ShouldSkipSession(const sync_driver::SyncedSession& session) {
[email protected]48bf1572014-08-12 01:31:0976 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
zeae8f0c131d2015-08-04 23:20:2785void CopyTabToJava(
86 JNIEnv* env,
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()));
92
93 const sessions::SerializedNavigationEntry& current_navigation =
94 tab.navigations.at(selected_index);
95
96 GURL tab_url = current_navigation.virtual_url();
97
98 Java_ForeignSessionHelper_pushTab(
99 env, j_window.obj(),
100 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
101 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
102 tab.timestamp.ToJavaTime(),
103 tab.tab_id.id());
104}
105
106void CopyWindowToJava(
[email protected]5365e52c2013-07-31 23:07:17107 JNIEnv* env,
skuhneb7409dcf2014-11-14 04:06:55108 const sessions::SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:17109 ScopedJavaLocalRef<jobject>& j_window) {
skuhneb7409dcf2014-11-14 04:06:55110 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;
[email protected]5365e52c2013-07-31 23:07:17113
[email protected]48bf1572014-08-12 01:31:09114 if (ShouldSkipTab(session_tab))
zeae8f0c131d2015-08-04 23:20:27115 return;
[email protected]48bf1572014-08-12 01:31:09116
zeae8f0c131d2015-08-04 23:20:27117 CopyTabToJava(env, session_tab, j_window);
[email protected]5365e52c2013-07-31 23:07:17118 }
119}
120
zeae8f0c131d2015-08-04 23:20:27121void CopySessionToJava(
[email protected]5365e52c2013-07-31 23:07:17122 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08123 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17124 ScopedJavaLocalRef<jobject>& j_session) {
125 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08126 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:55127 const sessions::SessionWindow &window = *(it->second);
[email protected]9744a15e2013-09-23 20:59:08128
[email protected]48bf1572014-08-12 01:31:09129 if (ShouldSkipWindow(window))
130 continue;
131
[email protected]5365e52c2013-07-31 23:07:17132 ScopedJavaLocalRef<jobject> last_pushed_window;
133 last_pushed_window.Reset(
134 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08135 env, j_session.obj(),
136 window.timestamp.ToJavaTime(),
137 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17138
zeae8f0c131d2015-08-04 23:20:27139 CopyWindowToJava(env, window, last_pushed_window);
[email protected]5365e52c2013-07-31 23:07:17140 }
141}
142
143} // namespace
144
torne89cc5d92015-09-04 11:16:35145static jlong Init(JNIEnv* env,
146 const JavaParamRef<jclass>& clazz,
147 const JavaParamRef<jobject>& profile) {
[email protected]5365e52c2013-07-31 23:07:17148 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
149 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31150 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17151}
152
153ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
blundellbbdc8a82015-10-28 15:47:20154 : profile_(profile), scoped_observer_(this) {
[email protected]5365e52c2013-07-31 23:07:17155 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
156 GetForProfile(profile);
blundellbbdc8a82015-10-28 15:47:20157
158 // NOTE: The ProfileSyncService can be null in tests.
159 if (service)
160 scoped_observer_.Add(service);
[email protected]5365e52c2013-07-31 23:07:17161}
162
163ForeignSessionHelper::~ForeignSessionHelper() {
164}
165
tornee621a272015-11-30 18:40:53166void ForeignSessionHelper::Destroy(JNIEnv* env,
167 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17168 delete this;
169}
170
tornee621a272015-11-30 18:40:53171jboolean ForeignSessionHelper::IsTabSyncEnabled(
172 JNIEnv* env,
173 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17174 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
175 GetForProfile(profile_);
176 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
177}
178
tornee621a272015-11-30 18:40:53179void ForeignSessionHelper::TriggerSessionSync(
180 JNIEnv* env,
181 const JavaParamRef<jobject>& obj) {
blundell0631ab032015-10-29 09:28:02182 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
183 GetForProfile(profile_);
184 if (!service)
185 return;
186
maxbogue5c8d0e22014-11-06 01:00:01187 const syncer::ModelTypeSet types(syncer::SESSIONS);
blundell0631ab032015-10-29 09:28:02188 service->TriggerRefresh(types);
maxbogue5c8d0e22014-11-06 01:00:01189}
190
tornee621a272015-11-30 18:40:53191void ForeignSessionHelper::SetOnForeignSessionCallback(
192 JNIEnv* env,
193 const JavaParamRef<jobject>& obj,
194 const JavaParamRef<jobject>& callback) {
[email protected]5365e52c2013-07-31 23:07:17195 callback_.Reset(env, callback);
196}
197
blundellbbdc8a82015-10-28 15:47:20198void ForeignSessionHelper::FireForeignSessionCallback() {
[email protected]5365e52c2013-07-31 23:07:17199 if (callback_.is_null())
200 return;
201
202 JNIEnv* env = AttachCurrentThread();
blundellbbdc8a82015-10-28 15:47:20203 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
204}
[email protected]5365e52c2013-07-31 23:07:17205
blundellfc39f9aa2015-10-30 08:25:54206void ForeignSessionHelper::OnSyncConfigurationCompleted() {
207 FireForeignSessionCallback();
[email protected]5365e52c2013-07-31 23:07:17208}
209
blundellfc39f9aa2015-10-30 08:25:54210void ForeignSessionHelper::OnForeignSessionUpdated() {
blundellbbdc8a82015-10-28 15:47:20211 FireForeignSessionCallback();
212}
213
tornee621a272015-11-30 18:40:53214jboolean ForeignSessionHelper::GetForeignSessions(
215 JNIEnv* env,
216 const JavaParamRef<jobject>& obj,
217 const JavaParamRef<jobject>& result) {
[email protected]a9f56622013-11-21 19:37:06218 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
219 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17220 return false;
221
droger6a118632015-06-23 17:59:45222 std::vector<const sync_driver::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06223 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17224 return false;
225
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);
[email protected]5bcdd99d2013-12-23 18:28:30231 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
232 scoped_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17233 pref_collapsed_sessions->DeepCopy());
234 pref_collapsed_sessions->Clear();
235
236 ScopedJavaLocalRef<jobject> last_pushed_session;
[email protected]5365e52c2013-07-31 23:07:17237
238 // Note: we don't own the SyncedSessions themselves.
239 for (size_t i = 0; i < sessions.size(); ++i) {
droger6a118632015-06-23 17:59:45240 const sync_driver::SyncedSession& session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09241 if (ShouldSkipSession(session))
242 continue;
[email protected]5365e52c2013-07-31 23:07:17243
[email protected]9744a15e2013-09-23 20:59:08244 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17245
246 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08247 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17248
249 last_pushed_session.Reset(
250 Java_ForeignSessionHelper_pushSession(
251 env,
252 result,
[email protected]c2a6ac32014-04-27 00:18:09253 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
254 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08255 session.device_type,
256 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17257
zeae8f0c131d2015-08-04 23:20:27258 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))
271 continue;
272 CopyTabToJava(env, *tab, last_pushed_window);
273 }
274 } else {
275 // Push the full session, with tabs ordered by visual position.
276 CopySessionToJava(env, session, last_pushed_session);
277 }
[email protected]5365e52c2013-07-31 23:07:17278 }
279
280 return true;
281}
282
tornee621a272015-11-30 18:40:53283jboolean ForeignSessionHelper::OpenForeignSessionTab(
284 JNIEnv* env,
285 const JavaParamRef<jobject>& obj,
286 const JavaParamRef<jobject>& j_tab,
287 const JavaParamRef<jstring>& session_tag,
288 jint session_tab_id,
289 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06290 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
291 if (!open_tabs) {
292 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39293 return false;
294 }
295
skuhneb7409dcf2014-11-14 04:06:55296 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39297
[email protected]a9f56622013-11-21 19:37:06298 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
299 session_tab_id,
300 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39301 LOG(ERROR) << "Failed to load foreign tab.";
302 return false;
303 }
304
305 if (session_tab->navigations.empty()) {
306 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
307 return false;
308 }
309
310 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
311 if (!tab_android)
312 return false;
313 content::WebContents* web_contents = tab_android->web_contents();
314 if (!web_contents)
315 return false;
316
317 WindowOpenDisposition disposition =
318 static_cast<WindowOpenDisposition>(j_disposition);
319 SessionRestore::RestoreForeignSessionTab(web_contents,
320 *session_tab,
321 disposition);
322
323 return true;
324}
325
tornee621a272015-11-30 18:40:53326void ForeignSessionHelper::DeleteForeignSession(
327 JNIEnv* env,
328 const JavaParamRef<jobject>& obj,
329 const JavaParamRef<jstring>& session_tag) {
[email protected]a9f56622013-11-21 19:37:06330 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
331 if (open_tabs)
332 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17333}
334
335// static
336bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
337 return RegisterNativesImpl(env);
338}