blob: 0722bf337921aea2969effdcc4dd5c47489619c7 [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 "chrome/browser/android/tab_android.h"
[email protected]5365e52c2013-07-31 23:07:1712#include "chrome/browser/chrome_notification_types.h"
[email protected]5365e52c2013-07-31 23:07:1713#include "chrome/browser/profiles/profile_android.h"
[email protected]816939d2013-11-13 00:57:3914#include "chrome/browser/sessions/session_restore.h"
[email protected]5365e52c2013-07-31 23:07:1715#include "chrome/browser/sync/profile_sync_service_factory.h"
16#include "chrome/browser/ui/android/tab_model/tab_model.h"
17#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/common/url_constants.h"
blundell7282b512015-11-09 07:21:1120#include "components/browser_sync/browser/profile_sync_service.h"
brettwb1fc1b82016-02-02 00:19:0821#include "components/prefs/pref_service.h"
22#include "components/prefs/scoped_user_pref_update.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
torne86560112016-08-04 15:59:0430using base::android::JavaParamRef;
[email protected]5365e52c2013-07-31 23:07:1731using base::android::ScopedJavaGlobalRef;
32using base::android::ScopedJavaLocalRef;
33using base::android::AttachCurrentThread;
34using base::android::ConvertUTF16ToJavaString;
35using base::android::ConvertUTF8ToJavaString;
36using base::android::ConvertJavaStringToUTF8;
droger6a118632015-06-23 17:59:4537using sync_driver::OpenTabsUIDelegate;
38using sync_driver::SyncedSession;
[email protected]5365e52c2013-07-31 23:07:1739
40namespace {
41
[email protected]a9f56622013-11-21 19:37:0642OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
[email protected]5365e52c2013-07-31 23:07:1743 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
44 GetForProfile(profile);
45
[email protected]a9f56622013-11-21 19:37:0646 // Only return the delegate if it exists and it is done syncing sessions.
maxbogue192b3602015-06-04 00:33:5547 if (!service || !service->IsSyncActive())
[email protected]5365e52c2013-07-31 23:07:1748 return NULL;
49
[email protected]a9f56622013-11-21 19:37:0650 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1751}
52
skuhneb7409dcf2014-11-14 04:06:5553bool ShouldSkipTab(const sessions::SessionTab& session_tab) {
[email protected]48bf1572014-08-12 01:31:0954 if (session_tab.navigations.empty())
55 return true;
56
maxbogue4157d2b2014-08-27 21:56:4857 int selected_index = session_tab.normalized_navigation_index();
zeae8f0c131d2015-08-04 23:20:2758 const sessions::SerializedNavigationEntry& current_navigation =
[email protected]48bf1572014-08-12 01:31:0959 session_tab.navigations.at(selected_index);
60
61 if (current_navigation.virtual_url().is_empty())
62 return true;
63
64 return false;
65}
66
skuhneb7409dcf2014-11-14 04:06:5567bool ShouldSkipWindow(const sessions::SessionWindow& window) {
68 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
69 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
70 const sessions::SessionTab &session_tab = **tab_it;
[email protected]48bf1572014-08-12 01:31:0971 if (!ShouldSkipTab(session_tab))
72 return false;
73 }
74 return true;
75}
76
droger6a118632015-06-23 17:59:4577bool ShouldSkipSession(const sync_driver::SyncedSession& session) {
[email protected]48bf1572014-08-12 01:31:0978 for (SyncedSession::SyncedWindowMap::const_iterator it =
79 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:5580 const sessions::SessionWindow &window = *(it->second);
[email protected]48bf1572014-08-12 01:31:0981 if (!ShouldSkipWindow(window))
82 return false;
83 }
84 return true;
85}
86
zeae8f0c131d2015-08-04 23:20:2787void CopyTabToJava(
88 JNIEnv* env,
89 const sessions::SessionTab& tab,
90 ScopedJavaLocalRef<jobject>& j_window) {
91 int selected_index = tab.normalized_navigation_index();
92 DCHECK_GE(selected_index, 0);
93 DCHECK_LT(selected_index, static_cast<int>(tab.navigations.size()));
94
95 const sessions::SerializedNavigationEntry& current_navigation =
96 tab.navigations.at(selected_index);
97
98 GURL tab_url = current_navigation.virtual_url();
99
100 Java_ForeignSessionHelper_pushTab(
101 env, j_window.obj(),
102 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
103 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
104 tab.timestamp.ToJavaTime(),
105 tab.tab_id.id());
106}
107
108void CopyWindowToJava(
[email protected]5365e52c2013-07-31 23:07:17109 JNIEnv* env,
skuhneb7409dcf2014-11-14 04:06:55110 const sessions::SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:17111 ScopedJavaLocalRef<jobject>& j_window) {
skuhneb7409dcf2014-11-14 04:06:55112 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
113 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
114 const sessions::SessionTab &session_tab = **tab_it;
[email protected]5365e52c2013-07-31 23:07:17115
[email protected]48bf1572014-08-12 01:31:09116 if (ShouldSkipTab(session_tab))
zeae8f0c131d2015-08-04 23:20:27117 return;
[email protected]48bf1572014-08-12 01:31:09118
zeae8f0c131d2015-08-04 23:20:27119 CopyTabToJava(env, session_tab, j_window);
[email protected]5365e52c2013-07-31 23:07:17120 }
121}
122
zeae8f0c131d2015-08-04 23:20:27123void CopySessionToJava(
[email protected]5365e52c2013-07-31 23:07:17124 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08125 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17126 ScopedJavaLocalRef<jobject>& j_session) {
127 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08128 session.windows.begin(); it != session.windows.end(); ++it) {
skuhneb7409dcf2014-11-14 04:06:55129 const sessions::SessionWindow &window = *(it->second);
[email protected]9744a15e2013-09-23 20:59:08130
[email protected]48bf1572014-08-12 01:31:09131 if (ShouldSkipWindow(window))
132 continue;
133
[email protected]5365e52c2013-07-31 23:07:17134 ScopedJavaLocalRef<jobject> last_pushed_window;
135 last_pushed_window.Reset(
136 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08137 env, j_session.obj(),
138 window.timestamp.ToJavaTime(),
139 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17140
zeae8f0c131d2015-08-04 23:20:27141 CopyWindowToJava(env, window, last_pushed_window);
[email protected]5365e52c2013-07-31 23:07:17142 }
143}
144
145} // namespace
146
torne89cc5d92015-09-04 11:16:35147static jlong Init(JNIEnv* env,
148 const JavaParamRef<jclass>& clazz,
149 const JavaParamRef<jobject>& profile) {
[email protected]5365e52c2013-07-31 23:07:17150 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
151 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31152 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17153}
154
155ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
blundellbbdc8a82015-10-28 15:47:20156 : profile_(profile), scoped_observer_(this) {
[email protected]5365e52c2013-07-31 23:07:17157 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
158 GetForProfile(profile);
blundellbbdc8a82015-10-28 15:47:20159
160 // NOTE: The ProfileSyncService can be null in tests.
161 if (service)
162 scoped_observer_.Add(service);
[email protected]5365e52c2013-07-31 23:07:17163}
164
165ForeignSessionHelper::~ForeignSessionHelper() {
166}
167
tornee621a272015-11-30 18:40:53168void ForeignSessionHelper::Destroy(JNIEnv* env,
169 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17170 delete this;
171}
172
tornee621a272015-11-30 18:40:53173jboolean ForeignSessionHelper::IsTabSyncEnabled(
174 JNIEnv* env,
175 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17176 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
177 GetForProfile(profile_);
178 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
179}
180
tornee621a272015-11-30 18:40:53181void ForeignSessionHelper::TriggerSessionSync(
182 JNIEnv* env,
183 const JavaParamRef<jobject>& obj) {
blundell0631ab032015-10-29 09:28:02184 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
185 GetForProfile(profile_);
186 if (!service)
187 return;
188
maxbogue5c8d0e22014-11-06 01:00:01189 const syncer::ModelTypeSet types(syncer::SESSIONS);
blundell0631ab032015-10-29 09:28:02190 service->TriggerRefresh(types);
maxbogue5c8d0e22014-11-06 01:00:01191}
192
tornee621a272015-11-30 18:40:53193void ForeignSessionHelper::SetOnForeignSessionCallback(
194 JNIEnv* env,
195 const JavaParamRef<jobject>& obj,
196 const JavaParamRef<jobject>& callback) {
[email protected]5365e52c2013-07-31 23:07:17197 callback_.Reset(env, callback);
198}
199
blundellbbdc8a82015-10-28 15:47:20200void ForeignSessionHelper::FireForeignSessionCallback() {
[email protected]5365e52c2013-07-31 23:07:17201 if (callback_.is_null())
202 return;
203
204 JNIEnv* env = AttachCurrentThread();
blundellbbdc8a82015-10-28 15:47:20205 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
206}
[email protected]5365e52c2013-07-31 23:07:17207
blundellfc39f9aa2015-10-30 08:25:54208void ForeignSessionHelper::OnSyncConfigurationCompleted() {
209 FireForeignSessionCallback();
[email protected]5365e52c2013-07-31 23:07:17210}
211
blundellfc39f9aa2015-10-30 08:25:54212void ForeignSessionHelper::OnForeignSessionUpdated() {
blundellbbdc8a82015-10-28 15:47:20213 FireForeignSessionCallback();
214}
215
tornee621a272015-11-30 18:40:53216jboolean ForeignSessionHelper::GetForeignSessions(
217 JNIEnv* env,
218 const JavaParamRef<jobject>& obj,
219 const JavaParamRef<jobject>& result) {
[email protected]a9f56622013-11-21 19:37:06220 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
221 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17222 return false;
223
droger6a118632015-06-23 17:59:45224 std::vector<const sync_driver::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06225 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17226 return false;
227
228 // Use a pref to keep track of sessions that were collapsed by the user.
229 // To prevent the pref from accumulating stale sessions, clear it each time
230 // and only add back sessions that are still current.
231 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
232 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30233 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
dchengaeceb05e2016-04-07 23:41:10234 std::unique_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17235 pref_collapsed_sessions->DeepCopy());
236 pref_collapsed_sessions->Clear();
237
238 ScopedJavaLocalRef<jobject> last_pushed_session;
[email protected]5365e52c2013-07-31 23:07:17239
240 // Note: we don't own the SyncedSessions themselves.
241 for (size_t i = 0; i < sessions.size(); ++i) {
droger6a118632015-06-23 17:59:45242 const sync_driver::SyncedSession& session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09243 if (ShouldSkipSession(session))
244 continue;
[email protected]5365e52c2013-07-31 23:07:17245
[email protected]9744a15e2013-09-23 20:59:08246 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17247
248 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08249 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17250
251 last_pushed_session.Reset(
252 Java_ForeignSessionHelper_pushSession(
253 env,
254 result,
[email protected]c2a6ac32014-04-27 00:18:09255 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
256 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08257 session.device_type,
258 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17259
zeae8f0c131d2015-08-04 23:20:27260 const std::string group_name =
261 base::FieldTrialList::FindFullName("TabSyncByRecency");
262 if (group_name == "Enabled") {
263 // Create a custom window with tabs from all windows included and ordered
264 // by recency (GetForeignSessionTabs will do ordering automatically).
265 std::vector<const sessions::SessionTab*> tabs;
266 open_tabs->GetForeignSessionTabs(session.session_tag, &tabs);
267 ScopedJavaLocalRef<jobject> last_pushed_window(
268 Java_ForeignSessionHelper_pushWindow(
269 env, last_pushed_session.obj(),
270 session.modified_time.ToJavaTime(), 0));
271 for (const sessions::SessionTab* tab : tabs) {
272 if (ShouldSkipTab(*tab))
273 continue;
274 CopyTabToJava(env, *tab, last_pushed_window);
275 }
276 } else {
277 // Push the full session, with tabs ordered by visual position.
278 CopySessionToJava(env, session, last_pushed_session);
279 }
[email protected]5365e52c2013-07-31 23:07:17280 }
281
282 return true;
283}
284
tornee621a272015-11-30 18:40:53285jboolean ForeignSessionHelper::OpenForeignSessionTab(
286 JNIEnv* env,
287 const JavaParamRef<jobject>& obj,
288 const JavaParamRef<jobject>& j_tab,
289 const JavaParamRef<jstring>& session_tag,
290 jint session_tab_id,
291 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06292 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
293 if (!open_tabs) {
294 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39295 return false;
296 }
297
skuhneb7409dcf2014-11-14 04:06:55298 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39299
[email protected]a9f56622013-11-21 19:37:06300 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
301 session_tab_id,
302 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39303 LOG(ERROR) << "Failed to load foreign tab.";
304 return false;
305 }
306
307 if (session_tab->navigations.empty()) {
308 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
309 return false;
310 }
311
312 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
313 if (!tab_android)
314 return false;
315 content::WebContents* web_contents = tab_android->web_contents();
316 if (!web_contents)
317 return false;
318
319 WindowOpenDisposition disposition =
320 static_cast<WindowOpenDisposition>(j_disposition);
321 SessionRestore::RestoreForeignSessionTab(web_contents,
322 *session_tab,
323 disposition);
324
325 return true;
326}
327
tornee621a272015-11-30 18:40:53328void ForeignSessionHelper::DeleteForeignSession(
329 JNIEnv* env,
330 const JavaParamRef<jobject>& obj,
331 const JavaParamRef<jstring>& session_tag) {
[email protected]a9f56622013-11-21 19:37:06332 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
333 if (open_tabs)
334 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17335}
336
337// static
338bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
339 return RegisterNativesImpl(env);
340}