blob: 3ed809316e65d030942c8fdd61c84fdddbb1b286 [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"
maxbogue26f40222016-09-16 20:22:1820#include "components/browser_sync/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;
maxboguea79d99b72016-09-15 15:59:1637using sync_sessions::OpenTabsUIDelegate;
38using sync_sessions::SyncedSession;
[email protected]5365e52c2013-07-31 23:07:1739
40namespace {
41
[email protected]a9f56622013-11-21 19:37:0642OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
maxbogue0a379452016-09-22 21:35:0543 browser_sync::ProfileSyncService* service =
44 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
[email protected]5365e52c2013-07-31 23:07:1745
[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) {
avi498cabca2016-09-21 19:03:5068 for (const auto& tab_ptr : window.tabs) {
69 const sessions::SessionTab& session_tab = *(tab_ptr.get());
[email protected]48bf1572014-08-12 01:31:0970 if (!ShouldSkipTab(session_tab))
71 return false;
72 }
73 return true;
74}
75
maxboguea79d99b72016-09-15 15:59:1676bool ShouldSkipSession(const SyncedSession& session) {
avi498cabca2016-09-21 19:03:5077 for (const auto& window_pair : session.windows) {
zea325508e2017-03-29 20:11:2278 const sessions::SessionWindow& window = window_pair.second->wrapped_window;
[email protected]48bf1572014-08-12 01:31:0979 if (!ShouldSkipWindow(window))
80 return false;
81 }
82 return true;
83}
84
Daniel Bratell7aacf952017-11-21 17:51:2585void JNI_ForeignSessionHelper_CopyTabToJava(
zeae8f0c131d2015-08-04 23:20:2786 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(
torne948f3662016-08-16 15:10:4499 env, j_window, ConvertUTF8ToJavaString(env, tab_url.spec()),
100 ConvertUTF16ToJavaString(env, current_navigation.title()),
101 tab.timestamp.ToJavaTime(), tab.tab_id.id());
zeae8f0c131d2015-08-04 23:20:27102}
103
Daniel Bratell7aacf952017-11-21 17:51:25104void JNI_ForeignSessionHelper_CopyWindowToJava(
[email protected]5365e52c2013-07-31 23:07:17105 JNIEnv* env,
skuhneb7409dcf2014-11-14 04:06:55106 const sessions::SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:17107 ScopedJavaLocalRef<jobject>& j_window) {
avi498cabca2016-09-21 19:03:50108 for (const auto& tab_ptr : window.tabs) {
109 const sessions::SessionTab& session_tab = *(tab_ptr.get());
[email protected]5365e52c2013-07-31 23:07:17110
[email protected]48bf1572014-08-12 01:31:09111 if (ShouldSkipTab(session_tab))
zeae8f0c131d2015-08-04 23:20:27112 return;
[email protected]48bf1572014-08-12 01:31:09113
Daniel Bratell7aacf952017-11-21 17:51:25114 JNI_ForeignSessionHelper_CopyTabToJava(env, session_tab, j_window);
[email protected]5365e52c2013-07-31 23:07:17115 }
116}
117
Daniel Bratell7aacf952017-11-21 17:51:25118void JNI_ForeignSessionHelper_CopySessionToJava(
[email protected]5365e52c2013-07-31 23:07:17119 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08120 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17121 ScopedJavaLocalRef<jobject>& j_session) {
avi498cabca2016-09-21 19:03:50122 for (const auto& window_pair : session.windows) {
zea325508e2017-03-29 20:11:22123 const sessions::SessionWindow& window = window_pair.second->wrapped_window;
[email protected]9744a15e2013-09-23 20:59:08124
[email protected]48bf1572014-08-12 01:31:09125 if (ShouldSkipWindow(window))
126 continue;
127
[email protected]5365e52c2013-07-31 23:07:17128 ScopedJavaLocalRef<jobject> last_pushed_window;
torne948f3662016-08-16 15:10:44129 last_pushed_window.Reset(Java_ForeignSessionHelper_pushWindow(
130 env, j_session, window.timestamp.ToJavaTime(), window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17131
Daniel Bratell7aacf952017-11-21 17:51:25132 JNI_ForeignSessionHelper_CopyWindowToJava(env, window, last_pushed_window);
[email protected]5365e52c2013-07-31 23:07:17133 }
134}
135
136} // namespace
137
Daniel Bratell7aacf952017-11-21 17:51:25138static jlong JNI_ForeignSessionHelper_Init(
139 JNIEnv* env,
140 const JavaParamRef<jclass>& clazz,
141 const JavaParamRef<jobject>& profile) {
[email protected]5365e52c2013-07-31 23:07:17142 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
143 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31144 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17145}
146
147ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
blundellbbdc8a82015-10-28 15:47:20148 : profile_(profile), scoped_observer_(this) {
maxbogue0a379452016-09-22 21:35:05149 browser_sync::ProfileSyncService* service =
150 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
blundellbbdc8a82015-10-28 15:47:20151
152 // NOTE: The ProfileSyncService can be null in tests.
153 if (service)
154 scoped_observer_.Add(service);
[email protected]5365e52c2013-07-31 23:07:17155}
156
157ForeignSessionHelper::~ForeignSessionHelper() {
158}
159
tornee621a272015-11-30 18:40:53160void ForeignSessionHelper::Destroy(JNIEnv* env,
161 const JavaParamRef<jobject>& obj) {
[email protected]5365e52c2013-07-31 23:07:17162 delete this;
163}
164
tornee621a272015-11-30 18:40:53165jboolean ForeignSessionHelper::IsTabSyncEnabled(
166 JNIEnv* env,
167 const JavaParamRef<jobject>& obj) {
maxbogue0a379452016-09-22 21:35:05168 browser_sync::ProfileSyncService* service =
169 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
[email protected]5365e52c2013-07-31 23:07:17170 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
171}
172
tornee621a272015-11-30 18:40:53173void ForeignSessionHelper::TriggerSessionSync(
174 JNIEnv* env,
175 const JavaParamRef<jobject>& obj) {
maxbogue0a379452016-09-22 21:35:05176 browser_sync::ProfileSyncService* service =
177 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
blundell0631ab032015-10-29 09:28:02178 if (!service)
179 return;
180
maxbogue5c8d0e22014-11-06 01:00:01181 const syncer::ModelTypeSet types(syncer::SESSIONS);
blundell0631ab032015-10-29 09:28:02182 service->TriggerRefresh(types);
maxbogue5c8d0e22014-11-06 01:00:01183}
184
tornee621a272015-11-30 18:40:53185void ForeignSessionHelper::SetOnForeignSessionCallback(
186 JNIEnv* env,
187 const JavaParamRef<jobject>& obj,
188 const JavaParamRef<jobject>& callback) {
[email protected]5365e52c2013-07-31 23:07:17189 callback_.Reset(env, callback);
190}
191
blundellbbdc8a82015-10-28 15:47:20192void ForeignSessionHelper::FireForeignSessionCallback() {
[email protected]5365e52c2013-07-31 23:07:17193 if (callback_.is_null())
194 return;
195
196 JNIEnv* env = AttachCurrentThread();
torne948f3662016-08-16 15:10:44197 Java_ForeignSessionCallback_onUpdated(env, callback_);
blundellbbdc8a82015-10-28 15:47:20198}
[email protected]5365e52c2013-07-31 23:07:17199
holte4ef35702017-02-03 03:30:10200void ForeignSessionHelper::OnSyncConfigurationCompleted(
201 syncer::SyncService* sync) {
blundellfc39f9aa2015-10-30 08:25:54202 FireForeignSessionCallback();
[email protected]5365e52c2013-07-31 23:07:17203}
204
holte4ef35702017-02-03 03:30:10205void ForeignSessionHelper::OnForeignSessionUpdated(syncer::SyncService* sync) {
blundellbbdc8a82015-10-28 15:47:20206 FireForeignSessionCallback();
207}
208
tornee621a272015-11-30 18:40:53209jboolean ForeignSessionHelper::GetForeignSessions(
210 JNIEnv* env,
211 const JavaParamRef<jobject>& obj,
212 const JavaParamRef<jobject>& result) {
[email protected]a9f56622013-11-21 19:37:06213 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
214 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17215 return false;
216
maxboguea79d99b72016-09-15 15:59:16217 std::vector<const SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06218 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17219 return false;
220
221 // Use a pref to keep track of sessions that were collapsed by the user.
222 // To prevent the pref from accumulating stale sessions, clear it each time
223 // and only add back sessions that are still current.
224 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
225 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30226 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
dchengaeceb05e2016-04-07 23:41:10227 std::unique_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17228 pref_collapsed_sessions->DeepCopy());
229 pref_collapsed_sessions->Clear();
230
231 ScopedJavaLocalRef<jobject> last_pushed_session;
[email protected]5365e52c2013-07-31 23:07:17232
233 // Note: we don't own the SyncedSessions themselves.
234 for (size_t i = 0; i < sessions.size(); ++i) {
maxboguea79d99b72016-09-15 15:59:16235 const SyncedSession& session = *(sessions[i]);
[email protected]48bf1572014-08-12 01:31:09236 if (ShouldSkipSession(session))
237 continue;
[email protected]5365e52c2013-07-31 23:07:17238
[email protected]9744a15e2013-09-23 20:59:08239 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
[email protected]5365e52c2013-07-31 23:07:17240
241 if (is_collapsed)
[email protected]9744a15e2013-09-23 20:59:08242 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
[email protected]5365e52c2013-07-31 23:07:17243
torne948f3662016-08-16 15:10:44244 last_pushed_session.Reset(Java_ForeignSessionHelper_pushSession(
245 env, result, ConvertUTF8ToJavaString(env, session.session_tag),
246 ConvertUTF8ToJavaString(env, session.session_name), session.device_type,
247 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17248
zeae8f0c131d2015-08-04 23:20:27249 const std::string group_name =
250 base::FieldTrialList::FindFullName("TabSyncByRecency");
251 if (group_name == "Enabled") {
252 // Create a custom window with tabs from all windows included and ordered
253 // by recency (GetForeignSessionTabs will do ordering automatically).
254 std::vector<const sessions::SessionTab*> tabs;
255 open_tabs->GetForeignSessionTabs(session.session_tag, &tabs);
256 ScopedJavaLocalRef<jobject> last_pushed_window(
257 Java_ForeignSessionHelper_pushWindow(
torne948f3662016-08-16 15:10:44258 env, last_pushed_session, session.modified_time.ToJavaTime(), 0));
zeae8f0c131d2015-08-04 23:20:27259 for (const sessions::SessionTab* tab : tabs) {
260 if (ShouldSkipTab(*tab))
261 continue;
Daniel Bratell7aacf952017-11-21 17:51:25262 JNI_ForeignSessionHelper_CopyTabToJava(env, *tab, last_pushed_window);
zeae8f0c131d2015-08-04 23:20:27263 }
264 } else {
265 // Push the full session, with tabs ordered by visual position.
Daniel Bratell7aacf952017-11-21 17:51:25266 JNI_ForeignSessionHelper_CopySessionToJava(env, session,
267 last_pushed_session);
zeae8f0c131d2015-08-04 23:20:27268 }
[email protected]5365e52c2013-07-31 23:07:17269 }
270
271 return true;
272}
273
tornee621a272015-11-30 18:40:53274jboolean ForeignSessionHelper::OpenForeignSessionTab(
275 JNIEnv* env,
276 const JavaParamRef<jobject>& obj,
277 const JavaParamRef<jobject>& j_tab,
278 const JavaParamRef<jstring>& session_tag,
279 jint session_tab_id,
280 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06281 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
282 if (!open_tabs) {
283 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39284 return false;
285 }
286
skuhneb7409dcf2014-11-14 04:06:55287 const sessions::SessionTab* session_tab;
[email protected]816939d2013-11-13 00:57:39288
[email protected]a9f56622013-11-21 19:37:06289 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
Mikel Astize023c572018-03-28 07:56:56290 SessionID::FromSerializedValue(session_tab_id),
[email protected]a9f56622013-11-21 19:37:06291 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39292 LOG(ERROR) << "Failed to load foreign tab.";
293 return false;
294 }
295
296 if (session_tab->navigations.empty()) {
297 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
298 return false;
299 }
300
301 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
302 if (!tab_android)
303 return false;
304 content::WebContents* web_contents = tab_android->web_contents();
305 if (!web_contents)
306 return false;
307
308 WindowOpenDisposition disposition =
309 static_cast<WindowOpenDisposition>(j_disposition);
310 SessionRestore::RestoreForeignSessionTab(web_contents,
311 *session_tab,
312 disposition);
313
314 return true;
315}
316
tornee621a272015-11-30 18:40:53317void ForeignSessionHelper::DeleteForeignSession(
318 JNIEnv* env,
319 const JavaParamRef<jobject>& obj,
320 const JavaParamRef<jstring>& session_tag) {
[email protected]a9f56622013-11-21 19:37:06321 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
322 if (open_tabs)
323 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17324}