blob: 8d69b6dc6604ec989bf4897ca9da51c869c8b83e [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"
Sebastien Marchandf1349f52019-01-25 03:16:4111#include "base/bind.h"
Andrew Grieve4a42c22e2019-06-24 16:14:2912#include "chrome/android/chrome_jni_headers/ForeignSessionHelper_jni.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"
Mikel Astiz0dd1e66b2018-11-12 10:08:5718#include "chrome/browser/sync/session_sync_service_factory.h"
[email protected]5365e52c2013-07-31 23:07:1719#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"
brettwb1fc1b82016-02-02 00:19:0823#include "components/prefs/pref_service.h"
24#include "components/prefs/scoped_user_pref_update.h"
Marc Treib32a98792019-02-10 19:26:4625#include "components/sync/driver/sync_service.h"
maxbogue8ef25082015-11-16 19:09:5826#include "components/sync_sessions/open_tabs_ui_delegate.h"
Mikel Astiz0dd1e66b2018-11-12 10:08:5727#include "components/sync_sessions/session_sync_service.h"
maxbogue5c8d0e22014-11-06 01:00:0128#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_service.h"
[email protected]5365e52c2013-07-31 23:07:1730#include "content/public/browser/notification_source.h"
31#include "content/public/browser/web_contents.h"
[email protected]5365e52c2013-07-31 23:07:1732
torne86560112016-08-04 15:59:0433using base::android::JavaParamRef;
[email protected]5365e52c2013-07-31 23:07:1734using base::android::ScopedJavaGlobalRef;
35using base::android::ScopedJavaLocalRef;
36using base::android::AttachCurrentThread;
37using base::android::ConvertUTF16ToJavaString;
38using base::android::ConvertUTF8ToJavaString;
39using base::android::ConvertJavaStringToUTF8;
maxboguea79d99b72016-09-15 15:59:1640using sync_sessions::OpenTabsUIDelegate;
41using sync_sessions::SyncedSession;
[email protected]5365e52c2013-07-31 23:07:1742
43namespace {
44
[email protected]a9f56622013-11-21 19:37:0645OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
Mikel Astiz0dd1e66b2018-11-12 10:08:5746 sync_sessions::SessionSyncService* service =
47 SessionSyncServiceFactory::GetInstance()->GetForProfile(profile);
[email protected]5365e52c2013-07-31 23:07:1748
Mikel Astiz0dd1e66b2018-11-12 10:08:5749 // Only return the delegate if it exists.
50 if (!service)
[email protected]5365e52c2013-07-31 23:07:1751 return NULL;
52
[email protected]a9f56622013-11-21 19:37:0653 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1754}
55
skuhneb7409dcf2014-11-14 04:06:5556bool ShouldSkipTab(const sessions::SessionTab& session_tab) {
[email protected]48bf1572014-08-12 01:31:0957 if (session_tab.navigations.empty())
58 return true;
59
maxbogue4157d2b2014-08-27 21:56:4860 int selected_index = session_tab.normalized_navigation_index();
zeae8f0c131d2015-08-04 23:20:2761 const sessions::SerializedNavigationEntry& current_navigation =
[email protected]48bf1572014-08-12 01:31:0962 session_tab.navigations.at(selected_index);
63
64 if (current_navigation.virtual_url().is_empty())
65 return true;
66
67 return false;
68}
69
skuhneb7409dcf2014-11-14 04:06:5570bool ShouldSkipWindow(const sessions::SessionWindow& window) {
avi498cabca2016-09-21 19:03:5071 for (const auto& tab_ptr : window.tabs) {
72 const sessions::SessionTab& session_tab = *(tab_ptr.get());
[email protected]48bf1572014-08-12 01:31:0973 if (!ShouldSkipTab(session_tab))
74 return false;
75 }
76 return true;
77}
78
maxboguea79d99b72016-09-15 15:59:1679bool ShouldSkipSession(const SyncedSession& session) {
avi498cabca2016-09-21 19:03:5080 for (const auto& window_pair : session.windows) {
zea325508e2017-03-29 20:11:2281 const sessions::SessionWindow& window = window_pair.second->wrapped_window;
[email protected]48bf1572014-08-12 01:31:0982 if (!ShouldSkipWindow(window))
83 return false;
84 }
85 return true;
86}
87
Daniel Bratell7aacf952017-11-21 17:51:2588void JNI_ForeignSessionHelper_CopyTabToJava(
zeae8f0c131d2015-08-04 23:20:2789 JNIEnv* env,
90 const sessions::SessionTab& tab,
91 ScopedJavaLocalRef<jobject>& j_window) {
92 int selected_index = tab.normalized_navigation_index();
93 DCHECK_GE(selected_index, 0);
94 DCHECK_LT(selected_index, static_cast<int>(tab.navigations.size()));
95
96 const sessions::SerializedNavigationEntry& current_navigation =
97 tab.navigations.at(selected_index);
98
99 GURL tab_url = current_navigation.virtual_url();
100
101 Java_ForeignSessionHelper_pushTab(
torne948f3662016-08-16 15:10:44102 env, j_window, ConvertUTF8ToJavaString(env, tab_url.spec()),
103 ConvertUTF16ToJavaString(env, current_navigation.title()),
104 tab.timestamp.ToJavaTime(), tab.tab_id.id());
zeae8f0c131d2015-08-04 23:20:27105}
106
Daniel Bratell7aacf952017-11-21 17:51:25107void JNI_ForeignSessionHelper_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) {
avi498cabca2016-09-21 19:03:50111 for (const auto& tab_ptr : window.tabs) {
112 const sessions::SessionTab& session_tab = *(tab_ptr.get());
[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
Daniel Bratell7aacf952017-11-21 17:51:25117 JNI_ForeignSessionHelper_CopyTabToJava(env, session_tab, j_window);
[email protected]5365e52c2013-07-31 23:07:17118 }
119}
120
Daniel Bratell7aacf952017-11-21 17:51:25121void JNI_ForeignSessionHelper_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) {
avi498cabca2016-09-21 19:03:50125 for (const auto& window_pair : session.windows) {
zea325508e2017-03-29 20:11:22126 const sessions::SessionWindow& window = window_pair.second->wrapped_window;
[email protected]9744a15e2013-09-23 20:59:08127
[email protected]48bf1572014-08-12 01:31:09128 if (ShouldSkipWindow(window))
129 continue;
130
[email protected]5365e52c2013-07-31 23:07:17131 ScopedJavaLocalRef<jobject> last_pushed_window;
torne948f3662016-08-16 15:10:44132 last_pushed_window.Reset(Java_ForeignSessionHelper_pushWindow(
133 env, j_session, window.timestamp.ToJavaTime(), window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17134
Daniel Bratell7aacf952017-11-21 17:51:25135 JNI_ForeignSessionHelper_CopyWindowToJava(env, window, last_pushed_window);
[email protected]5365e52c2013-07-31 23:07:17136 }
137}
138
139} // namespace
140
Daniel Bratell7aacf952017-11-21 17:51:25141static jlong JNI_ForeignSessionHelper_Init(
142 JNIEnv* env,
Daniel Bratell7aacf952017-11-21 17:51:25143 const JavaParamRef<jobject>& profile) {
[email protected]5365e52c2013-07-31 23:07:17144 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
145 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31146 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17147}
148
149ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
Mikel Astiz0dd1e66b2018-11-12 10:08:57150 : profile_(profile) {
151 sync_sessions::SessionSyncService* service =
152 SessionSyncServiceFactory::GetInstance()->GetForProfile(profile);
blundellbbdc8a82015-10-28 15:47:20153
Mikel Astiz0dd1e66b2018-11-12 10:08:57154 // NOTE: The SessionSyncService can be null in tests.
155 if (service) {
156 // base::Unretained() is safe below because the subscription itself is a
157 // class member field and handles destruction well.
158 foreign_session_updated_subscription_ =
159 service->SubscribeToForeignSessionsChanged(base::BindRepeating(
160 &ForeignSessionHelper::FireForeignSessionCallback,
161 base::Unretained(this)));
162 }
[email protected]5365e52c2013-07-31 23:07:17163}
164
165ForeignSessionHelper::~ForeignSessionHelper() {
166}
167
Eric Stevensond6b8ada2019-07-26 21:26:06168void ForeignSessionHelper::Destroy(JNIEnv* env) {
[email protected]5365e52c2013-07-31 23:07:17169 delete this;
170}
171
Eric Stevensond6b8ada2019-07-26 21:26:06172jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env) {
Mikel Astiz0dd1e66b2018-11-12 10:08:57173 sync_sessions::SessionSyncService* service =
174 SessionSyncServiceFactory::GetInstance()->GetForProfile(profile_);
175 return service && service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:17176}
177
Eric Stevensond6b8ada2019-07-26 21:26:06178void ForeignSessionHelper::TriggerSessionSync(JNIEnv* env) {
Marc Treib32a98792019-02-10 19:26:46179 syncer::SyncService* service =
Marc Treib205e9862019-02-27 21:43:00180 ProfileSyncServiceFactory::GetForProfile(profile_);
blundell0631ab032015-10-29 09:28:02181 if (!service)
182 return;
183
Marc Treib32a98792019-02-10 19:26:46184 service->TriggerRefresh({syncer::SESSIONS});
maxbogue5c8d0e22014-11-06 01:00:01185}
186
tornee621a272015-11-30 18:40:53187void ForeignSessionHelper::SetOnForeignSessionCallback(
188 JNIEnv* env,
tornee621a272015-11-30 18:40:53189 const JavaParamRef<jobject>& callback) {
[email protected]5365e52c2013-07-31 23:07:17190 callback_.Reset(env, callback);
191}
192
blundellbbdc8a82015-10-28 15:47:20193void ForeignSessionHelper::FireForeignSessionCallback() {
[email protected]5365e52c2013-07-31 23:07:17194 if (callback_.is_null())
195 return;
196
197 JNIEnv* env = AttachCurrentThread();
torne948f3662016-08-16 15:10:44198 Java_ForeignSessionCallback_onUpdated(env, callback_);
blundellbbdc8a82015-10-28 15:47:20199}
[email protected]5365e52c2013-07-31 23:07:17200
tornee621a272015-11-30 18:40:53201jboolean ForeignSessionHelper::GetForeignSessions(
202 JNIEnv* env,
tornee621a272015-11-30 18:40:53203 const JavaParamRef<jobject>& result) {
[email protected]a9f56622013-11-21 19:37:06204 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
205 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17206 return false;
207
maxboguea79d99b72016-09-15 15:59:16208 std::vector<const SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06209 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17210 return false;
211
212 // Use a pref to keep track of sessions that were collapsed by the user.
213 // To prevent the pref from accumulating stale sessions, clear it each time
214 // and only add back sessions that are still current.
215 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
216 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30217 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
dchengaeceb05e2016-04-07 23:41:10218 std::unique_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17219 pref_collapsed_sessions->DeepCopy());
220 pref_collapsed_sessions->Clear();
221
222 ScopedJavaLocalRef<jobject> last_pushed_session;
[email protected]5365e52c2013-07-31 23:07:17223
224 // Note: we don't own the SyncedSessions themselves.
225 for (size_t i = 0; i < sessions.size(); ++i) {
maxboguea79d99b72016-09-15 15:59:16226 const SyncedSession& session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09227 if (ShouldSkipSession(session))
228 continue;
[email protected]5365e52c2013-07-31 23:07:17229
[email protected]9744a15e2013-09-23 20:59:08230 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17231
232 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08233 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17234
torne948f3662016-08-16 15:10:44235 last_pushed_session.Reset(Java_ForeignSessionHelper_pushSession(
236 env, result, ConvertUTF8ToJavaString(env, session.session_tag),
237 ConvertUTF8ToJavaString(env, session.session_name), session.device_type,
238 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17239
Mikel Astizb1513e62018-04-05 16:38:13240 // Push the full session, with tabs ordered by visual position.
241 JNI_ForeignSessionHelper_CopySessionToJava(env, session,
242 last_pushed_session);
[email protected]5365e52c2013-07-31 23:07:17243 }
244
245 return true;
246}
247
tornee621a272015-11-30 18:40:53248jboolean ForeignSessionHelper::OpenForeignSessionTab(
249 JNIEnv* env,
tornee621a272015-11-30 18:40:53250 const JavaParamRef<jobject>& j_tab,
251 const JavaParamRef<jstring>& session_tag,
252 jint session_tab_id,
253 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06254 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
255 if (!open_tabs) {
256 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39257 return false;
258 }
259
skuhneb7409dcf2014-11-14 04:06:55260 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39261
[email protected]a9f56622013-11-21 19:37:06262 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
Mikel Astize023c572018-03-28 07:56:56263 SessionID::FromSerializedValue(session_tab_id),
[email protected]a9f56622013-11-21 19:37:06264 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39265 LOG(ERROR) << "Failed to load foreign tab.";
266 return false;
267 }
268
269 if (session_tab->navigations.empty()) {
270 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
271 return false;
272 }
273
274 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
275 if (!tab_android)
276 return false;
277 content::WebContents* web_contents = tab_android->web_contents();
278 if (!web_contents)
279 return false;
280
281 WindowOpenDisposition disposition =
282 static_cast<WindowOpenDisposition>(j_disposition);
283 SessionRestore::RestoreForeignSessionTab(web_contents,
284 *session_tab,
285 disposition);
286
287 return true;
288}
289
tornee621a272015-11-30 18:40:53290void ForeignSessionHelper::DeleteForeignSession(
291 JNIEnv* env,
tornee621a272015-11-30 18:40:53292 const JavaParamRef<jstring>& session_tag) {
[email protected]a9f56622013-11-21 19:37:06293 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
294 if (open_tabs)
295 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17296}
Tanja Gornak9086f1a2018-12-05 08:23:51297
298void ForeignSessionHelper::SetInvalidationsForSessionsEnabled(
299 JNIEnv* env,
Tanja Gornak9086f1a2018-12-05 08:23:51300 jboolean enabled) {
Marc Treib32a98792019-02-10 19:26:46301 syncer::SyncService* service =
Marc Treib205e9862019-02-27 21:43:00302 ProfileSyncServiceFactory::GetForProfile(profile_);
Tanja Gornak9086f1a2018-12-05 08:23:51303 if (!service)
304 return;
305
306 service->SetInvalidationsForSessionsEnabled(enabled);
307}