ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / android / foreign_session_helper.cc
blobef4a5dc3de0e048b1779da02cf598ed64f166a4d
1 // 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.
5 #include "chrome/browser/android/foreign_session_helper.h"
7 #include <jni.h>
9 #include "base/android/jni_string.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "chrome/browser/android/tab_android.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/profiles/profile_android.h"
15 #include "chrome/browser/sessions/session_restore.h"
16 #include "chrome/browser/sync/profile_sync_service.h"
17 #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"
22 #include "components/sync_driver/open_tabs_ui_delegate.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "jni/ForeignSessionHelper_jni.h"
29 using base::android::ScopedJavaGlobalRef;
30 using base::android::ScopedJavaLocalRef;
31 using base::android::AttachCurrentThread;
32 using base::android::ConvertUTF16ToJavaString;
33 using base::android::ConvertUTF8ToJavaString;
34 using base::android::ConvertJavaStringToUTF8;
35 using sync_driver::OpenTabsUIDelegate;
36 using sync_driver::SyncedSession;
38 namespace {
40 OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
41 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
42 GetForProfile(profile);
44 // Only return the delegate if it exists and it is done syncing sessions.
45 if (!service || !service->IsSyncActive())
46 return NULL;
48 return service->GetOpenTabsUIDelegate();
51 bool ShouldSkipTab(const sessions::SessionTab& session_tab) {
52 if (session_tab.navigations.empty())
53 return true;
55 int selected_index = session_tab.normalized_navigation_index();
56 const sessions::SerializedNavigationEntry& current_navigation =
57 session_tab.navigations.at(selected_index);
59 if (current_navigation.virtual_url().is_empty())
60 return true;
62 return false;
65 bool ShouldSkipWindow(const sessions::SessionWindow& window) {
66 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
67 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
68 const sessions::SessionTab &session_tab = **tab_it;
69 if (!ShouldSkipTab(session_tab))
70 return false;
72 return true;
75 bool ShouldSkipSession(const sync_driver::SyncedSession& session) {
76 for (SyncedSession::SyncedWindowMap::const_iterator it =
77 session.windows.begin(); it != session.windows.end(); ++it) {
78 const sessions::SessionWindow &window = *(it->second);
79 if (!ShouldSkipWindow(window))
80 return false;
82 return true;
85 void CopyTabToJava(
86 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()));
93 const sessions::SerializedNavigationEntry& current_navigation =
94 tab.navigations.at(selected_index);
96 GURL tab_url = current_navigation.virtual_url();
98 Java_ForeignSessionHelper_pushTab(
99 env, j_window.obj(),
100 ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
101 ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
102 tab.timestamp.ToJavaTime(),
103 tab.tab_id.id());
106 void CopyWindowToJava(
107 JNIEnv* env,
108 const sessions::SessionWindow& window,
109 ScopedJavaLocalRef<jobject>& j_window) {
110 for (std::vector<sessions::SessionTab*>::const_iterator tab_it =
111 window.tabs.begin(); tab_it != window.tabs.end(); ++tab_it) {
112 const sessions::SessionTab &session_tab = **tab_it;
114 if (ShouldSkipTab(session_tab))
115 return;
117 CopyTabToJava(env, session_tab, j_window);
121 void CopySessionToJava(
122 JNIEnv* env,
123 const SyncedSession& session,
124 ScopedJavaLocalRef<jobject>& j_session) {
125 for (SyncedSession::SyncedWindowMap::const_iterator it =
126 session.windows.begin(); it != session.windows.end(); ++it) {
127 const sessions::SessionWindow &window = *(it->second);
129 if (ShouldSkipWindow(window))
130 continue;
132 ScopedJavaLocalRef<jobject> last_pushed_window;
133 last_pushed_window.Reset(
134 Java_ForeignSessionHelper_pushWindow(
135 env, j_session.obj(),
136 window.timestamp.ToJavaTime(),
137 window.window_id.id()));
139 CopyWindowToJava(env, window, last_pushed_window);
143 } // namespace
145 static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
146 ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
147 ProfileAndroid::FromProfileAndroid(profile));
148 return reinterpret_cast<intptr_t>(foreign_session_helper);
151 ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
152 : profile_(profile) {
153 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
154 GetForProfile(profile);
155 registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
156 content::Source<ProfileSyncService>(service));
157 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
158 content::Source<Profile>(profile));
159 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
160 content::Source<Profile>(profile));
163 ForeignSessionHelper::~ForeignSessionHelper() {
166 void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
167 delete this;
170 jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) {
171 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
172 GetForProfile(profile_);
173 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
176 void ForeignSessionHelper::TriggerSessionSync(JNIEnv* env, jobject obj) {
177 const syncer::ModelTypeSet types(syncer::SESSIONS);
178 content::NotificationService::current()->Notify(
179 chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
180 content::Source<Profile>(profile_),
181 content::Details<const syncer::ModelTypeSet>(&types));
184 void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
185 jobject obj,
186 jobject callback) {
187 callback_.Reset(env, callback);
190 void ForeignSessionHelper::Observe(
191 int type, const content::NotificationSource& source,
192 const content::NotificationDetails& details) {
193 if (callback_.is_null())
194 return;
196 JNIEnv* env = AttachCurrentThread();
198 switch (type) {
199 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
200 // Tab sync is disabled, so clean up data about collapsed sessions.
201 profile_->GetPrefs()->ClearPref(
202 prefs::kNtpCollapsedForeignSessions);
203 // Purposeful fall through.
204 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
205 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
206 Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
207 break;
208 default:
209 NOTREACHED();
213 jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
214 jobject obj,
215 jobject result) {
216 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
217 if (!open_tabs)
218 return false;
220 std::vector<const sync_driver::SyncedSession*> sessions;
221 if (!open_tabs->GetAllForeignSessions(&sessions))
222 return false;
224 // Use a pref to keep track of sessions that were collapsed by the user.
225 // To prevent the pref from accumulating stale sessions, clear it each time
226 // and only add back sessions that are still current.
227 DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
228 prefs::kNtpCollapsedForeignSessions);
229 base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
230 scoped_ptr<base::DictionaryValue> collapsed_sessions(
231 pref_collapsed_sessions->DeepCopy());
232 pref_collapsed_sessions->Clear();
234 ScopedJavaLocalRef<jobject> last_pushed_session;
236 // Note: we don't own the SyncedSessions themselves.
237 for (size_t i = 0; i < sessions.size(); ++i) {
238 const sync_driver::SyncedSession& session = *(sessions[i]);
239 if (ShouldSkipSession(session))
240 continue;
242 const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
244 if (is_collapsed)
245 pref_collapsed_sessions->SetBoolean(session.session_tag, true);
247 last_pushed_session.Reset(
248 Java_ForeignSessionHelper_pushSession(
249 env,
250 result,
251 ConvertUTF8ToJavaString(env, session.session_tag).obj(),
252 ConvertUTF8ToJavaString(env, session.session_name).obj(),
253 session.device_type,
254 session.modified_time.ToJavaTime()));
256 const std::string group_name =
257 base::FieldTrialList::FindFullName("TabSyncByRecency");
258 if (group_name == "Enabled") {
259 // Create a custom window with tabs from all windows included and ordered
260 // by recency (GetForeignSessionTabs will do ordering automatically).
261 std::vector<const sessions::SessionTab*> tabs;
262 open_tabs->GetForeignSessionTabs(session.session_tag, &tabs);
263 ScopedJavaLocalRef<jobject> last_pushed_window(
264 Java_ForeignSessionHelper_pushWindow(
265 env, last_pushed_session.obj(),
266 session.modified_time.ToJavaTime(), 0));
267 for (const sessions::SessionTab* tab : tabs) {
268 if (ShouldSkipTab(*tab))
269 continue;
270 CopyTabToJava(env, *tab, last_pushed_window);
272 } else {
273 // Push the full session, with tabs ordered by visual position.
274 CopySessionToJava(env, session, last_pushed_session);
278 return true;
281 jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
282 jobject obj,
283 jobject j_tab,
284 jstring session_tag,
285 jint session_tab_id,
286 jint j_disposition) {
287 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
288 if (!open_tabs) {
289 LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
290 return false;
293 const sessions::SessionTab* session_tab;
295 if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
296 session_tab_id,
297 &session_tab)) {
298 LOG(ERROR) << "Failed to load foreign tab.";
299 return false;
302 if (session_tab->navigations.empty()) {
303 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
304 return false;
307 TabAndroid* tab_android = TabAndroid::GetNativeTab(env, j_tab);
308 if (!tab_android)
309 return false;
310 content::WebContents* web_contents = tab_android->web_contents();
311 if (!web_contents)
312 return false;
314 WindowOpenDisposition disposition =
315 static_cast<WindowOpenDisposition>(j_disposition);
316 SessionRestore::RestoreForeignSessionTab(web_contents,
317 *session_tab,
318 disposition);
320 return true;
323 void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
324 jstring session_tag) {
325 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
326 if (open_tabs)
327 open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
330 // static
331 bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
332 return RegisterNativesImpl(env);