blob: a3712f40b1be658e0af4c2e9e583c3e89e15c039 [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>
avie4d7b6f2015-12-26 00:59:188#include <stddef.h>
[email protected]5365e52c2013-07-31 23:07:179
10#include "base/android/jni_string.h"
[email protected]816939d2013-11-13 00:57:3911#include "base/prefs/pref_service.h"
[email protected]9eec53fe2013-10-30 20:21:1712#include "base/prefs/scoped_user_pref_update.h"
[email protected]816939d2013-11-13 00:57:3913#include "chrome/browser/android/tab_android.h"
[email protected]5365e52c2013-07-31 23:07:1714#include "chrome/browser/chrome_notification_types.h"
[email protected]5365e52c2013-07-31 23:07:1715#include "chrome/browser/profiles/profile_android.h"
[email protected]816939d2013-11-13 00:57:3916#include "chrome/browser/sessions/session_restore.h"
[email protected]5365e52c2013-07-31 23:07:1717#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"
blundell7282b512015-11-09 07:21:1122#include "components/browser_sync/browser/profile_sync_service.h"
maxbogue8ef25082015-11-16 19:09:5823#include "components/sync_sessions/open_tabs_ui_delegate.h"
maxbogue5c8d0e22014-11-06 01:00:0124#include "content/public/browser/notification_details.h"
25#include "content/public/browser/notification_service.h"
[email protected]5365e52c2013-07-31 23:07:1726#include "content/public/browser/notification_source.h"
27#include "content/public/browser/web_contents.h"
28#include "jni/ForeignSessionHelper_jni.h"
29
30using base::android::ScopedJavaGlobalRef;
31using base::android::ScopedJavaLocalRef;
32using base::android::AttachCurrentThread;
33using base::android::ConvertUTF16ToJavaString;
34using base::android::ConvertUTF8ToJavaString;
35using base::android::ConvertJavaStringToUTF8;
droger6a118632015-06-23 17:59:4536using sync_driver::OpenTabsUIDelegate;
37using sync_driver::SyncedSession;
[email protected]5365e52c2013-07-31 23:07:1738
39namespace {
40
[email protected]a9f56622013-11-21 19:37:0641OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
[email protected]5365e52c2013-07-31 23:07:1742 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
43 GetForProfile(profile);
44
[email protected]a9f56622013-11-21 19:37:0645 // Only return the delegate if it exists and it is done syncing sessions.
maxbogue192b3602015-06-04 00:33:5546 if (!service || !service->IsSyncActive())
[email protected]5365e52c2013-07-31 23:07:1747 return NULL;
48
[email protected]a9f56622013-11-21 19:37:0649 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1750}
51
skuhneb7409dcf2014-11-14 04:06:5552bool ShouldSkipTab(const sessions::SessionTab& session_tab) {
[email protected]48bf1572014-08-12 01:31:0953 if (session_tab.navigations.empty())
54 return true;
55
maxbogue4157d2b2014-08-27 21:56:4856 int selected_index = session_tab.normalized_navigation_index();
zeae8f0c131d2015-08-04 23:20:2757 const sessions::SerializedNavigationEntry& current_navigation =
[email protected]48bf1572014-08-12 01:31:0958 session_tab.navigations.at(selected_index);
59
60 if (current_navigation.virtual_url().is_empty())
61 return true;
62
63 return false;
64}
65
skuhneb7409dcf2014-11-14 04:06:5566bool ShouldSkipWindow(const sessions::SessionWindow& window) {
67 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
68 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
69 const sessions::SessionTab &session_tab = **tab_it;
[email protected]48bf1572014-08-12 01:31:0970 if (!ShouldSkipTab(session_tab))
71 return false;
72 }
73 return true;
74}
75
droger6a118632015-06-23 17:59:4576bool ShouldSkipSession(const sync_driver::SyncedSession& session) {
[email protected]48bf1572014-08-12 01:31:0977 for (SyncedSession::SyncedWindowMap::const_iterator it =
78 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:5579 const sessions::SessionWindow &window = *(it->second);
[email protected]48bf1572014-08-12 01:31:0980 if (!ShouldSkipWindow(window))
81 return false;
82 }
83 return true;
84}
85
zeae8f0c131d2015-08-04 23:20:2786void CopyTabToJava(
87 JNIEnv* env,
88 const sessions::SessionTab& tab,
89 ScopedJavaLocalRef<jobject>& j_window) {
90 int selected_index = tab.normalized_navigation_index();
91 DCHECK_GE(selected_index, 0);
92 DCHECK_LT(selected_index, static_cast<int>(tab.navigations.size()));
93
94 const sessions::SerializedNavigationEntry& current_navigation =
95 tab.navigations.at(selected_index);
96
97 GURL tab_url = current_navigation.virtual_url();
98
99 Java_ForeignSessionHelper_pushTab(
100 env, j_window.obj(),
101 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
102 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
103 tab.timestamp.ToJavaTime(),
104 tab.tab_id.id());
105}
106
107void CopyWindowToJava(
[email protected]5365e52c2013-07-31 23:07:17108 JNIEnv* env,
skuhneb7409dcf2014-11-14 04:06:55109 const sessions::SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:17110 ScopedJavaLocalRef<jobject>& j_window) {
skuhneb7409dcf2014-11-14 04:06:55111 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
112 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
113 const sessions::SessionTab &session_tab = **tab_it;
[email protected]5365e52c2013-07-31 23:07:17114
[email protected]48bf1572014-08-12 01:31:09115 if (ShouldSkipTab(session_tab))
zeae8f0c131d2015-08-04 23:20:27116 return;
[email protected]48bf1572014-08-12 01:31:09117
zeae8f0c131d2015-08-04 23:20:27118 CopyTabToJava(env, session_tab, j_window);
[email protected]5365e52c2013-07-31 23:07:17119 }
120}
121
zeae8f0c131d2015-08-04 23:20:27122void CopySessionToJava(
[email protected]5365e52c2013-07-31 23:07:17123 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08124 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17125 ScopedJavaLocalRef<jobject>& j_session) {
126 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08127 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:55128 const sessions::SessionWindow &window = *(it->second);
[email protected]9744a15e2013-09-23 20:59:08129
[email protected]48bf1572014-08-12 01:31:09130 if (ShouldSkipWindow(window))
131 continue;
132
[email protected]5365e52c2013-07-31 23:07:17133 ScopedJavaLocalRef<jobject> last_pushed_window;
134 last_pushed_window.Reset(
135 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08136 env, j_session.obj(),
137 window.timestamp.ToJavaTime(),
138 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17139
zeae8f0c131d2015-08-04 23:20:27140 CopyWindowToJava(env, window, last_pushed_window);
[email protected]5365e52c2013-07-31 23:07:17141 }
142}
143
144} // namespace
145
torne89cc5d92015-09-04 11:16:35146static jlong Init(JNIEnv* env,
147 const JavaParamRef<jclass>& clazz,
148 const JavaParamRef<jobject>& profile) {
[email protected]5365e52c2013-07-31 23:07:17149 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
150 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31151 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17152}
153
154ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
blundellbbdc8a82015-10-28 15:47:20155 : profile_(profile), scoped_observer_(this) {
[email protected]5365e52c2013-07-31 23:07:17156 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
157 GetForProfile(profile);
blundellbbdc8a82015-10-28 15:47:20158
159 // NOTE: The ProfileSyncService can be null in tests.
160 if (service)
161 scoped_observer_.Add(service);
[email protected]5365e52c2013-07-31 23:07:17162}
163
164ForeignSessionHelper::~ForeignSessionHelper() {
165}
166
tornee621a272015-11-30 18:40:53167void ForeignSessionHelper::Destroy(JNIEnv* env,
168 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17169 delete this;
170}
171
tornee621a272015-11-30 18:40:53172jboolean ForeignSessionHelper::IsTabSyncEnabled(
173 JNIEnv* env,
174 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17175 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
176 GetForProfile(profile_);
177 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
178}
179
tornee621a272015-11-30 18:40:53180void ForeignSessionHelper::TriggerSessionSync(
181 JNIEnv* env,
182 const JavaParamRef<jobject>& obj) {
blundell0631ab032015-10-29 09:28:02183 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
184 GetForProfile(profile_);
185 if (!service)
186 return;
187
maxbogue5c8d0e22014-11-06 01:00:01188 const syncer::ModelTypeSet types(syncer::SESSIONS);
blundell0631ab032015-10-29 09:28:02189 service->TriggerRefresh(types);
maxbogue5c8d0e22014-11-06 01:00:01190}
191
tornee621a272015-11-30 18:40:53192void ForeignSessionHelper::SetOnForeignSessionCallback(
193 JNIEnv* env,
194 const JavaParamRef<jobject>& obj,
195 const JavaParamRef<jobject>& callback) {
[email protected]5365e52c2013-07-31 23:07:17196 callback_.Reset(env, callback);
197}
198
blundellbbdc8a82015-10-28 15:47:20199void ForeignSessionHelper::FireForeignSessionCallback() {
[email protected]5365e52c2013-07-31 23:07:17200 if (callback_.is_null())
201 return;
202
203 JNIEnv* env = AttachCurrentThread();
blundellbbdc8a82015-10-28 15:47:20204 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
205}
[email protected]5365e52c2013-07-31 23:07:17206
blundellfc39f9aa2015-10-30 08:25:54207void ForeignSessionHelper::OnSyncConfigurationCompleted() {
208 FireForeignSessionCallback();
[email protected]5365e52c2013-07-31 23:07:17209}
210
blundellfc39f9aa2015-10-30 08:25:54211void ForeignSessionHelper::OnForeignSessionUpdated() {
blundellbbdc8a82015-10-28 15:47:20212 FireForeignSessionCallback();
213}
214
tornee621a272015-11-30 18:40:53215jboolean ForeignSessionHelper::GetForeignSessions(
216 JNIEnv* env,
217 const JavaParamRef<jobject>& obj,
218 const JavaParamRef<jobject>& result) {
[email protected]a9f56622013-11-21 19:37:06219 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
220 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17221 return false;
222
droger6a118632015-06-23 17:59:45223 std::vector<const sync_driver::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06224 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17225 return false;
226
227 // Use a pref to keep track of sessions that were collapsed by the user.
228 // To prevent the pref from accumulating stale sessions, clear it each time
229 // and only add back sessions that are still current.
230 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
231 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30232 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
233 scoped_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17234 pref_collapsed_sessions->DeepCopy());
235 pref_collapsed_sessions->Clear();
236
237 ScopedJavaLocalRef<jobject> last_pushed_session;
[email protected]5365e52c2013-07-31 23:07:17238
239 // Note: we don't own the SyncedSessions themselves.
240 for (size_t i = 0; i < sessions.size(); ++i) {
droger6a118632015-06-23 17:59:45241 const sync_driver::SyncedSession& session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09242 if (ShouldSkipSession(session))
243 continue;
[email protected]5365e52c2013-07-31 23:07:17244
[email protected]9744a15e2013-09-23 20:59:08245 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17246
247 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08248 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17249
250 last_pushed_session.Reset(
251 Java_ForeignSessionHelper_pushSession(
252 env,
253 result,
[email protected]c2a6ac32014-04-27 00:18:09254 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
255 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08256 session.device_type,
257 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17258
zeae8f0c131d2015-08-04 23:20:27259 const std::string group_name =
260 base::FieldTrialList::FindFullName("TabSyncByRecency");
261 if (group_name == "Enabled") {
262 // Create a custom window with tabs from all windows included and ordered
263 // by recency (GetForeignSessionTabs will do ordering automatically).
264 std::vector<const sessions::SessionTab*> tabs;
265 open_tabs->GetForeignSessionTabs(session.session_tag, &tabs);
266 ScopedJavaLocalRef<jobject> last_pushed_window(
267 Java_ForeignSessionHelper_pushWindow(
268 env, last_pushed_session.obj(),
269 session.modified_time.ToJavaTime(), 0));
270 for (const sessions::SessionTab* tab : tabs) {
271 if (ShouldSkipTab(*tab))
272 continue;
273 CopyTabToJava(env, *tab, last_pushed_window);
274 }
275 } else {
276 // Push the full session, with tabs ordered by visual position.
277 CopySessionToJava(env, session, last_pushed_session);
278 }
[email protected]5365e52c2013-07-31 23:07:17279 }
280
281 return true;
282}
283
tornee621a272015-11-30 18:40:53284jboolean ForeignSessionHelper::OpenForeignSessionTab(
285 JNIEnv* env,
286 const JavaParamRef<jobject>& obj,
287 const JavaParamRef<jobject>& j_tab,
288 const JavaParamRef<jstring>& session_tag,
289 jint session_tab_id,
290 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06291 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
292 if (!open_tabs) {
293 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39294 return false;
295 }
296
skuhneb7409dcf2014-11-14 04:06:55297 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39298
[email protected]a9f56622013-11-21 19:37:06299 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
300 session_tab_id,
301 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39302 LOG(ERROR) << "Failed to load foreign tab.";
303 return false;
304 }
305
306 if (session_tab->navigations.empty()) {
307 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
308 return false;
309 }
310
311 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
312 if (!tab_android)
313 return false;
314 content::WebContents* web_contents = tab_android->web_contents();
315 if (!web_contents)
316 return false;
317
318 WindowOpenDisposition disposition =
319 static_cast<WindowOpenDisposition>(j_disposition);
320 SessionRestore::RestoreForeignSessionTab(web_contents,
321 *session_tab,
322 disposition);
323
324 return true;
325}
326
tornee621a272015-11-30 18:40:53327void ForeignSessionHelper::DeleteForeignSession(
328 JNIEnv* env,
329 const JavaParamRef<jobject>& obj,
330 const JavaParamRef<jstring>& session_tag) {
[email protected]a9f56622013-11-21 19:37:06331 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
332 if (open_tabs)
333 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17334}
335
336// static
337bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
338 return RegisterNativesImpl(env);
339}