• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/android/foreign_session_helper.h"
6 
7 #include <jni.h>
8 
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/open_tabs_ui_delegate.h"
17 #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 
27 using base::android::ScopedJavaGlobalRef;
28 using base::android::ScopedJavaLocalRef;
29 using base::android::AttachCurrentThread;
30 using base::android::ConvertUTF16ToJavaString;
31 using base::android::ConvertUTF8ToJavaString;
32 using base::android::ConvertJavaStringToUTF8;
33 using browser_sync::OpenTabsUIDelegate;
34 using browser_sync::SyncedSession;
35 
36 namespace {
37 
GetOpenTabsUIDelegate(Profile * profile)38 OpenTabsUIDelegate* GetOpenTabsUIDelegate(Profile* profile) {
39   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
40       GetForProfile(profile);
41 
42   // Only return the delegate if it exists and it is done syncing sessions.
43   if (!service || !service->ShouldPushChanges())
44     return NULL;
45 
46   return service->GetOpenTabsUIDelegate();
47 }
48 
ShouldSkipTab(const SessionTab & session_tab)49 bool ShouldSkipTab(const SessionTab& session_tab) {
50     if (session_tab.navigations.empty())
51       return true;
52 
53     int selected_index = session_tab.current_navigation_index;
54     if (selected_index < 0 ||
55         selected_index >= static_cast<int>(session_tab.navigations.size()))
56       return true;
57 
58     const ::sessions::SerializedNavigationEntry& current_navigation =
59         session_tab.navigations.at(selected_index);
60 
61     if (current_navigation.virtual_url().is_empty())
62       return true;
63 
64     return false;
65 }
66 
ShouldSkipWindow(const SessionWindow & window)67 bool ShouldSkipWindow(const SessionWindow& window) {
68   for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
69       tab_it != window.tabs.end(); ++tab_it) {
70     const SessionTab &session_tab = **tab_it;
71     if (!ShouldSkipTab(session_tab))
72       return false;
73   }
74   return true;
75 }
76 
ShouldSkipSession(const browser_sync::SyncedSession & session)77 bool 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 
CopyTabsToJava(JNIEnv * env,const SessionWindow & window,ScopedJavaLocalRef<jobject> & j_window)87 void CopyTabsToJava(
88     JNIEnv* env,
89     const SessionWindow& window,
90     ScopedJavaLocalRef<jobject>& j_window) {
91   for (std::vector<SessionTab*>::const_iterator tab_it = window.tabs.begin();
92       tab_it != window.tabs.end(); ++tab_it) {
93     const SessionTab &session_tab = **tab_it;
94 
95     if (ShouldSkipTab(session_tab))
96       continue;
97 
98     int selected_index = session_tab.current_navigation_index;
99     DCHECK(selected_index >= 0);
100     DCHECK(selected_index < static_cast<int>(session_tab.navigations.size()));
101 
102     const ::sessions::SerializedNavigationEntry& current_navigation =
103         session_tab.navigations.at(selected_index);
104 
105     GURL tab_url = current_navigation.virtual_url();
106 
107     Java_ForeignSessionHelper_pushTab(
108         env, j_window.obj(),
109         ConvertUTF8ToJavaString(env, tab_url.spec()).obj(),
110         ConvertUTF16ToJavaString(env, current_navigation.title()).obj(),
111         session_tab.timestamp.ToJavaTime(),
112         session_tab.tab_id.id());
113   }
114 }
115 
CopyWindowsToJava(JNIEnv * env,const SyncedSession & session,ScopedJavaLocalRef<jobject> & j_session)116 void CopyWindowsToJava(
117     JNIEnv* env,
118     const SyncedSession& session,
119     ScopedJavaLocalRef<jobject>& j_session) {
120   for (SyncedSession::SyncedWindowMap::const_iterator it =
121       session.windows.begin(); it != session.windows.end(); ++it) {
122     const SessionWindow &window = *(it->second);
123 
124     if (ShouldSkipWindow(window))
125       continue;
126 
127     ScopedJavaLocalRef<jobject> last_pushed_window;
128     last_pushed_window.Reset(
129         Java_ForeignSessionHelper_pushWindow(
130             env, j_session.obj(),
131             window.timestamp.ToJavaTime(),
132             window.window_id.id()));
133 
134     CopyTabsToJava(env, window, last_pushed_window);
135   }
136 }
137 
138 }  // namespace
139 
Init(JNIEnv * env,jclass clazz,jobject profile)140 static jlong Init(JNIEnv* env, jclass clazz, jobject profile) {
141   ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
142       ProfileAndroid::FromProfileAndroid(profile));
143   return reinterpret_cast<intptr_t>(foreign_session_helper);
144 }
145 
ForeignSessionHelper(Profile * profile)146 ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
147     : profile_(profile) {
148   ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
149       GetForProfile(profile);
150   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 
~ForeignSessionHelper()158 ForeignSessionHelper::~ForeignSessionHelper() {
159 }
160 
Destroy(JNIEnv * env,jobject obj)161 void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
162   delete this;
163 }
164 
IsTabSyncEnabled(JNIEnv * env,jobject obj)165 jboolean 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 
SetOnForeignSessionCallback(JNIEnv * env,jobject obj,jobject callback)171 void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
172                                                        jobject obj,
173                                                        jobject callback) {
174   callback_.Reset(env, callback);
175 }
176 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)177 void 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 
GetForeignSessions(JNIEnv * env,jobject obj,jobject result)200 jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
201                                                   jobject obj,
202                                                   jobject result) {
203   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
204   if (!open_tabs)
205     return false;
206 
207   std::vector<const browser_sync::SyncedSession*> sessions;
208   if (!open_tabs->GetAllForeignSessions(&sessions))
209     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);
216   base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
217   scoped_ptr<base::DictionaryValue> collapsed_sessions(
218       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) {
226     const browser_sync::SyncedSession &session = *(sessions[i]);
227     if (ShouldSkipSession(session))
228       continue;
229 
230     const bool is_collapsed = collapsed_sessions->HasKey(session.session_tag);
231 
232     if (is_collapsed)
233       pref_collapsed_sessions->SetBoolean(session.session_tag, true);
234 
235     last_pushed_session.Reset(
236         Java_ForeignSessionHelper_pushSession(
237             env,
238             result,
239             ConvertUTF8ToJavaString(env, session.session_tag).obj(),
240             ConvertUTF8ToJavaString(env, session.session_name).obj(),
241             session.device_type,
242             session.modified_time.ToJavaTime()));
243 
244     CopyWindowsToJava(env, session, last_pushed_session);
245   }
246 
247   return true;
248 }
249 
OpenForeignSessionTab(JNIEnv * env,jobject obj,jobject j_tab,jstring session_tag,jint session_tab_id,jint j_disposition)250 jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
251                                                      jobject obj,
252                                                      jobject j_tab,
253                                                      jstring session_tag,
254                                                      jint session_tab_id,
255                                                      jint j_disposition) {
256   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
257   if (!open_tabs) {
258     LOG(ERROR) << "Null OpenTabsUIDelegate returned.";
259     return false;
260   }
261 
262   const SessionTab* session_tab;
263 
264   if (!open_tabs->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
265                                 session_tab_id,
266                                 &session_tab)) {
267     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 
DeleteForeignSession(JNIEnv * env,jobject obj,jstring session_tag)292 void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
293                                                 jstring session_tag) {
294   OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(profile_);
295   if (open_tabs)
296     open_tabs->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
297 }
298 
299 // static
RegisterForeignSessionHelper(JNIEnv * env)300 bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
301   return RegisterNativesImpl(env);
302 }
303