blob: f1b35656673af4d3bef953110ac605f67e658dce [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]a9f56622013-11-21 19:37:0616#include "chrome/browser/sync/open_tabs_ui_delegate.h"
[email protected]5365e52c2013-07-31 23:07:1717#include "chrome/browser/sync/profile_sync_service.h"
18#include "chrome/browser/sync/profile_sync_service_factory.h"
19#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"
23#include "content/public/browser/notification_source.h"
24#include "content/public/browser/web_contents.h"
25#include "jni/ForeignSessionHelper_jni.h"
26
27using base::android::ScopedJavaGlobalRef;
28using base::android::ScopedJavaLocalRef;
29using base::android::AttachCurrentThread;
30using base::android::ConvertUTF16ToJavaString;
31using base::android::ConvertUTF8ToJavaString;
32using base::android::ConvertJavaStringToUTF8;
[email protected]a9f56622013-11-21 19:37:0633using browser_sync::OpenTabsUIDelegate;
[email protected]5365e52c2013-07-31 23:07:1734using browser_sync::SyncedSession;
35
36namespace {
37
[email protected]a9f56622013-11-21 19:37:0638OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
[email protected]5365e52c2013-07-31 23:07:1739 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
40 GetForProfile(profile);
41
[email protected]a9f56622013-11-21 19:37:0642 // Only return the delegate if it exists and it is done syncing sessions.
[email protected]5365e52c2013-07-31 23:07:1743 if (!service || !service->ShouldPushChanges())
44 return NULL;
45
[email protected]a9f56622013-11-21 19:37:0646 return service->GetOpenTabsUIDelegate();
[email protected]5365e52c2013-07-31 23:07:1747}
48
[email protected]816939d2013-11-13 00:57:3949bool ShouldSkipTab(const SessionTab& session_tab) {
50 if (session_tab.navigations.empty())
[email protected]9744a15e2013-09-23 20:59:0851 return true;
52
[email protected]816939d2013-11-13 00:57:3953 int selected_index = session_tab.current_navigation_index;
[email protected]9744a15e2013-09-23 20:59:0854 if (selected_index < 0 ||
[email protected]816939d2013-11-13 00:57:3955 selected_index >= static_cast<int>(session_tab.navigations.size()))
[email protected]9744a15e2013-09-23 20:59:0856 return true;
57
[email protected]c068ceb2013-10-30 09:30:1858 const ::sessions::SerializedNavigationEntry& current_navigation =
[email protected]816939d2013-11-13 00:57:3959 session_tab.navigations.at(selected_index);
[email protected]c068ceb2013-10-30 09:30:1860
61 if (current_navigation.virtual_url().is_empty())
62 return true;
63
[email protected]9744a15e2013-09-23 20:59:0864 return false;
65}
66
67bool ShouldSkipWindow(const SessionWindow& window) {
68 for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
69 tab_it != window.tabs.end(); ++tab_it) {
[email protected]816939d2013-11-13 00:57:3970 const SessionTab &session_tab = **tab_it;
71 if (!ShouldSkipTab(session_tab))
[email protected]9744a15e2013-09-23 20:59:0872 return false;
73 }
74 return true;
75}
76
77bool ShouldSkipSession(const browser_sync::SyncedSession& session) {
78 for (SyncedSession::SyncedWindowMap::const_iterator it =
79 session.windows.begin(); it != session.windows.end(); ++it) {
80 const SessionWindow &window = *(it->second);
81 if (!ShouldSkipWindow(window))
82 return false;
83 }
84 return true;
85}
86
[email protected]5365e52c2013-07-31 23:07:1787void CopyTabsToJava(
88 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:0889 const SessionWindow& window,
[email protected]5365e52c2013-07-31 23:07:1790 ScopedJavaLocalRef<jobject>& j_window) {
[email protected]9744a15e2013-09-23 20:59:0891 for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
92 tab_it != window.tabs.end(); ++tab_it) {
[email protected]816939d2013-11-13 00:57:3993 const SessionTab &session_tab = **tab_it;
[email protected]5365e52c2013-07-31 23:07:1794
[email protected]816939d2013-11-13 00:57:3995 if (ShouldSkipTab(session_tab))
[email protected]5365e52c2013-07-31 23:07:1796 continue;
97
[email protected]816939d2013-11-13 00:57:3998 int selected_index = session_tab.current_navigation_index;
[email protected]9744a15e2013-09-23 20:59:0899 DCHECK(selected_index >= 0);
[email protected]816939d2013-11-13 00:57:39100 DCHECK(selected_index < static_cast<int>(session_tab.navigations.size()));
[email protected]9744a15e2013-09-23 20:59:08101
[email protected]5365e52c2013-07-31 23:07:17102 const ::sessions::SerializedNavigationEntry& current_navigation =
[email protected]816939d2013-11-13 00:57:39103 session_tab.navigations.at(selected_index);
[email protected]5365e52c2013-07-31 23:07:17104
105 GURL tab_url = current_navigation.virtual_url();
[email protected]5365e52c2013-07-31 23:07:17106
107 Java_ForeignSessionHelper_pushTab(
108 env, j_window.obj(),
[email protected]c2a6ac32014-04-27 00:18:09109 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
110 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
[email protected]816939d2013-11-13 00:57:39111 session_tab.timestamp.ToJavaTime(),
112 session_tab.tab_id.id());
[email protected]5365e52c2013-07-31 23:07:17113 }
114}
115
116void CopyWindowsToJava(
117 JNIEnv* env,
[email protected]9744a15e2013-09-23 20:59:08118 const SyncedSession& session,
[email protected]5365e52c2013-07-31 23:07:17119 ScopedJavaLocalRef<jobject>& j_session) {
120 for (SyncedSession::SyncedWindowMap::const_iterator it =
[email protected]9744a15e2013-09-23 20:59:08121 session.windows.begin(); it != session.windows.end(); ++it) {
122 const SessionWindow &window = *(it->second);
123
124 if (ShouldSkipWindow(window))
125 continue;
[email protected]5365e52c2013-07-31 23:07:17126
127 ScopedJavaLocalRef<jobject> last_pushed_window;
128 last_pushed_window.Reset(
129 Java_ForeignSessionHelper_pushWindow(
[email protected]9744a15e2013-09-23 20:59:08130 env, j_session.obj(),
131 window.timestamp.ToJavaTime(),
132 window.window_id.id()));
[email protected]5365e52c2013-07-31 23:07:17133
134 CopyTabsToJava(env, window, last_pushed_window);
135 }
136}
137
138} // namespace
139
[email protected]d557b6042013-11-21 16:34:31140static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
[email protected]5365e52c2013-07-31 23:07:17141 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
142 ProfileAndroid::FromProfileAndroid(profile));
[email protected]d557b6042013-11-21 16:34:31143 return reinterpret_cast<intptr_t>(foreign_session_helper);
[email protected]5365e52c2013-07-31 23:07:17144}
145
146ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
147 : profile_(profile) {
148 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
149 GetForProfile(profile);
[email protected]5365e52c2013-07-31 23:07:17150 registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
151 content::Source<ProfileSyncService>(service));
152 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
153 content::Source<Profile>(profile));
154 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
155 content::Source<Profile>(profile));
156}
157
158ForeignSessionHelper::~ForeignSessionHelper() {
159}
160
161void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
162 delete this;
163}
164
165jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) {
166 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
167 GetForProfile(profile_);
168 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
169}
170
171void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
172 jobject obj,
173 jobject callback) {
174 callback_.Reset(env, callback);
175}
176
177void ForeignSessionHelper::Observe(
178 int type, const content::NotificationSource& source,
179 const content::NotificationDetails& details) {
180 if (callback_.is_null())
181 return;
182
183 JNIEnv* env = AttachCurrentThread();
184
185 switch (type) {
186 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
187 // Tab sync is disabled, so clean up data about collapsed sessions.
188 profile_->GetPrefs()->ClearPref(
189 prefs::kNtpCollapsedForeignSessions);
190 // Purposeful fall through.
191 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
192 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
193 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
194 break;
195 default:
196 NOTREACHED();
197 }
198}
199
200jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
201 jobject obj,
202 jobject result) {
[email protected]a9f56622013-11-21 19:37:06203 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
204 if (!open_tabs)
[email protected]5365e52c2013-07-31 23:07:17205 return false;
206
207 std::vector<const browser_sync::SyncedSession*> sessions;
[email protected]a9f56622013-11-21 19:37:06208 if (!open_tabs->GetAllForeignSessions(&sessions))
[email protected]5365e52c2013-07-31 23:07:17209 return false;
210
211 // Use a pref to keep track of sessions that were collapsed by the user.
212 // To prevent the pref from accumulating stale sessions, clear it each time
213 // and only add back sessions that are still current.
214 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
215 prefs::kNtpCollapsedForeignSessions);
[email protected]5bcdd99d2013-12-23 18:28:30216 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
217 scoped_ptr<base::DictionaryValue> collapsed_sessions(
[email protected]5365e52c2013-07-31 23:07:17218 pref_collapsed_sessions->DeepCopy());
219 pref_collapsed_sessions->Clear();
220
221 ScopedJavaLocalRef<jobject> last_pushed_session;
222 ScopedJavaLocalRef<jobject> last_pushed_window;
223
224 // Note: we don't own the SyncedSessions themselves.
225 for (size_t i = 0; i < sessions.size(); ++i) {
[email protected]9744a15e2013-09-23 20:59:08226 const browser_sync::SyncedSession &session = *(sessions[i]);
227 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
235 last_pushed_session.Reset(
236 Java_ForeignSessionHelper_pushSession(
237 env,
238 result,
[email protected]c2a6ac32014-04-27 00:18:09239 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
240 ConvertUTF8ToJavaString(env, session.session_name).obj(),
[email protected]9744a15e2013-09-23 20:59:08241 session.device_type,
242 session.modified_time.ToJavaTime()));
[email protected]5365e52c2013-07-31 23:07:17243
244 CopyWindowsToJava(env, session, last_pushed_session);
245 }
246
247 return true;
248}
249
[email protected]816939d2013-11-13 00:57:39250jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
251 jobject obj,
252 jobject j_tab,
253 jstring session_tag,
254 jint session_tab_id,
255 jint j_disposition) {
[email protected]a9f56622013-11-21 19:37:06256 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
257 if (!open_tabs) {
258 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
[email protected]816939d2013-11-13 00:57:39259 return false;
260 }
261
262 const SessionTab* session_tab;
263
[email protected]a9f56622013-11-21 19:37:06264 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
265 session_tab_id,
266 &session_tab)) {
[email protected]816939d2013-11-13 00:57:39267 LOG(ERROR) << "Failed to load foreign tab.";
268 return false;
269 }
270
271 if (session_tab->navigations.empty()) {
272 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
273 return false;
274 }
275
276 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
277 if (!tab_android)
278 return false;
279 content::WebContents* web_contents = tab_android->web_contents();
280 if (!web_contents)
281 return false;
282
283 WindowOpenDisposition disposition =
284 static_cast<WindowOpenDisposition>(j_disposition);
285 SessionRestore::RestoreForeignSessionTab(web_contents,
286 *session_tab,
287 disposition);
288
289 return true;
290}
291
[email protected]5365e52c2013-07-31 23:07:17292void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
293 jstring session_tag) {
[email protected]a9f56622013-11-21 19:37:06294 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
295 if (open_tabs)
296 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
[email protected]5365e52c2013-07-31 23:07:17297}
298
299// static
300bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
301 return RegisterNativesImpl(env);
302}