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/bookmarks/bookmarks_bridge.h"
6
7 #include "base/android/jni_string.h"
8 #include "base/prefs/pref_service.h"
9 #include "chrome/browser/bookmarks/bookmark_model.h"
10 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
11 #include "chrome/browser/profiles/incognito_helpers.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_android.h"
14 #include "chrome/browser/profiles/profile_manager.h"
15 #include "chrome/browser/signin/signin_manager.h"
16 #include "chrome/browser/signin/signin_manager_factory.h"
17 #include "chrome/common/pref_names.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "jni/BookmarksBridge_jni.h"
20
21 using base::android::AttachCurrentThread;
22 using base::android::ConvertUTF8ToJavaString;
23 using base::android::ConvertUTF16ToJavaString;
24 using base::android::ScopedJavaLocalRef;
25 using base::android::ScopedJavaGlobalRef;
26 using content::BrowserThread;
27
28 // Should mirror constants in BookmarkBridge.java
29 static const int kBookmarkTypeNormal = 0;
30 static const int kBookmarkTypeManaged = 1;
31 static const int kBookmarkTypePartner = 2;
32
BookmarksBridge(JNIEnv * env,jobject obj,jobject j_profile)33 BookmarksBridge::BookmarksBridge(JNIEnv* env,
34 jobject obj,
35 jobject j_profile)
36 : weak_java_ref_(env, obj),
37 bookmark_model_(NULL),
38 partner_bookmarks_shim_(NULL) {
39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40 profile_ = ProfileAndroid::FromProfileAndroid(j_profile);
41 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
42
43 // Registers the notifications we are interested.
44 bookmark_model_->AddObserver(this);
45
46 // Create the partner Bookmarks shim as early as possible (but don't attach).
47 partner_bookmarks_shim_ = PartnerBookmarksShim::BuildForBrowserContext(
48 chrome::GetBrowserContextRedirectedInIncognito(profile_));
49 partner_bookmarks_shim_->AddObserver(this);
50
51 managed_bookmarks_shim_.reset(new ManagedBookmarksShim(profile_->GetPrefs()));
52 managed_bookmarks_shim_->AddObserver(this);
53
54 NotifyIfDoneLoading();
55
56 // Since a sync or import could have started before this class is
57 // initialized, we need to make sure that our initial state is
58 // up to date.
59 if (bookmark_model_->IsDoingExtensiveChanges())
60 ExtensiveBookmarkChangesBeginning(bookmark_model_);
61 }
62
~BookmarksBridge()63 BookmarksBridge::~BookmarksBridge() {
64 bookmark_model_->RemoveObserver(this);
65 if (partner_bookmarks_shim_)
66 partner_bookmarks_shim_->RemoveObserver(this);
67 if (managed_bookmarks_shim_)
68 managed_bookmarks_shim_->RemoveObserver(this);
69 }
70
Destroy(JNIEnv *,jobject)71 void BookmarksBridge::Destroy(JNIEnv*, jobject) {
72 delete this;
73 }
74
75 // static
RegisterBookmarksBridge(JNIEnv * env)76 bool BookmarksBridge::RegisterBookmarksBridge(JNIEnv* env) {
77 return RegisterNativesImpl(env);
78 }
79
Init(JNIEnv * env,jobject obj,jobject j_profile)80 static jlong Init(JNIEnv* env, jobject obj, jobject j_profile) {
81 BookmarksBridge* delegate = new BookmarksBridge(env, obj, j_profile);
82 return reinterpret_cast<intptr_t>(delegate);
83 }
84
IsEditBookmarksEnabled()85 static bool IsEditBookmarksEnabled() {
86 return ProfileManager::GetLastUsedProfile()->GetPrefs()->GetBoolean(
87 prefs::kEditBookmarksEnabled);
88 }
89
IsEditBookmarksEnabled(JNIEnv * env,jclass clazz)90 static jboolean IsEditBookmarksEnabled(JNIEnv* env, jclass clazz) {
91 return IsEditBookmarksEnabled();
92 }
93
GetBookmarksForFolder(JNIEnv * env,jobject obj,jobject j_folder_id_obj,jobject j_callback_obj,jobject j_result_obj)94 void BookmarksBridge::GetBookmarksForFolder(JNIEnv* env,
95 jobject obj,
96 jobject j_folder_id_obj,
97 jobject j_callback_obj,
98 jobject j_result_obj) {
99 DCHECK(IsLoaded());
100 long folder_id = Java_BookmarkId_getId(env, j_folder_id_obj);
101 int type = Java_BookmarkId_getType(env, j_folder_id_obj);
102 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
103
104 if (!folder->is_folder() || !IsReachable(folder))
105 return;
106
107 // Recreate the java bookmarkId object due to fallback.
108 ScopedJavaLocalRef<jobject> folder_id_obj =
109 Java_BookmarksBridge_createBookmarkId(
110 env, folder->id(), GetBookmarkType(folder));
111 j_folder_id_obj = folder_id_obj.obj();
112
113 // If this is the Mobile bookmarks folder then add the "Managed bookmarks"
114 // folder first, so that it's the first entry.
115 if (folder == bookmark_model_->mobile_node() &&
116 managed_bookmarks_shim_->HasManagedBookmarks()) {
117 ExtractBookmarkNodeInformation(
118 managed_bookmarks_shim_->GetManagedBookmarksRoot(),
119 j_result_obj);
120 }
121 // Get the folder contents
122 for (int i = 0; i < folder->child_count(); ++i) {
123 const BookmarkNode* node = folder->GetChild(i);
124 if (!IsFolderAvailable(node))
125 continue;
126 ExtractBookmarkNodeInformation(node, j_result_obj);
127 }
128
129 if (folder == bookmark_model_->mobile_node() &&
130 partner_bookmarks_shim_->HasPartnerBookmarks()) {
131 ExtractBookmarkNodeInformation(
132 partner_bookmarks_shim_->GetPartnerBookmarksRoot(),
133 j_result_obj);
134 }
135
136 Java_BookmarksCallback_onBookmarksAvailable(
137 env, j_callback_obj, j_folder_id_obj, j_result_obj);
138 }
139
GetCurrentFolderHierarchy(JNIEnv * env,jobject obj,jobject j_folder_id_obj,jobject j_callback_obj,jobject j_result_obj)140 void BookmarksBridge::GetCurrentFolderHierarchy(JNIEnv* env,
141 jobject obj,
142 jobject j_folder_id_obj,
143 jobject j_callback_obj,
144 jobject j_result_obj) {
145 DCHECK(IsLoaded());
146 long folder_id = Java_BookmarkId_getId(env, j_folder_id_obj);
147 int type = Java_BookmarkId_getType(env, j_folder_id_obj);
148 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
149
150 if (!folder->is_folder() || !IsReachable(folder))
151 return;
152
153 // Recreate the java bookmarkId object due to fallback.
154 ScopedJavaLocalRef<jobject> folder_id_obj =
155 Java_BookmarksBridge_createBookmarkId(
156 env, folder->id(), GetBookmarkType(folder));
157 j_folder_id_obj = folder_id_obj.obj();
158
159 // Get the folder heirarchy
160 const BookmarkNode* node = folder;
161 while (node) {
162 ExtractBookmarkNodeInformation(node, j_result_obj);
163 node = GetParentNode(node);
164 }
165
166 Java_BookmarksCallback_onBookmarksFolderHierarchyAvailable(
167 env, j_callback_obj, j_folder_id_obj, j_result_obj);
168 }
169
DeleteBookmark(JNIEnv * env,jobject obj,jobject j_bookmark_id_obj)170 void BookmarksBridge::DeleteBookmark(JNIEnv* env,
171 jobject obj,
172 jobject j_bookmark_id_obj) {
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
174 DCHECK(IsLoaded());
175
176 long bookmark_id = Java_BookmarkId_getId(env, j_bookmark_id_obj);
177 int type = Java_BookmarkId_getType(env, j_bookmark_id_obj);
178 const BookmarkNode* node = GetNodeByID(bookmark_id, type);
179 if (!IsEditable(node)) {
180 NOTREACHED();
181 return;
182 }
183
184 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) {
185 partner_bookmarks_shim_->RemoveBookmark(node);
186 } else {
187 const BookmarkNode* parent_node = GetParentNode(node);
188 bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
189 }
190 }
191
CreateJavaBookmark(const BookmarkNode * node)192 ScopedJavaLocalRef<jobject> BookmarksBridge::CreateJavaBookmark(
193 const BookmarkNode* node) {
194 JNIEnv* env = AttachCurrentThread();
195
196 const BookmarkNode* parent = GetParentNode(node);
197 int64 parent_id = parent ? parent->id() : -1;
198
199 std::string url;
200 if (node->is_url())
201 url = node->url().spec();
202
203 return Java_BookmarksBridge_createBookmarkItem(
204 env,
205 node->id(),
206 GetBookmarkType(node),
207 ConvertUTF16ToJavaString(env, GetTitle(node)).obj(),
208 ConvertUTF8ToJavaString(env, url).obj(),
209 node->is_folder(),
210 parent_id,
211 GetBookmarkType(parent),
212 IsEditable(node));
213 }
214
ExtractBookmarkNodeInformation(const BookmarkNode * node,jobject j_result_obj)215 void BookmarksBridge::ExtractBookmarkNodeInformation(
216 const BookmarkNode* node,
217 jobject j_result_obj) {
218 JNIEnv* env = AttachCurrentThread();
219 if (!IsReachable(node))
220 return;
221 Java_BookmarksBridge_addToList(
222 env,
223 j_result_obj,
224 CreateJavaBookmark(node).obj());
225 }
226
GetNodeByID(long node_id,int type)227 const BookmarkNode* BookmarksBridge::GetNodeByID(long node_id,
228 int type) {
229 const BookmarkNode* node;
230 if (type == kBookmarkTypeManaged) {
231 node = managed_bookmarks_shim_->GetNodeByID(
232 static_cast<int64>(node_id));
233 } else if (type == kBookmarkTypePartner) {
234 node = partner_bookmarks_shim_->GetNodeByID(
235 static_cast<int64>(node_id));
236 } else {
237 node = bookmark_model_->GetNodeByID(static_cast<int64>(node_id));
238 }
239 return node;
240 }
241
GetFolderWithFallback(long folder_id,int type)242 const BookmarkNode* BookmarksBridge::GetFolderWithFallback(
243 long folder_id, int type) {
244 const BookmarkNode* folder = GetNodeByID(folder_id, type);
245 if (!folder || folder->type() == BookmarkNode::URL ||
246 !IsFolderAvailable(folder)) {
247 folder = bookmark_model_->mobile_node();
248 }
249 return folder;
250 }
251
IsEditable(const BookmarkNode * node) const252 bool BookmarksBridge::IsEditable(const BookmarkNode* node) const {
253 if (!node || (node->type() != BookmarkNode::FOLDER &&
254 node->type() != BookmarkNode::URL)) {
255 return false;
256 }
257 if (!IsEditBookmarksEnabled())
258 return false;
259 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
260 return true;
261 return !managed_bookmarks_shim_->IsManagedBookmark(node);
262 }
263
GetParentNode(const BookmarkNode * node)264 const BookmarkNode* BookmarksBridge::GetParentNode(const BookmarkNode* node) {
265 DCHECK(IsLoaded());
266 if (node == managed_bookmarks_shim_->GetManagedBookmarksRoot() ||
267 node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) {
268 return bookmark_model_->mobile_node();
269 } else {
270 return node->parent();
271 }
272 }
273
GetBookmarkType(const BookmarkNode * node)274 int BookmarksBridge::GetBookmarkType(const BookmarkNode* node) {
275 if (managed_bookmarks_shim_->IsManagedBookmark(node))
276 return kBookmarkTypeManaged;
277 else if (partner_bookmarks_shim_->IsPartnerBookmark(node))
278 return kBookmarkTypePartner;
279 else
280 return kBookmarkTypeNormal;
281 }
282
GetTitle(const BookmarkNode * node) const283 base::string16 BookmarksBridge::GetTitle(const BookmarkNode* node) const {
284 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
285 return partner_bookmarks_shim_->GetTitle(node);
286 return node->GetTitle();
287 }
288
IsReachable(const BookmarkNode * node) const289 bool BookmarksBridge::IsReachable(const BookmarkNode* node) const {
290 if (!partner_bookmarks_shim_->IsPartnerBookmark(node))
291 return true;
292 return partner_bookmarks_shim_->IsReachable(node);
293 }
294
IsLoaded() const295 bool BookmarksBridge::IsLoaded() const {
296 return (bookmark_model_->loaded() && partner_bookmarks_shim_->IsLoaded());
297 }
298
IsFolderAvailable(const BookmarkNode * folder) const299 bool BookmarksBridge::IsFolderAvailable(
300 const BookmarkNode* folder) const {
301 SigninManager* signin = SigninManagerFactory::GetForProfile(
302 profile_->GetOriginalProfile());
303 return (folder->type() != BookmarkNode::BOOKMARK_BAR &&
304 folder->type() != BookmarkNode::OTHER_NODE) ||
305 (signin && !signin->GetAuthenticatedUsername().empty());
306 }
307
NotifyIfDoneLoading()308 void BookmarksBridge::NotifyIfDoneLoading() {
309 if (!IsLoaded())
310 return;
311 JNIEnv* env = AttachCurrentThread();
312 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
313 if (obj.is_null())
314 return;
315 Java_BookmarksBridge_bookmarkModelLoaded(env, obj.obj());
316 }
317
318 // ------------- Observer-related methods ------------- //
319
BookmarkModelChanged()320 void BookmarksBridge::BookmarkModelChanged() {
321 if (!IsLoaded())
322 return;
323
324 // Called when there are changes to the bookmark model. It is most
325 // likely changes to either managed or partner bookmarks.
326 JNIEnv* env = AttachCurrentThread();
327 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
328 if (obj.is_null())
329 return;
330 Java_BookmarksBridge_bookmarkModelChanged(env, obj.obj());
331 }
332
Loaded(BookmarkModel * model,bool ids_reassigned)333 void BookmarksBridge::Loaded(BookmarkModel* model, bool ids_reassigned) {
334 NotifyIfDoneLoading();
335 }
336
BookmarkModelBeingDeleted(BookmarkModel * model)337 void BookmarksBridge::BookmarkModelBeingDeleted(BookmarkModel* model) {
338 if (!IsLoaded())
339 return;
340
341 JNIEnv* env = AttachCurrentThread();
342 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
343 if (obj.is_null())
344 return;
345 Java_BookmarksBridge_bookmarkModelDeleted(env, obj.obj());
346 }
347
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)348 void BookmarksBridge::BookmarkNodeMoved(BookmarkModel* model,
349 const BookmarkNode* old_parent,
350 int old_index,
351 const BookmarkNode* new_parent,
352 int new_index) {
353 if (!IsLoaded())
354 return;
355
356 JNIEnv* env = AttachCurrentThread();
357 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
358 if (obj.is_null())
359 return;
360 Java_BookmarksBridge_bookmarkNodeMoved(
361 env,
362 obj.obj(),
363 CreateJavaBookmark(old_parent).obj(),
364 old_index,
365 CreateJavaBookmark(new_parent).obj(),
366 new_index);
367 }
368
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)369 void BookmarksBridge::BookmarkNodeAdded(BookmarkModel* model,
370 const BookmarkNode* parent,
371 int index) {
372 if (!IsLoaded())
373 return;
374
375 JNIEnv* env = AttachCurrentThread();
376 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
377 if (obj.is_null())
378 return;
379 Java_BookmarksBridge_bookmarkNodeAdded(
380 env,
381 obj.obj(),
382 CreateJavaBookmark(parent).obj(),
383 index);
384 }
385
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int old_index,const BookmarkNode * node)386 void BookmarksBridge::BookmarkNodeRemoved(BookmarkModel* model,
387 const BookmarkNode* parent,
388 int old_index,
389 const BookmarkNode* node) {
390 if (!IsLoaded())
391 return;
392
393 JNIEnv* env = AttachCurrentThread();
394 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
395 if (obj.is_null())
396 return;
397 Java_BookmarksBridge_bookmarkNodeRemoved(
398 env,
399 obj.obj(),
400 CreateJavaBookmark(parent).obj(),
401 old_index,
402 CreateJavaBookmark(node).obj());
403 }
404
BookmarkNodeChanged(BookmarkModel * model,const BookmarkNode * node)405 void BookmarksBridge::BookmarkNodeChanged(BookmarkModel* model,
406 const BookmarkNode* node) {
407 if (!IsLoaded())
408 return;
409
410 JNIEnv* env = AttachCurrentThread();
411 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
412 if (obj.is_null())
413 return;
414 Java_BookmarksBridge_bookmarkNodeChanged(
415 env,
416 obj.obj(),
417 CreateJavaBookmark(node).obj());
418 }
419
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)420 void BookmarksBridge::BookmarkNodeChildrenReordered(BookmarkModel* model,
421 const BookmarkNode* node) {
422 if (!IsLoaded())
423 return;
424
425 JNIEnv* env = AttachCurrentThread();
426 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
427 if (obj.is_null())
428 return;
429 Java_BookmarksBridge_bookmarkNodeChildrenReordered(
430 env,
431 obj.obj(),
432 CreateJavaBookmark(node).obj());
433 }
434
ExtensiveBookmarkChangesBeginning(BookmarkModel * model)435 void BookmarksBridge::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
436 if (!IsLoaded())
437 return;
438
439 JNIEnv* env = AttachCurrentThread();
440 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
441 if (obj.is_null())
442 return;
443 Java_BookmarksBridge_extensiveBookmarkChangesBeginning(env, obj.obj());
444 }
445
ExtensiveBookmarkChangesEnded(BookmarkModel * model)446 void BookmarksBridge::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
447 if (!IsLoaded())
448 return;
449
450 JNIEnv* env = AttachCurrentThread();
451 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
452 if (obj.is_null())
453 return;
454 Java_BookmarksBridge_extensiveBookmarkChangesEnded(env, obj.obj());
455 }
456
OnManagedBookmarksChanged()457 void BookmarksBridge::OnManagedBookmarksChanged() {
458 if (!IsLoaded())
459 return;
460
461 BookmarkModelChanged();
462 }
463
PartnerShimChanged(PartnerBookmarksShim * shim)464 void BookmarksBridge::PartnerShimChanged(PartnerBookmarksShim* shim) {
465 if (!IsLoaded())
466 return;
467
468 BookmarkModelChanged();
469 }
470
PartnerShimLoaded(PartnerBookmarksShim * shim)471 void BookmarksBridge::PartnerShimLoaded(PartnerBookmarksShim* shim) {
472 NotifyIfDoneLoading();
473 }
474
ShimBeingDeleted(PartnerBookmarksShim * shim)475 void BookmarksBridge::ShimBeingDeleted(PartnerBookmarksShim* shim) {
476 partner_bookmarks_shim_ = NULL;
477 }
478