Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / android / bookmarks / bookmarks_bridge.cc
blobdb7145acd88cb64712eb3b8a2f95cbbfc5a97920
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/bookmarks/bookmarks_bridge.h"
7 #include "base/android/jni_array.h"
8 #include "base/android/jni_string.h"
9 #include "base/containers/stack_container.h"
10 #include "base/i18n/string_compare.h"
11 #include "base/prefs/pref_service.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
14 #include "chrome/browser/profiles/incognito_helpers.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_android.h"
17 #include "chrome/browser/signin/signin_manager_factory.h"
18 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/bookmarks/browser/bookmark_match.h"
21 #include "components/bookmarks/browser/bookmark_model.h"
22 #include "components/bookmarks/browser/bookmark_utils.h"
23 #include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
24 #include "components/bookmarks/common/android/bookmark_type.h"
25 #include "components/bookmarks/managed/managed_bookmark_service.h"
26 #include "components/enhanced_bookmarks/enhanced_bookmark_features.h"
27 #include "components/query_parser/query_parser.h"
28 #include "components/signin/core/browser/signin_manager.h"
29 #include "components/undo/bookmark_undo_service.h"
30 #include "components/undo/undo_manager.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "grit/components_strings.h"
33 #include "jni/BookmarksBridge_jni.h"
34 #include "ui/base/l10n/l10n_util.h"
36 using base::android::AttachCurrentThread;
37 using base::android::ConvertUTF8ToJavaString;
38 using base::android::ConvertUTF16ToJavaString;
39 using base::android::ScopedJavaLocalRef;
40 using base::android::ScopedJavaGlobalRef;
41 using base::android::ToJavaIntArray;
42 using bookmarks::android::JavaBookmarkIdCreateBookmarkId;
43 using bookmarks::android::JavaBookmarkIdGetId;
44 using bookmarks::android::JavaBookmarkIdGetType;
45 using bookmarks::BookmarkModel;
46 using bookmarks::BookmarkNode;
47 using bookmarks::BookmarkPermanentNode;
48 using bookmarks::BookmarkType;
49 using content::BrowserThread;
51 namespace {
53 class BookmarkNodeCreationTimeCompareFunctor {
54 public:
55 bool operator()(const BookmarkNode* lhs, const BookmarkNode* rhs) {
56 return lhs->date_added().ToJavaTime() > rhs->date_added().ToJavaTime();
60 class BookmarkTitleComparer {
61 public:
62 explicit BookmarkTitleComparer(BookmarksBridge* bookmarks_bridge,
63 const icu::Collator* collator)
64 : bookmarks_bridge_(bookmarks_bridge),
65 collator_(collator) {}
67 bool operator()(const BookmarkNode* lhs, const BookmarkNode* rhs) {
68 if (collator_) {
69 return base::i18n::CompareString16WithCollator(
70 *collator_, bookmarks_bridge_->GetTitle(lhs),
71 bookmarks_bridge_->GetTitle(rhs)) == UCOL_LESS;
72 } else {
73 return lhs->GetTitle() < rhs->GetTitle();
77 private:
78 BookmarksBridge* bookmarks_bridge_; // weak
79 const icu::Collator* collator_;
82 scoped_ptr<icu::Collator> GetICUCollator() {
83 UErrorCode error = U_ZERO_ERROR;
84 scoped_ptr<icu::Collator> collator_;
85 collator_.reset(icu::Collator::createInstance(error));
86 if (U_FAILURE(error))
87 collator_.reset(NULL);
89 return collator_.Pass();
92 } // namespace
94 BookmarksBridge::BookmarksBridge(JNIEnv* env, jobject obj, jobject j_profile)
95 : weak_java_ref_(env, obj),
96 bookmark_model_(NULL),
97 managed_bookmark_service_(NULL),
98 partner_bookmarks_shim_(NULL) {
99 DCHECK_CURRENTLY_ON(BrowserThread::UI);
100 profile_ = ProfileAndroid::FromProfileAndroid(j_profile);
101 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
102 managed_bookmark_service_ =
103 ManagedBookmarkServiceFactory::GetForProfile(profile_);
105 // Registers the notifications we are interested.
106 bookmark_model_->AddObserver(this);
108 // Create the partner Bookmarks shim as early as possible (but don't attach).
109 partner_bookmarks_shim_ = PartnerBookmarksShim::BuildForBrowserContext(
110 chrome::GetBrowserContextRedirectedInIncognito(profile_));
111 partner_bookmarks_shim_->AddObserver(this);
113 NotifyIfDoneLoading();
115 // Since a sync or import could have started before this class is
116 // initialized, we need to make sure that our initial state is
117 // up to date.
118 if (bookmark_model_->IsDoingExtensiveChanges())
119 ExtensiveBookmarkChangesBeginning(bookmark_model_);
122 BookmarksBridge::~BookmarksBridge() {
123 bookmark_model_->RemoveObserver(this);
124 if (partner_bookmarks_shim_)
125 partner_bookmarks_shim_->RemoveObserver(this);
128 void BookmarksBridge::Destroy(JNIEnv*, jobject) {
129 delete this;
132 // static
133 bool BookmarksBridge::RegisterBookmarksBridge(JNIEnv* env) {
134 return RegisterNativesImpl(env);
137 static jlong Init(JNIEnv* env,
138 const JavaParamRef<jobject>& obj,
139 const JavaParamRef<jobject>& j_profile) {
140 BookmarksBridge* delegate = new BookmarksBridge(env, obj, j_profile);
141 return reinterpret_cast<intptr_t>(delegate);
144 static jboolean IsEnhancedBookmarksFeatureEnabled(
145 JNIEnv* env,
146 const JavaParamRef<jclass>& clazz) {
147 return enhanced_bookmarks::IsEnhancedBookmarksEnabled();
150 jboolean BookmarksBridge::IsEditBookmarksEnabled(JNIEnv* env, jobject obj) {
151 return IsEditBookmarksEnabled();
154 void BookmarksBridge::LoadEmptyPartnerBookmarkShimForTesting(JNIEnv* env,
155 jobject obj) {
156 if (partner_bookmarks_shim_->IsLoaded())
157 return;
158 partner_bookmarks_shim_->SetPartnerBookmarksRoot(
159 new BookmarkPermanentNode(0));
160 DCHECK(partner_bookmarks_shim_->IsLoaded());
163 ScopedJavaLocalRef<jobject> BookmarksBridge::GetBookmarkByID(JNIEnv* env,
164 jobject obj,
165 jlong id,
166 jint type) {
167 DCHECK(IsLoaded());
168 const BookmarkNode* node = GetNodeByID(id, type);
169 return node ? CreateJavaBookmark(node) : ScopedJavaLocalRef<jobject>();
172 bool BookmarksBridge::IsDoingExtensiveChanges(JNIEnv* env, jobject obj) {
173 return bookmark_model_->IsDoingExtensiveChanges();
176 void BookmarksBridge::GetPermanentNodeIDs(JNIEnv* env,
177 jobject obj,
178 jobject j_result_obj) {
179 // TODO(kkimlabs): Remove this function.
180 DCHECK(IsLoaded());
182 base::StackVector<const BookmarkNode*, 8> permanent_nodes;
184 // Save all the permanent nodes.
185 const BookmarkNode* root_node = bookmark_model_->root_node();
186 permanent_nodes->push_back(root_node);
187 for (int i = 0; i < root_node->child_count(); ++i) {
188 permanent_nodes->push_back(root_node->GetChild(i));
190 permanent_nodes->push_back(
191 partner_bookmarks_shim_->GetPartnerBookmarksRoot());
193 // Write the permanent nodes to |j_result_obj|.
194 for (base::StackVector<const BookmarkNode*, 8>::ContainerType::const_iterator
195 it = permanent_nodes->begin();
196 it != permanent_nodes->end();
197 ++it) {
198 if (*it != NULL) {
199 Java_BookmarksBridge_addToBookmarkIdList(
200 env, j_result_obj, (*it)->id(), GetBookmarkType(*it));
205 void BookmarksBridge::GetTopLevelFolderParentIDs(JNIEnv* env,
206 jobject obj,
207 jobject j_result_obj) {
208 Java_BookmarksBridge_addToBookmarkIdList(
209 env, j_result_obj, bookmark_model_->root_node()->id(),
210 GetBookmarkType(bookmark_model_->root_node()));
213 void BookmarksBridge::GetTopLevelFolderIDs(JNIEnv* env,
214 jobject obj,
215 jboolean get_special,
216 jboolean get_normal,
217 jobject j_result_obj) {
218 DCHECK(IsLoaded());
219 std::vector<const BookmarkNode*> top_level_folders;
221 if (get_special) {
222 if (managed_bookmark_service_->managed_node() &&
223 managed_bookmark_service_->managed_node()->child_count() > 0) {
224 top_level_folders.push_back(managed_bookmark_service_->managed_node());
226 if (managed_bookmark_service_->supervised_node() &&
227 managed_bookmark_service_->supervised_node()->child_count() > 0) {
228 top_level_folders.push_back(managed_bookmark_service_->supervised_node());
230 if (partner_bookmarks_shim_->HasPartnerBookmarks()
231 && IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) {
232 top_level_folders.push_back(
233 partner_bookmarks_shim_->GetPartnerBookmarksRoot());
236 std::size_t special_count = top_level_folders.size();
238 if (get_normal) {
239 DCHECK_EQ(bookmark_model_->root_node()->child_count(), 5);
241 const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
242 for (int i = 0; i < mobile_node->child_count(); ++i) {
243 const BookmarkNode* node = mobile_node->GetChild(i);
244 if (node->is_folder()) {
245 top_level_folders.push_back(node);
249 const BookmarkNode* bookmark_bar_node =
250 bookmark_model_->bookmark_bar_node();
251 for (int i = 0; i < bookmark_bar_node->child_count(); ++i) {
252 const BookmarkNode* node = bookmark_bar_node->GetChild(i);
253 if (node->is_folder()) {
254 top_level_folders.push_back(node);
258 const BookmarkNode* other_node = bookmark_model_->other_node();
259 for (int i = 0; i < other_node->child_count(); ++i) {
260 const BookmarkNode* node = other_node->GetChild(i);
261 if (node->is_folder()) {
262 top_level_folders.push_back(node);
266 scoped_ptr<icu::Collator> collator = GetICUCollator();
267 std::stable_sort(top_level_folders.begin() + special_count,
268 top_level_folders.end(),
269 BookmarkTitleComparer(this, collator.get()));
272 for (std::vector<const BookmarkNode*>::const_iterator it =
273 top_level_folders.begin(); it != top_level_folders.end(); ++it) {
274 Java_BookmarksBridge_addToBookmarkIdList(env,
275 j_result_obj,
276 (*it)->id(),
277 GetBookmarkType(*it));
281 void BookmarksBridge::GetAllFoldersWithDepths(JNIEnv* env,
282 jobject obj,
283 jobject j_folders_obj,
284 jobject j_depths_obj) {
285 DCHECK(IsLoaded());
287 scoped_ptr<icu::Collator> collator = GetICUCollator();
289 // Vector to temporarily contain all child bookmarks at same level for sorting
290 std::vector<const BookmarkNode*> bookmarkList;
292 // Stack for Depth-First Search of bookmark model. It stores nodes and their
293 // heights.
294 std::stack<std::pair<const BookmarkNode*, int> > stk;
296 bookmarkList.push_back(bookmark_model_->mobile_node());
297 bookmarkList.push_back(bookmark_model_->bookmark_bar_node());
298 bookmarkList.push_back(bookmark_model_->other_node());
300 // Push all sorted top folders in stack and give them depth of 0.
301 // Note the order to push folders to stack should be opposite to the order in
302 // output.
303 for (std::vector<const BookmarkNode*>::reverse_iterator it =
304 bookmarkList.rbegin();
305 it != bookmarkList.rend();
306 ++it) {
307 stk.push(std::make_pair(*it, 0));
310 while (!stk.empty()) {
311 const BookmarkNode* node = stk.top().first;
312 int depth = stk.top().second;
313 stk.pop();
314 Java_BookmarksBridge_addToBookmarkIdListWithDepth(env,
315 j_folders_obj,
316 node->id(),
317 GetBookmarkType(node),
318 j_depths_obj,
319 depth);
320 bookmarkList.clear();
321 for (int i = 0; i < node->child_count(); ++i) {
322 const BookmarkNode* child = node->GetChild(i);
323 if (child->is_folder() &&
324 managed_bookmark_service_->CanBeEditedByUser(child)) {
325 bookmarkList.push_back(node->GetChild(i));
328 std::stable_sort(bookmarkList.begin(),
329 bookmarkList.end(),
330 BookmarkTitleComparer(this, collator.get()));
331 for (std::vector<const BookmarkNode*>::reverse_iterator it =
332 bookmarkList.rbegin();
333 it != bookmarkList.rend();
334 ++it) {
335 stk.push(std::make_pair(*it, depth + 1));
340 ScopedJavaLocalRef<jobject> BookmarksBridge::GetRootFolderId(JNIEnv* env,
341 jobject obj) {
342 const BookmarkNode* root_node = bookmark_model_->root_node();
343 ScopedJavaLocalRef<jobject> folder_id_obj =
344 JavaBookmarkIdCreateBookmarkId(
345 env, root_node->id(), GetBookmarkType(root_node));
346 return folder_id_obj;
349 ScopedJavaLocalRef<jobject> BookmarksBridge::GetMobileFolderId(JNIEnv* env,
350 jobject obj) {
351 const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
352 ScopedJavaLocalRef<jobject> folder_id_obj =
353 JavaBookmarkIdCreateBookmarkId(
354 env, mobile_node->id(), GetBookmarkType(mobile_node));
355 return folder_id_obj;
358 ScopedJavaLocalRef<jobject> BookmarksBridge::GetOtherFolderId(JNIEnv* env,
359 jobject obj) {
360 const BookmarkNode* other_node = bookmark_model_->other_node();
361 ScopedJavaLocalRef<jobject> folder_id_obj =
362 JavaBookmarkIdCreateBookmarkId(
363 env, other_node->id(), GetBookmarkType(other_node));
364 return folder_id_obj;
367 ScopedJavaLocalRef<jobject> BookmarksBridge::GetDesktopFolderId(JNIEnv* env,
368 jobject obj) {
369 const BookmarkNode* desktop_node = bookmark_model_->bookmark_bar_node();
370 ScopedJavaLocalRef<jobject> folder_id_obj =
371 JavaBookmarkIdCreateBookmarkId(
372 env, desktop_node->id(), GetBookmarkType(desktop_node));
373 return folder_id_obj;
376 void BookmarksBridge::GetChildIDs(JNIEnv* env,
377 jobject obj,
378 jlong id,
379 jint type,
380 jboolean get_folders,
381 jboolean get_bookmarks,
382 jobject j_result_obj) {
383 DCHECK(IsLoaded());
385 const BookmarkNode* parent = GetNodeByID(id, type);
386 if (!parent->is_folder() || !IsReachable(parent))
387 return;
389 // Get the folder contents
390 for (int i = 0; i < parent->child_count(); ++i) {
391 const BookmarkNode* child = parent->GetChild(i);
392 if (!IsFolderAvailable(child) || !IsReachable(child))
393 continue;
395 if ((child->is_folder() && get_folders) ||
396 (!child->is_folder() && get_bookmarks)) {
397 Java_BookmarksBridge_addToBookmarkIdList(
398 env, j_result_obj, child->id(), GetBookmarkType(child));
402 // Partner bookmark root node is under mobile node.
403 if (parent == bookmark_model_->mobile_node() && get_folders &&
404 partner_bookmarks_shim_->HasPartnerBookmarks() &&
405 IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) {
406 Java_BookmarksBridge_addToBookmarkIdList(
407 env,
408 j_result_obj,
409 partner_bookmarks_shim_->GetPartnerBookmarksRoot()->id(),
410 BookmarkType::BOOKMARK_TYPE_PARTNER);
414 ScopedJavaLocalRef<jobject> BookmarksBridge::GetChildAt(JNIEnv* env,
415 jobject obj,
416 jlong id,
417 jint type,
418 jint index) {
419 DCHECK(IsLoaded());
421 const BookmarkNode* parent = GetNodeByID(id, type);
422 DCHECK(parent);
423 const BookmarkNode* child = parent->GetChild(index);
424 return JavaBookmarkIdCreateBookmarkId(
425 env, child->id(), GetBookmarkType(child));
428 void BookmarksBridge::GetAllBookmarkIDsOrderedByCreationDate(
429 JNIEnv* env,
430 jobject obj,
431 jobject j_result_obj) {
432 DCHECK(IsLoaded());
433 std::list<const BookmarkNode*> folders;
434 std::vector<const BookmarkNode*> result;
435 folders.push_back(bookmark_model_->root_node());
437 for (std::list<const BookmarkNode*>::iterator folder_iter = folders.begin();
438 folder_iter != folders.end(); ++folder_iter) {
439 if (*folder_iter == NULL)
440 continue;
442 std::list<const BookmarkNode*>::iterator insert_iter = folder_iter;
443 ++insert_iter;
445 for (int i = 0; i < (*folder_iter)->child_count(); ++i) {
446 const BookmarkNode* child = (*folder_iter)->GetChild(i);
447 if (!IsReachable(child) ||
448 bookmarks::IsDescendantOf(
449 child, managed_bookmark_service_->managed_node()) ||
450 bookmarks::IsDescendantOf(
451 child, managed_bookmark_service_->supervised_node())) {
452 continue;
455 if (child->is_folder()) {
456 insert_iter = folders.insert(insert_iter, child);
457 } else {
458 result.push_back(child);
463 std::sort(
464 result.begin(), result.end(), BookmarkNodeCreationTimeCompareFunctor());
466 for (std::vector<const BookmarkNode*>::const_iterator iter = result.begin();
467 iter != result.end();
468 ++iter) {
469 const BookmarkNode* bookmark = *iter;
470 Java_BookmarksBridge_addToBookmarkIdList(
471 env, j_result_obj, bookmark->id(), GetBookmarkType(bookmark));
475 void BookmarksBridge::SetBookmarkTitle(JNIEnv* env,
476 jobject obj,
477 jlong id,
478 jint type,
479 jstring j_title) {
480 DCHECK(IsLoaded());
481 const BookmarkNode* bookmark = GetNodeByID(id, type);
482 const base::string16 title =
483 base::android::ConvertJavaStringToUTF16(env, j_title);
485 if (partner_bookmarks_shim_->IsPartnerBookmark(bookmark)) {
486 partner_bookmarks_shim_->RenameBookmark(bookmark, title);
487 } else {
488 bookmark_model_->SetTitle(bookmark, title);
492 void BookmarksBridge::SetBookmarkUrl(JNIEnv* env,
493 jobject obj,
494 jlong id,
495 jint type,
496 jstring url) {
497 DCHECK(IsLoaded());
498 bookmark_model_->SetURL(
499 GetNodeByID(id, type),
500 GURL(base::android::ConvertJavaStringToUTF16(env, url)));
503 bool BookmarksBridge::DoesBookmarkExist(JNIEnv* env,
504 jobject obj,
505 jlong id,
506 jint type) {
507 DCHECK(IsLoaded());
509 const BookmarkNode* node = GetNodeByID(id, type);
511 if (!node)
512 return false;
514 if (type == BookmarkType::BOOKMARK_TYPE_NORMAL) {
515 return true;
516 } else {
517 DCHECK(type == BookmarkType::BOOKMARK_TYPE_PARTNER);
518 return partner_bookmarks_shim_->IsReachable(node);
522 void BookmarksBridge::GetBookmarksForFolder(JNIEnv* env,
523 jobject obj,
524 jobject j_folder_id_obj,
525 jobject j_callback_obj,
526 jobject j_result_obj) {
527 DCHECK(IsLoaded());
528 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj);
529 int type = JavaBookmarkIdGetType(env, j_folder_id_obj);
530 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
532 if (!folder->is_folder() || !IsReachable(folder))
533 return;
535 // Recreate the java bookmarkId object due to fallback.
536 ScopedJavaLocalRef<jobject> folder_id_obj =
537 JavaBookmarkIdCreateBookmarkId(
538 env, folder->id(), GetBookmarkType(folder));
539 j_folder_id_obj = folder_id_obj.obj();
541 // Get the folder contents.
542 for (int i = 0; i < folder->child_count(); ++i) {
543 const BookmarkNode* node = folder->GetChild(i);
544 if (!IsFolderAvailable(node))
545 continue;
546 ExtractBookmarkNodeInformation(node, j_result_obj);
549 if (folder == bookmark_model_->mobile_node() &&
550 partner_bookmarks_shim_->HasPartnerBookmarks()) {
551 ExtractBookmarkNodeInformation(
552 partner_bookmarks_shim_->GetPartnerBookmarksRoot(),
553 j_result_obj);
556 if (j_callback_obj) {
557 Java_BookmarksCallback_onBookmarksAvailable(
558 env, j_callback_obj, j_folder_id_obj, j_result_obj);
562 jboolean BookmarksBridge::IsFolderVisible(JNIEnv* env,
563 jobject obj,
564 jlong id,
565 jint type) {
566 if (type == BookmarkType::BOOKMARK_TYPE_NORMAL) {
567 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
568 bookmark_model_, static_cast<int64>(id));
569 return node->IsVisible();
570 } else if (type == BookmarkType::BOOKMARK_TYPE_PARTNER) {
571 const BookmarkNode* node = partner_bookmarks_shim_->GetNodeByID(
572 static_cast<long>(id));
573 return partner_bookmarks_shim_->IsReachable(node);
576 NOTREACHED();
577 return false;
580 void BookmarksBridge::GetCurrentFolderHierarchy(JNIEnv* env,
581 jobject obj,
582 jobject j_folder_id_obj,
583 jobject j_callback_obj,
584 jobject j_result_obj) {
585 DCHECK(IsLoaded());
586 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj);
587 int type = JavaBookmarkIdGetType(env, j_folder_id_obj);
588 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
590 if (!folder->is_folder() || !IsReachable(folder))
591 return;
593 // Recreate the java bookmarkId object due to fallback.
594 ScopedJavaLocalRef<jobject> folder_id_obj =
595 JavaBookmarkIdCreateBookmarkId(
596 env, folder->id(), GetBookmarkType(folder));
597 j_folder_id_obj = folder_id_obj.obj();
599 // Get the folder hierarchy.
600 const BookmarkNode* node = folder;
601 while (node) {
602 ExtractBookmarkNodeInformation(node, j_result_obj);
603 node = GetParentNode(node);
606 Java_BookmarksCallback_onBookmarksFolderHierarchyAvailable(
607 env, j_callback_obj, j_folder_id_obj, j_result_obj);
610 void BookmarksBridge::SearchBookmarks(JNIEnv* env,
611 jobject obj,
612 jobject j_list,
613 jstring j_query,
614 jint max_results) {
615 DCHECK(bookmark_model_->loaded());
617 std::vector<bookmarks::BookmarkMatch> results;
618 bookmark_model_->GetBookmarksMatching(
619 base::android::ConvertJavaStringToUTF16(env, j_query),
620 max_results,
621 query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH,
622 &results);
623 for (const bookmarks::BookmarkMatch& match : results) {
624 const BookmarkNode* node = match.node;
626 std::vector<int> title_match_start_positions;
627 std::vector<int> title_match_end_positions;
628 for (auto position : match.title_match_positions) {
629 title_match_start_positions.push_back(position.first);
630 title_match_end_positions.push_back(position.second);
633 std::vector<int> url_match_start_positions;
634 std::vector<int> url_match_end_positions;
635 for (auto position : match.url_match_positions) {
636 url_match_start_positions.push_back(position.first);
637 url_match_end_positions.push_back(position.second);
640 Java_BookmarksBridge_addToBookmarkMatchList(
641 env, j_list, node->id(), node->type(),
642 ToJavaIntArray(env, title_match_start_positions).obj(),
643 ToJavaIntArray(env, title_match_end_positions).obj(),
644 ToJavaIntArray(env, url_match_start_positions).obj(),
645 ToJavaIntArray(env, url_match_end_positions).obj());
649 ScopedJavaLocalRef<jobject> BookmarksBridge::AddFolder(JNIEnv* env,
650 jobject obj,
651 jobject j_parent_id_obj,
652 jint index,
653 jstring j_title) {
654 DCHECK(IsLoaded());
655 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
656 int type = JavaBookmarkIdGetType(env, j_parent_id_obj);
657 const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
659 const BookmarkNode* new_node = bookmark_model_->AddFolder(
660 parent, index, base::android::ConvertJavaStringToUTF16(env, j_title));
661 if (!new_node) {
662 NOTREACHED();
663 return ScopedJavaLocalRef<jobject>();
665 ScopedJavaLocalRef<jobject> new_java_obj =
666 JavaBookmarkIdCreateBookmarkId(
667 env, new_node->id(), GetBookmarkType(new_node));
668 return new_java_obj;
671 void BookmarksBridge::DeleteBookmark(JNIEnv* env,
672 jobject obj,
673 jobject j_bookmark_id_obj) {
674 DCHECK_CURRENTLY_ON(BrowserThread::UI);
675 DCHECK(IsLoaded());
677 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
678 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj);
679 const BookmarkNode* node = GetNodeByID(bookmark_id, type);
680 if (!IsEditable(node)) {
681 NOTREACHED();
682 return;
685 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
686 partner_bookmarks_shim_->RemoveBookmark(node);
687 else
688 bookmark_model_->Remove(node);
691 void BookmarksBridge::MoveBookmark(JNIEnv* env,
692 jobject obj,
693 jobject j_bookmark_id_obj,
694 jobject j_parent_id_obj,
695 jint index) {
696 DCHECK_CURRENTLY_ON(BrowserThread::UI);
697 DCHECK(IsLoaded());
699 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
700 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj);
701 const BookmarkNode* node = GetNodeByID(bookmark_id, type);
702 if (!IsEditable(node)) {
703 NOTREACHED();
704 return;
706 bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
707 type = JavaBookmarkIdGetType(env, j_parent_id_obj);
708 const BookmarkNode* new_parent_node = GetNodeByID(bookmark_id, type);
709 bookmark_model_->Move(node, new_parent_node, index);
712 ScopedJavaLocalRef<jobject> BookmarksBridge::AddBookmark(
713 JNIEnv* env,
714 jobject obj,
715 jobject j_parent_id_obj,
716 jint index,
717 jstring j_title,
718 jstring j_url) {
719 DCHECK(IsLoaded());
720 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
721 int type = JavaBookmarkIdGetType(env, j_parent_id_obj);
722 const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
724 const BookmarkNode* new_node = bookmark_model_->AddURL(
725 parent,
726 index,
727 base::android::ConvertJavaStringToUTF16(env, j_title),
728 GURL(base::android::ConvertJavaStringToUTF16(env, j_url)));
729 if (!new_node) {
730 NOTREACHED();
731 return ScopedJavaLocalRef<jobject>();
733 ScopedJavaLocalRef<jobject> new_java_obj =
734 JavaBookmarkIdCreateBookmarkId(
735 env, new_node->id(), GetBookmarkType(new_node));
736 return new_java_obj;
739 void BookmarksBridge::Undo(JNIEnv* env, jobject obj) {
740 DCHECK_CURRENTLY_ON(BrowserThread::UI);
741 DCHECK(IsLoaded());
742 BookmarkUndoService* undo_service =
743 BookmarkUndoServiceFactory::GetForProfile(profile_);
744 UndoManager* undo_manager = undo_service->undo_manager();
745 undo_manager->Undo();
748 void BookmarksBridge::StartGroupingUndos(JNIEnv* env, jobject obj) {
749 DCHECK_CURRENTLY_ON(BrowserThread::UI);
750 DCHECK(IsLoaded());
751 DCHECK(!grouped_bookmark_actions_.get()); // shouldn't have started already
752 grouped_bookmark_actions_.reset(
753 new bookmarks::ScopedGroupBookmarkActions(bookmark_model_));
756 void BookmarksBridge::EndGroupingUndos(JNIEnv* env, jobject obj) {
757 DCHECK_CURRENTLY_ON(BrowserThread::UI);
758 DCHECK(IsLoaded());
759 DCHECK(grouped_bookmark_actions_.get()); // should only call after start
760 grouped_bookmark_actions_.reset();
763 base::string16 BookmarksBridge::GetTitle(const BookmarkNode* node) const {
764 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
765 return partner_bookmarks_shim_->GetTitle(node);
767 if (node == bookmark_model_->bookmark_bar_node()
768 && enhanced_bookmarks::IsEnhancedBookmarksEnabled()) {
769 return l10n_util::GetStringUTF16(IDS_ENHANCED_BOOKMARK_BAR_FOLDER_NAME);
772 return node->GetTitle();
775 ScopedJavaLocalRef<jobject> BookmarksBridge::CreateJavaBookmark(
776 const BookmarkNode* node) {
777 JNIEnv* env = AttachCurrentThread();
779 const BookmarkNode* parent = GetParentNode(node);
780 int64 parent_id = parent ? parent->id() : -1;
782 std::string url;
783 if (node->is_url())
784 url = node->url().spec();
786 return Java_BookmarksBridge_createBookmarkItem(
787 env,
788 node->id(),
789 GetBookmarkType(node),
790 ConvertUTF16ToJavaString(env, GetTitle(node)).obj(),
791 ConvertUTF8ToJavaString(env, url).obj(),
792 node->is_folder(),
793 parent_id,
794 GetBookmarkType(parent),
795 IsEditable(node),
796 IsManaged(node));
799 void BookmarksBridge::ExtractBookmarkNodeInformation(const BookmarkNode* node,
800 jobject j_result_obj) {
801 JNIEnv* env = AttachCurrentThread();
802 if (!IsReachable(node))
803 return;
804 Java_BookmarksBridge_addToList(
805 env, j_result_obj, CreateJavaBookmark(node).obj());
808 const BookmarkNode* BookmarksBridge::GetNodeByID(long node_id, int type) {
809 const BookmarkNode* node;
810 if (type == BookmarkType::BOOKMARK_TYPE_PARTNER) {
811 node = partner_bookmarks_shim_->GetNodeByID(
812 static_cast<int64>(node_id));
813 } else {
814 node = bookmarks::GetBookmarkNodeByID(bookmark_model_,
815 static_cast<int64>(node_id));
817 return node;
820 const BookmarkNode* BookmarksBridge::GetFolderWithFallback(long folder_id,
821 int type) {
822 const BookmarkNode* folder = GetNodeByID(folder_id, type);
823 if (!folder || folder->type() == BookmarkNode::URL ||
824 !IsFolderAvailable(folder)) {
825 if (!managed_bookmark_service_->managed_node()->empty())
826 folder = managed_bookmark_service_->managed_node();
827 else
828 folder = bookmark_model_->mobile_node();
830 return folder;
833 bool BookmarksBridge::IsEditBookmarksEnabled() const {
834 return profile_->GetPrefs()->GetBoolean(
835 bookmarks::prefs::kEditBookmarksEnabled);
838 bool BookmarksBridge::IsEditable(const BookmarkNode* node) const {
839 if (!node || (node->type() != BookmarkNode::FOLDER &&
840 node->type() != BookmarkNode::URL)) {
841 return false;
843 if (!IsEditBookmarksEnabled())
844 return false;
845 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
846 return partner_bookmarks_shim_->IsEditable(node);
847 return managed_bookmark_service_->CanBeEditedByUser(node);
850 bool BookmarksBridge::IsManaged(const BookmarkNode* node) const {
851 return bookmarks::IsDescendantOf(node,
852 managed_bookmark_service_->managed_node());
855 const BookmarkNode* BookmarksBridge::GetParentNode(const BookmarkNode* node) {
856 DCHECK(IsLoaded());
857 if (node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) {
858 return bookmark_model_->mobile_node();
859 } else {
860 return node->parent();
864 int BookmarksBridge::GetBookmarkType(const BookmarkNode* node) {
865 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
866 return BookmarkType::BOOKMARK_TYPE_PARTNER;
867 else
868 return BookmarkType::BOOKMARK_TYPE_NORMAL;
871 bool BookmarksBridge::IsReachable(const BookmarkNode* node) const {
872 if (!partner_bookmarks_shim_->IsPartnerBookmark(node))
873 return true;
874 return partner_bookmarks_shim_->IsReachable(node);
877 bool BookmarksBridge::IsLoaded() const {
878 return (bookmark_model_->loaded() && partner_bookmarks_shim_->IsLoaded());
881 bool BookmarksBridge::IsFolderAvailable(
882 const BookmarkNode* folder) const {
883 // The managed bookmarks folder is not shown if there are no bookmarks
884 // configured via policy.
885 if (folder == managed_bookmark_service_->managed_node() && folder->empty())
886 return false;
887 // Similarly, the supervised bookmarks folder is not shown if there are no
888 // bookmarks configured by the custodian.
889 if (folder == managed_bookmark_service_->supervised_node() && folder->empty())
890 return false;
892 SigninManager* signin = SigninManagerFactory::GetForProfile(
893 profile_->GetOriginalProfile());
894 return (folder->type() != BookmarkNode::BOOKMARK_BAR &&
895 folder->type() != BookmarkNode::OTHER_NODE) ||
896 (signin && signin->IsAuthenticated());
899 void BookmarksBridge::NotifyIfDoneLoading() {
900 if (!IsLoaded())
901 return;
902 JNIEnv* env = AttachCurrentThread();
903 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
904 if (obj.is_null())
905 return;
906 Java_BookmarksBridge_bookmarkModelLoaded(env, obj.obj());
909 // ------------- Observer-related methods ------------- //
911 void BookmarksBridge::BookmarkModelChanged() {
912 if (!IsLoaded())
913 return;
915 // Called when there are changes to the bookmark model. It is most
916 // likely changes to the partner bookmarks.
917 JNIEnv* env = AttachCurrentThread();
918 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
919 if (obj.is_null())
920 return;
921 Java_BookmarksBridge_bookmarkModelChanged(env, obj.obj());
924 void BookmarksBridge::BookmarkModelLoaded(BookmarkModel* model,
925 bool ids_reassigned) {
926 NotifyIfDoneLoading();
929 void BookmarksBridge::BookmarkModelBeingDeleted(BookmarkModel* model) {
930 if (!IsLoaded())
931 return;
933 JNIEnv* env = AttachCurrentThread();
934 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
935 if (obj.is_null())
936 return;
937 Java_BookmarksBridge_bookmarkModelDeleted(env, obj.obj());
940 void BookmarksBridge::BookmarkNodeMoved(BookmarkModel* model,
941 const BookmarkNode* old_parent,
942 int old_index,
943 const BookmarkNode* new_parent,
944 int new_index) {
945 if (!IsLoaded())
946 return;
948 JNIEnv* env = AttachCurrentThread();
949 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
950 if (obj.is_null())
951 return;
952 Java_BookmarksBridge_bookmarkNodeMoved(
953 env,
954 obj.obj(),
955 CreateJavaBookmark(old_parent).obj(),
956 old_index,
957 CreateJavaBookmark(new_parent).obj(),
958 new_index);
961 void BookmarksBridge::BookmarkNodeAdded(BookmarkModel* model,
962 const BookmarkNode* parent,
963 int index) {
964 if (!IsLoaded())
965 return;
967 JNIEnv* env = AttachCurrentThread();
968 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
969 if (obj.is_null())
970 return;
971 Java_BookmarksBridge_bookmarkNodeAdded(
972 env,
973 obj.obj(),
974 CreateJavaBookmark(parent).obj(),
975 index);
978 void BookmarksBridge::BookmarkNodeRemoved(BookmarkModel* model,
979 const BookmarkNode* parent,
980 int old_index,
981 const BookmarkNode* node,
982 const std::set<GURL>& removed_urls) {
983 if (!IsLoaded())
984 return;
986 JNIEnv* env = AttachCurrentThread();
987 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
988 if (obj.is_null())
989 return;
990 Java_BookmarksBridge_bookmarkNodeRemoved(
991 env,
992 obj.obj(),
993 CreateJavaBookmark(parent).obj(),
994 old_index,
995 CreateJavaBookmark(node).obj());
998 void BookmarksBridge::BookmarkAllUserNodesRemoved(
999 BookmarkModel* model,
1000 const std::set<GURL>& removed_urls) {
1001 if (!IsLoaded())
1002 return;
1004 JNIEnv* env = AttachCurrentThread();
1005 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1006 if (obj.is_null())
1007 return;
1008 Java_BookmarksBridge_bookmarkAllUserNodesRemoved(env, obj.obj());
1011 void BookmarksBridge::BookmarkNodeChanged(BookmarkModel* model,
1012 const BookmarkNode* node) {
1013 if (!IsLoaded())
1014 return;
1016 JNIEnv* env = AttachCurrentThread();
1017 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1018 if (obj.is_null())
1019 return;
1020 Java_BookmarksBridge_bookmarkNodeChanged(
1021 env,
1022 obj.obj(),
1023 CreateJavaBookmark(node).obj());
1026 void BookmarksBridge::BookmarkNodeChildrenReordered(BookmarkModel* model,
1027 const BookmarkNode* node) {
1028 if (!IsLoaded())
1029 return;
1031 JNIEnv* env = AttachCurrentThread();
1032 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1033 if (obj.is_null())
1034 return;
1035 Java_BookmarksBridge_bookmarkNodeChildrenReordered(
1036 env,
1037 obj.obj(),
1038 CreateJavaBookmark(node).obj());
1041 void BookmarksBridge::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
1042 if (!IsLoaded())
1043 return;
1045 JNIEnv* env = AttachCurrentThread();
1046 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1047 if (obj.is_null())
1048 return;
1049 Java_BookmarksBridge_extensiveBookmarkChangesBeginning(env, obj.obj());
1052 void BookmarksBridge::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
1053 if (!IsLoaded())
1054 return;
1056 JNIEnv* env = AttachCurrentThread();
1057 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1058 if (obj.is_null())
1059 return;
1060 Java_BookmarksBridge_extensiveBookmarkChangesEnded(env, obj.obj());
1063 void BookmarksBridge::PartnerShimChanged(PartnerBookmarksShim* shim) {
1064 if (!IsLoaded())
1065 return;
1067 BookmarkModelChanged();
1070 void BookmarksBridge::PartnerShimLoaded(PartnerBookmarksShim* shim) {
1071 NotifyIfDoneLoading();
1074 void BookmarksBridge::ShimBeingDeleted(PartnerBookmarksShim* shim) {
1075 partner_bookmarks_shim_ = NULL;