ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / android / bookmarks / bookmarks_bridge.cc
blob663ddc3c6a607cb26e7dc600e65a8e9cf0a53f6b
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, jobject obj, jobject j_profile) {
138 BookmarksBridge* delegate = new BookmarksBridge(env, obj, j_profile);
139 return reinterpret_cast<intptr_t>(delegate);
142 static jboolean IsEnhancedBookmarksFeatureEnabled(JNIEnv* env,
143 jclass clazz) {
144 return enhanced_bookmarks::IsEnhancedBookmarksEnabled();
147 jboolean BookmarksBridge::IsEditBookmarksEnabled(JNIEnv* env, jobject obj) {
148 return IsEditBookmarksEnabled();
151 void BookmarksBridge::LoadEmptyPartnerBookmarkShimForTesting(JNIEnv* env,
152 jobject obj) {
153 if (partner_bookmarks_shim_->IsLoaded())
154 return;
155 partner_bookmarks_shim_->SetPartnerBookmarksRoot(
156 new BookmarkPermanentNode(0));
157 DCHECK(partner_bookmarks_shim_->IsLoaded());
160 ScopedJavaLocalRef<jobject> BookmarksBridge::GetBookmarkByID(JNIEnv* env,
161 jobject obj,
162 jlong id,
163 jint type) {
164 DCHECK(IsLoaded());
165 const BookmarkNode* node = GetNodeByID(id, type);
166 return node ? CreateJavaBookmark(node) : ScopedJavaLocalRef<jobject>();
169 bool BookmarksBridge::IsDoingExtensiveChanges(JNIEnv* env, jobject obj) {
170 return bookmark_model_->IsDoingExtensiveChanges();
173 void BookmarksBridge::GetPermanentNodeIDs(JNIEnv* env,
174 jobject obj,
175 jobject j_result_obj) {
176 // TODO(kkimlabs): Remove this function.
177 DCHECK(IsLoaded());
179 base::StackVector<const BookmarkNode*, 8> permanent_nodes;
181 // Save all the permanent nodes.
182 const BookmarkNode* root_node = bookmark_model_->root_node();
183 permanent_nodes->push_back(root_node);
184 for (int i = 0; i < root_node->child_count(); ++i) {
185 permanent_nodes->push_back(root_node->GetChild(i));
187 permanent_nodes->push_back(
188 partner_bookmarks_shim_->GetPartnerBookmarksRoot());
190 // Write the permanent nodes to |j_result_obj|.
191 for (base::StackVector<const BookmarkNode*, 8>::ContainerType::const_iterator
192 it = permanent_nodes->begin();
193 it != permanent_nodes->end();
194 ++it) {
195 if (*it != NULL) {
196 Java_BookmarksBridge_addToBookmarkIdList(
197 env, j_result_obj, (*it)->id(), GetBookmarkType(*it));
202 void BookmarksBridge::GetTopLevelFolderParentIDs(JNIEnv* env,
203 jobject obj,
204 jobject j_result_obj) {
205 Java_BookmarksBridge_addToBookmarkIdList(
206 env, j_result_obj, bookmark_model_->root_node()->id(),
207 GetBookmarkType(bookmark_model_->root_node()));
210 void BookmarksBridge::GetTopLevelFolderIDs(JNIEnv* env,
211 jobject obj,
212 jboolean get_special,
213 jboolean get_normal,
214 jobject j_result_obj) {
215 DCHECK(IsLoaded());
216 std::vector<const BookmarkNode*> top_level_folders;
218 if (get_special) {
219 if (managed_bookmark_service_->managed_node() &&
220 managed_bookmark_service_->managed_node()->child_count() > 0) {
221 top_level_folders.push_back(managed_bookmark_service_->managed_node());
223 if (managed_bookmark_service_->supervised_node() &&
224 managed_bookmark_service_->supervised_node()->child_count() > 0) {
225 top_level_folders.push_back(managed_bookmark_service_->supervised_node());
227 if (partner_bookmarks_shim_->HasPartnerBookmarks()
228 && IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) {
229 top_level_folders.push_back(
230 partner_bookmarks_shim_->GetPartnerBookmarksRoot());
233 std::size_t special_count = top_level_folders.size();
235 if (get_normal) {
236 DCHECK_EQ(bookmark_model_->root_node()->child_count(), 5);
238 const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
239 for (int i = 0; i < mobile_node->child_count(); ++i) {
240 const BookmarkNode* node = mobile_node->GetChild(i);
241 if (node->is_folder()) {
242 top_level_folders.push_back(node);
246 const BookmarkNode* bookmark_bar_node =
247 bookmark_model_->bookmark_bar_node();
248 for (int i = 0; i < bookmark_bar_node->child_count(); ++i) {
249 const BookmarkNode* node = bookmark_bar_node->GetChild(i);
250 if (node->is_folder()) {
251 top_level_folders.push_back(node);
255 const BookmarkNode* other_node = bookmark_model_->other_node();
256 for (int i = 0; i < other_node->child_count(); ++i) {
257 const BookmarkNode* node = other_node->GetChild(i);
258 if (node->is_folder()) {
259 top_level_folders.push_back(node);
263 scoped_ptr<icu::Collator> collator = GetICUCollator();
264 std::stable_sort(top_level_folders.begin() + special_count,
265 top_level_folders.end(),
266 BookmarkTitleComparer(this, collator.get()));
269 for (std::vector<const BookmarkNode*>::const_iterator it =
270 top_level_folders.begin(); it != top_level_folders.end(); ++it) {
271 Java_BookmarksBridge_addToBookmarkIdList(env,
272 j_result_obj,
273 (*it)->id(),
274 GetBookmarkType(*it));
278 void BookmarksBridge::GetAllFoldersWithDepths(JNIEnv* env,
279 jobject obj,
280 jobject j_folders_obj,
281 jobject j_depths_obj) {
282 DCHECK(IsLoaded());
284 scoped_ptr<icu::Collator> collator = GetICUCollator();
286 // Vector to temporarily contain all child bookmarks at same level for sorting
287 std::vector<const BookmarkNode*> bookmarkList;
289 // Stack for Depth-First Search of bookmark model. It stores nodes and their
290 // heights.
291 std::stack<std::pair<const BookmarkNode*, int> > stk;
293 bookmarkList.push_back(bookmark_model_->mobile_node());
294 bookmarkList.push_back(bookmark_model_->bookmark_bar_node());
295 bookmarkList.push_back(bookmark_model_->other_node());
297 // Push all sorted top folders in stack and give them depth of 0.
298 // Note the order to push folders to stack should be opposite to the order in
299 // output.
300 for (std::vector<const BookmarkNode*>::reverse_iterator it =
301 bookmarkList.rbegin();
302 it != bookmarkList.rend();
303 ++it) {
304 stk.push(std::make_pair(*it, 0));
307 while (!stk.empty()) {
308 const BookmarkNode* node = stk.top().first;
309 int depth = stk.top().second;
310 stk.pop();
311 Java_BookmarksBridge_addToBookmarkIdListWithDepth(env,
312 j_folders_obj,
313 node->id(),
314 GetBookmarkType(node),
315 j_depths_obj,
316 depth);
317 bookmarkList.clear();
318 for (int i = 0; i < node->child_count(); ++i) {
319 const BookmarkNode* child = node->GetChild(i);
320 if (child->is_folder() &&
321 managed_bookmark_service_->CanBeEditedByUser(child)) {
322 bookmarkList.push_back(node->GetChild(i));
325 std::stable_sort(bookmarkList.begin(),
326 bookmarkList.end(),
327 BookmarkTitleComparer(this, collator.get()));
328 for (std::vector<const BookmarkNode*>::reverse_iterator it =
329 bookmarkList.rbegin();
330 it != bookmarkList.rend();
331 ++it) {
332 stk.push(std::make_pair(*it, depth + 1));
337 ScopedJavaLocalRef<jobject> BookmarksBridge::GetRootFolderId(JNIEnv* env,
338 jobject obj) {
339 const BookmarkNode* root_node = bookmark_model_->root_node();
340 ScopedJavaLocalRef<jobject> folder_id_obj =
341 JavaBookmarkIdCreateBookmarkId(
342 env, root_node->id(), GetBookmarkType(root_node));
343 return folder_id_obj;
346 ScopedJavaLocalRef<jobject> BookmarksBridge::GetMobileFolderId(JNIEnv* env,
347 jobject obj) {
348 const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
349 ScopedJavaLocalRef<jobject> folder_id_obj =
350 JavaBookmarkIdCreateBookmarkId(
351 env, mobile_node->id(), GetBookmarkType(mobile_node));
352 return folder_id_obj;
355 ScopedJavaLocalRef<jobject> BookmarksBridge::GetOtherFolderId(JNIEnv* env,
356 jobject obj) {
357 const BookmarkNode* other_node = bookmark_model_->other_node();
358 ScopedJavaLocalRef<jobject> folder_id_obj =
359 JavaBookmarkIdCreateBookmarkId(
360 env, other_node->id(), GetBookmarkType(other_node));
361 return folder_id_obj;
364 ScopedJavaLocalRef<jobject> BookmarksBridge::GetDesktopFolderId(JNIEnv* env,
365 jobject obj) {
366 const BookmarkNode* desktop_node = bookmark_model_->bookmark_bar_node();
367 ScopedJavaLocalRef<jobject> folder_id_obj =
368 JavaBookmarkIdCreateBookmarkId(
369 env, desktop_node->id(), GetBookmarkType(desktop_node));
370 return folder_id_obj;
373 void BookmarksBridge::GetChildIDs(JNIEnv* env,
374 jobject obj,
375 jlong id,
376 jint type,
377 jboolean get_folders,
378 jboolean get_bookmarks,
379 jobject j_result_obj) {
380 DCHECK(IsLoaded());
382 const BookmarkNode* parent = GetNodeByID(id, type);
383 if (!parent->is_folder() || !IsReachable(parent))
384 return;
386 // Get the folder contents
387 for (int i = 0; i < parent->child_count(); ++i) {
388 const BookmarkNode* child = parent->GetChild(i);
389 if (!IsFolderAvailable(child) || !IsReachable(child))
390 continue;
392 if ((child->is_folder() && get_folders) ||
393 (!child->is_folder() && get_bookmarks)) {
394 Java_BookmarksBridge_addToBookmarkIdList(
395 env, j_result_obj, child->id(), GetBookmarkType(child));
399 // Partner bookmark root node is under mobile node.
400 if (parent == bookmark_model_->mobile_node() && get_folders &&
401 partner_bookmarks_shim_->HasPartnerBookmarks() &&
402 IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) {
403 Java_BookmarksBridge_addToBookmarkIdList(
404 env,
405 j_result_obj,
406 partner_bookmarks_shim_->GetPartnerBookmarksRoot()->id(),
407 BookmarkType::BOOKMARK_TYPE_PARTNER);
411 ScopedJavaLocalRef<jobject> BookmarksBridge::GetChildAt(JNIEnv* env,
412 jobject obj,
413 jlong id,
414 jint type,
415 jint index) {
416 DCHECK(IsLoaded());
418 const BookmarkNode* parent = GetNodeByID(id, type);
419 DCHECK(parent);
420 const BookmarkNode* child = parent->GetChild(index);
421 return JavaBookmarkIdCreateBookmarkId(
422 env, child->id(), GetBookmarkType(child));
425 void BookmarksBridge::GetAllBookmarkIDsOrderedByCreationDate(
426 JNIEnv* env,
427 jobject obj,
428 jobject j_result_obj) {
429 DCHECK(IsLoaded());
430 std::list<const BookmarkNode*> folders;
431 std::vector<const BookmarkNode*> result;
432 folders.push_back(bookmark_model_->root_node());
434 for (std::list<const BookmarkNode*>::iterator folder_iter = folders.begin();
435 folder_iter != folders.end(); ++folder_iter) {
436 if (*folder_iter == NULL)
437 continue;
439 std::list<const BookmarkNode*>::iterator insert_iter = folder_iter;
440 ++insert_iter;
442 for (int i = 0; i < (*folder_iter)->child_count(); ++i) {
443 const BookmarkNode* child = (*folder_iter)->GetChild(i);
444 if (!IsReachable(child) ||
445 bookmarks::IsDescendantOf(
446 child, managed_bookmark_service_->managed_node()) ||
447 bookmarks::IsDescendantOf(
448 child, managed_bookmark_service_->supervised_node())) {
449 continue;
452 if (child->is_folder()) {
453 insert_iter = folders.insert(insert_iter, child);
454 } else {
455 result.push_back(child);
460 std::sort(
461 result.begin(), result.end(), BookmarkNodeCreationTimeCompareFunctor());
463 for (std::vector<const BookmarkNode*>::const_iterator iter = result.begin();
464 iter != result.end();
465 ++iter) {
466 const BookmarkNode* bookmark = *iter;
467 Java_BookmarksBridge_addToBookmarkIdList(
468 env, j_result_obj, bookmark->id(), GetBookmarkType(bookmark));
472 void BookmarksBridge::SetBookmarkTitle(JNIEnv* env,
473 jobject obj,
474 jlong id,
475 jint type,
476 jstring j_title) {
477 DCHECK(IsLoaded());
478 const BookmarkNode* bookmark = GetNodeByID(id, type);
479 const base::string16 title =
480 base::android::ConvertJavaStringToUTF16(env, j_title);
482 if (partner_bookmarks_shim_->IsPartnerBookmark(bookmark)) {
483 partner_bookmarks_shim_->RenameBookmark(bookmark, title);
484 } else {
485 bookmark_model_->SetTitle(bookmark, title);
489 void BookmarksBridge::SetBookmarkUrl(JNIEnv* env,
490 jobject obj,
491 jlong id,
492 jint type,
493 jstring url) {
494 DCHECK(IsLoaded());
495 bookmark_model_->SetURL(
496 GetNodeByID(id, type),
497 GURL(base::android::ConvertJavaStringToUTF16(env, url)));
500 bool BookmarksBridge::DoesBookmarkExist(JNIEnv* env,
501 jobject obj,
502 jlong id,
503 jint type) {
504 DCHECK(IsLoaded());
506 const BookmarkNode* node = GetNodeByID(id, type);
508 if (!node)
509 return false;
511 if (type == BookmarkType::BOOKMARK_TYPE_NORMAL) {
512 return true;
513 } else {
514 DCHECK(type == BookmarkType::BOOKMARK_TYPE_PARTNER);
515 return partner_bookmarks_shim_->IsReachable(node);
519 void BookmarksBridge::GetBookmarksForFolder(JNIEnv* env,
520 jobject obj,
521 jobject j_folder_id_obj,
522 jobject j_callback_obj,
523 jobject j_result_obj) {
524 DCHECK(IsLoaded());
525 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj);
526 int type = JavaBookmarkIdGetType(env, j_folder_id_obj);
527 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
529 if (!folder->is_folder() || !IsReachable(folder))
530 return;
532 // Recreate the java bookmarkId object due to fallback.
533 ScopedJavaLocalRef<jobject> folder_id_obj =
534 JavaBookmarkIdCreateBookmarkId(
535 env, folder->id(), GetBookmarkType(folder));
536 j_folder_id_obj = folder_id_obj.obj();
538 // Get the folder contents.
539 for (int i = 0; i < folder->child_count(); ++i) {
540 const BookmarkNode* node = folder->GetChild(i);
541 if (!IsFolderAvailable(node))
542 continue;
543 ExtractBookmarkNodeInformation(node, j_result_obj);
546 if (folder == bookmark_model_->mobile_node() &&
547 partner_bookmarks_shim_->HasPartnerBookmarks()) {
548 ExtractBookmarkNodeInformation(
549 partner_bookmarks_shim_->GetPartnerBookmarksRoot(),
550 j_result_obj);
553 if (j_callback_obj) {
554 Java_BookmarksCallback_onBookmarksAvailable(
555 env, j_callback_obj, j_folder_id_obj, j_result_obj);
559 jboolean BookmarksBridge::IsFolderVisible(JNIEnv* env,
560 jobject obj,
561 jlong id,
562 jint type) {
563 if (type == BookmarkType::BOOKMARK_TYPE_NORMAL) {
564 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
565 bookmark_model_, static_cast<int64>(id));
566 return node->IsVisible();
567 } else if (type == BookmarkType::BOOKMARK_TYPE_PARTNER) {
568 const BookmarkNode* node = partner_bookmarks_shim_->GetNodeByID(
569 static_cast<long>(id));
570 return partner_bookmarks_shim_->IsReachable(node);
573 NOTREACHED();
574 return false;
577 void BookmarksBridge::GetCurrentFolderHierarchy(JNIEnv* env,
578 jobject obj,
579 jobject j_folder_id_obj,
580 jobject j_callback_obj,
581 jobject j_result_obj) {
582 DCHECK(IsLoaded());
583 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj);
584 int type = JavaBookmarkIdGetType(env, j_folder_id_obj);
585 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type);
587 if (!folder->is_folder() || !IsReachable(folder))
588 return;
590 // Recreate the java bookmarkId object due to fallback.
591 ScopedJavaLocalRef<jobject> folder_id_obj =
592 JavaBookmarkIdCreateBookmarkId(
593 env, folder->id(), GetBookmarkType(folder));
594 j_folder_id_obj = folder_id_obj.obj();
596 // Get the folder hierarchy.
597 const BookmarkNode* node = folder;
598 while (node) {
599 ExtractBookmarkNodeInformation(node, j_result_obj);
600 node = GetParentNode(node);
603 Java_BookmarksCallback_onBookmarksFolderHierarchyAvailable(
604 env, j_callback_obj, j_folder_id_obj, j_result_obj);
607 void BookmarksBridge::SearchBookmarks(JNIEnv* env,
608 jobject obj,
609 jobject j_list,
610 jstring j_query,
611 jint max_results) {
612 DCHECK(bookmark_model_->loaded());
614 std::vector<bookmarks::BookmarkMatch> results;
615 bookmark_model_->GetBookmarksMatching(
616 base::android::ConvertJavaStringToUTF16(env, j_query),
617 max_results,
618 query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH,
619 &results);
620 for (const bookmarks::BookmarkMatch& match : results) {
621 const BookmarkNode* node = match.node;
623 std::vector<int> title_match_start_positions;
624 std::vector<int> title_match_end_positions;
625 for (auto position : match.title_match_positions) {
626 title_match_start_positions.push_back(position.first);
627 title_match_end_positions.push_back(position.second);
630 std::vector<int> url_match_start_positions;
631 std::vector<int> url_match_end_positions;
632 for (auto position : match.url_match_positions) {
633 url_match_start_positions.push_back(position.first);
634 url_match_end_positions.push_back(position.second);
637 Java_BookmarksBridge_addToBookmarkMatchList(
638 env, j_list, node->id(), node->type(),
639 ToJavaIntArray(env, title_match_start_positions).obj(),
640 ToJavaIntArray(env, title_match_end_positions).obj(),
641 ToJavaIntArray(env, url_match_start_positions).obj(),
642 ToJavaIntArray(env, url_match_end_positions).obj());
646 ScopedJavaLocalRef<jobject> BookmarksBridge::AddFolder(JNIEnv* env,
647 jobject obj,
648 jobject j_parent_id_obj,
649 jint index,
650 jstring j_title) {
651 DCHECK(IsLoaded());
652 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
653 int type = JavaBookmarkIdGetType(env, j_parent_id_obj);
654 const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
656 const BookmarkNode* new_node = bookmark_model_->AddFolder(
657 parent, index, base::android::ConvertJavaStringToUTF16(env, j_title));
658 if (!new_node) {
659 NOTREACHED();
660 return ScopedJavaLocalRef<jobject>();
662 ScopedJavaLocalRef<jobject> new_java_obj =
663 JavaBookmarkIdCreateBookmarkId(
664 env, new_node->id(), GetBookmarkType(new_node));
665 return new_java_obj;
668 void BookmarksBridge::DeleteBookmark(JNIEnv* env,
669 jobject obj,
670 jobject j_bookmark_id_obj) {
671 DCHECK_CURRENTLY_ON(BrowserThread::UI);
672 DCHECK(IsLoaded());
674 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
675 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj);
676 const BookmarkNode* node = GetNodeByID(bookmark_id, type);
677 if (!IsEditable(node)) {
678 NOTREACHED();
679 return;
682 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
683 partner_bookmarks_shim_->RemoveBookmark(node);
684 else
685 bookmark_model_->Remove(node);
688 void BookmarksBridge::MoveBookmark(JNIEnv* env,
689 jobject obj,
690 jobject j_bookmark_id_obj,
691 jobject j_parent_id_obj,
692 jint index) {
693 DCHECK_CURRENTLY_ON(BrowserThread::UI);
694 DCHECK(IsLoaded());
696 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
697 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj);
698 const BookmarkNode* node = GetNodeByID(bookmark_id, type);
699 if (!IsEditable(node)) {
700 NOTREACHED();
701 return;
703 bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
704 type = JavaBookmarkIdGetType(env, j_parent_id_obj);
705 const BookmarkNode* new_parent_node = GetNodeByID(bookmark_id, type);
706 bookmark_model_->Move(node, new_parent_node, index);
709 ScopedJavaLocalRef<jobject> BookmarksBridge::AddBookmark(
710 JNIEnv* env,
711 jobject obj,
712 jobject j_parent_id_obj,
713 jint index,
714 jstring j_title,
715 jstring j_url) {
716 DCHECK(IsLoaded());
717 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
718 int type = JavaBookmarkIdGetType(env, j_parent_id_obj);
719 const BookmarkNode* parent = GetNodeByID(bookmark_id, type);
721 const BookmarkNode* new_node = bookmark_model_->AddURL(
722 parent,
723 index,
724 base::android::ConvertJavaStringToUTF16(env, j_title),
725 GURL(base::android::ConvertJavaStringToUTF16(env, j_url)));
726 if (!new_node) {
727 NOTREACHED();
728 return ScopedJavaLocalRef<jobject>();
730 ScopedJavaLocalRef<jobject> new_java_obj =
731 JavaBookmarkIdCreateBookmarkId(
732 env, new_node->id(), GetBookmarkType(new_node));
733 return new_java_obj;
736 void BookmarksBridge::Undo(JNIEnv* env, jobject obj) {
737 DCHECK_CURRENTLY_ON(BrowserThread::UI);
738 DCHECK(IsLoaded());
739 BookmarkUndoService* undo_service =
740 BookmarkUndoServiceFactory::GetForProfile(profile_);
741 UndoManager* undo_manager = undo_service->undo_manager();
742 undo_manager->Undo();
745 void BookmarksBridge::StartGroupingUndos(JNIEnv* env, jobject obj) {
746 DCHECK_CURRENTLY_ON(BrowserThread::UI);
747 DCHECK(IsLoaded());
748 DCHECK(!grouped_bookmark_actions_.get()); // shouldn't have started already
749 grouped_bookmark_actions_.reset(
750 new bookmarks::ScopedGroupBookmarkActions(bookmark_model_));
753 void BookmarksBridge::EndGroupingUndos(JNIEnv* env, jobject obj) {
754 DCHECK_CURRENTLY_ON(BrowserThread::UI);
755 DCHECK(IsLoaded());
756 DCHECK(grouped_bookmark_actions_.get()); // should only call after start
757 grouped_bookmark_actions_.reset();
760 base::string16 BookmarksBridge::GetTitle(const BookmarkNode* node) const {
761 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
762 return partner_bookmarks_shim_->GetTitle(node);
764 if (node == bookmark_model_->bookmark_bar_node()
765 && enhanced_bookmarks::IsEnhancedBookmarksEnabled()) {
766 return l10n_util::GetStringUTF16(IDS_ENHANCED_BOOKMARK_BAR_FOLDER_NAME);
769 return node->GetTitle();
772 ScopedJavaLocalRef<jobject> BookmarksBridge::CreateJavaBookmark(
773 const BookmarkNode* node) {
774 JNIEnv* env = AttachCurrentThread();
776 const BookmarkNode* parent = GetParentNode(node);
777 int64 parent_id = parent ? parent->id() : -1;
779 std::string url;
780 if (node->is_url())
781 url = node->url().spec();
783 return Java_BookmarksBridge_createBookmarkItem(
784 env,
785 node->id(),
786 GetBookmarkType(node),
787 ConvertUTF16ToJavaString(env, GetTitle(node)).obj(),
788 ConvertUTF8ToJavaString(env, url).obj(),
789 node->is_folder(),
790 parent_id,
791 GetBookmarkType(parent),
792 IsEditable(node),
793 IsManaged(node));
796 void BookmarksBridge::ExtractBookmarkNodeInformation(const BookmarkNode* node,
797 jobject j_result_obj) {
798 JNIEnv* env = AttachCurrentThread();
799 if (!IsReachable(node))
800 return;
801 Java_BookmarksBridge_addToList(
802 env, j_result_obj, CreateJavaBookmark(node).obj());
805 const BookmarkNode* BookmarksBridge::GetNodeByID(long node_id, int type) {
806 const BookmarkNode* node;
807 if (type == BookmarkType::BOOKMARK_TYPE_PARTNER) {
808 node = partner_bookmarks_shim_->GetNodeByID(
809 static_cast<int64>(node_id));
810 } else {
811 node = bookmarks::GetBookmarkNodeByID(bookmark_model_,
812 static_cast<int64>(node_id));
814 return node;
817 const BookmarkNode* BookmarksBridge::GetFolderWithFallback(long folder_id,
818 int type) {
819 const BookmarkNode* folder = GetNodeByID(folder_id, type);
820 if (!folder || folder->type() == BookmarkNode::URL ||
821 !IsFolderAvailable(folder)) {
822 if (!managed_bookmark_service_->managed_node()->empty())
823 folder = managed_bookmark_service_->managed_node();
824 else
825 folder = bookmark_model_->mobile_node();
827 return folder;
830 bool BookmarksBridge::IsEditBookmarksEnabled() const {
831 return profile_->GetPrefs()->GetBoolean(
832 bookmarks::prefs::kEditBookmarksEnabled);
835 bool BookmarksBridge::IsEditable(const BookmarkNode* node) const {
836 if (!node || (node->type() != BookmarkNode::FOLDER &&
837 node->type() != BookmarkNode::URL)) {
838 return false;
840 if (!IsEditBookmarksEnabled())
841 return false;
842 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
843 return partner_bookmarks_shim_->IsEditable(node);
844 return managed_bookmark_service_->CanBeEditedByUser(node);
847 bool BookmarksBridge::IsManaged(const BookmarkNode* node) const {
848 return bookmarks::IsDescendantOf(node,
849 managed_bookmark_service_->managed_node());
852 const BookmarkNode* BookmarksBridge::GetParentNode(const BookmarkNode* node) {
853 DCHECK(IsLoaded());
854 if (node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) {
855 return bookmark_model_->mobile_node();
856 } else {
857 return node->parent();
861 int BookmarksBridge::GetBookmarkType(const BookmarkNode* node) {
862 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
863 return BookmarkType::BOOKMARK_TYPE_PARTNER;
864 else
865 return BookmarkType::BOOKMARK_TYPE_NORMAL;
868 bool BookmarksBridge::IsReachable(const BookmarkNode* node) const {
869 if (!partner_bookmarks_shim_->IsPartnerBookmark(node))
870 return true;
871 return partner_bookmarks_shim_->IsReachable(node);
874 bool BookmarksBridge::IsLoaded() const {
875 return (bookmark_model_->loaded() && partner_bookmarks_shim_->IsLoaded());
878 bool BookmarksBridge::IsFolderAvailable(
879 const BookmarkNode* folder) const {
880 // The managed bookmarks folder is not shown if there are no bookmarks
881 // configured via policy.
882 if (folder == managed_bookmark_service_->managed_node() && folder->empty())
883 return false;
884 // Similarly, the supervised bookmarks folder is not shown if there are no
885 // bookmarks configured by the custodian.
886 if (folder == managed_bookmark_service_->supervised_node() && folder->empty())
887 return false;
889 SigninManager* signin = SigninManagerFactory::GetForProfile(
890 profile_->GetOriginalProfile());
891 return (folder->type() != BookmarkNode::BOOKMARK_BAR &&
892 folder->type() != BookmarkNode::OTHER_NODE) ||
893 (signin && signin->IsAuthenticated());
896 void BookmarksBridge::NotifyIfDoneLoading() {
897 if (!IsLoaded())
898 return;
899 JNIEnv* env = AttachCurrentThread();
900 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
901 if (obj.is_null())
902 return;
903 Java_BookmarksBridge_bookmarkModelLoaded(env, obj.obj());
906 // ------------- Observer-related methods ------------- //
908 void BookmarksBridge::BookmarkModelChanged() {
909 if (!IsLoaded())
910 return;
912 // Called when there are changes to the bookmark model. It is most
913 // likely changes to the partner bookmarks.
914 JNIEnv* env = AttachCurrentThread();
915 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
916 if (obj.is_null())
917 return;
918 Java_BookmarksBridge_bookmarkModelChanged(env, obj.obj());
921 void BookmarksBridge::BookmarkModelLoaded(BookmarkModel* model,
922 bool ids_reassigned) {
923 NotifyIfDoneLoading();
926 void BookmarksBridge::BookmarkModelBeingDeleted(BookmarkModel* model) {
927 if (!IsLoaded())
928 return;
930 JNIEnv* env = AttachCurrentThread();
931 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
932 if (obj.is_null())
933 return;
934 Java_BookmarksBridge_bookmarkModelDeleted(env, obj.obj());
937 void BookmarksBridge::BookmarkNodeMoved(BookmarkModel* model,
938 const BookmarkNode* old_parent,
939 int old_index,
940 const BookmarkNode* new_parent,
941 int new_index) {
942 if (!IsLoaded())
943 return;
945 JNIEnv* env = AttachCurrentThread();
946 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
947 if (obj.is_null())
948 return;
949 Java_BookmarksBridge_bookmarkNodeMoved(
950 env,
951 obj.obj(),
952 CreateJavaBookmark(old_parent).obj(),
953 old_index,
954 CreateJavaBookmark(new_parent).obj(),
955 new_index);
958 void BookmarksBridge::BookmarkNodeAdded(BookmarkModel* model,
959 const BookmarkNode* parent,
960 int index) {
961 if (!IsLoaded())
962 return;
964 JNIEnv* env = AttachCurrentThread();
965 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
966 if (obj.is_null())
967 return;
968 Java_BookmarksBridge_bookmarkNodeAdded(
969 env,
970 obj.obj(),
971 CreateJavaBookmark(parent).obj(),
972 index);
975 void BookmarksBridge::BookmarkNodeRemoved(BookmarkModel* model,
976 const BookmarkNode* parent,
977 int old_index,
978 const BookmarkNode* node,
979 const std::set<GURL>& removed_urls) {
980 if (!IsLoaded())
981 return;
983 JNIEnv* env = AttachCurrentThread();
984 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
985 if (obj.is_null())
986 return;
987 Java_BookmarksBridge_bookmarkNodeRemoved(
988 env,
989 obj.obj(),
990 CreateJavaBookmark(parent).obj(),
991 old_index,
992 CreateJavaBookmark(node).obj());
995 void BookmarksBridge::BookmarkAllUserNodesRemoved(
996 BookmarkModel* model,
997 const std::set<GURL>& removed_urls) {
998 if (!IsLoaded())
999 return;
1001 JNIEnv* env = AttachCurrentThread();
1002 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1003 if (obj.is_null())
1004 return;
1005 Java_BookmarksBridge_bookmarkAllUserNodesRemoved(env, obj.obj());
1008 void BookmarksBridge::BookmarkNodeChanged(BookmarkModel* model,
1009 const BookmarkNode* node) {
1010 if (!IsLoaded())
1011 return;
1013 JNIEnv* env = AttachCurrentThread();
1014 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1015 if (obj.is_null())
1016 return;
1017 Java_BookmarksBridge_bookmarkNodeChanged(
1018 env,
1019 obj.obj(),
1020 CreateJavaBookmark(node).obj());
1023 void BookmarksBridge::BookmarkNodeChildrenReordered(BookmarkModel* model,
1024 const BookmarkNode* node) {
1025 if (!IsLoaded())
1026 return;
1028 JNIEnv* env = AttachCurrentThread();
1029 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1030 if (obj.is_null())
1031 return;
1032 Java_BookmarksBridge_bookmarkNodeChildrenReordered(
1033 env,
1034 obj.obj(),
1035 CreateJavaBookmark(node).obj());
1038 void BookmarksBridge::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
1039 if (!IsLoaded())
1040 return;
1042 JNIEnv* env = AttachCurrentThread();
1043 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1044 if (obj.is_null())
1045 return;
1046 Java_BookmarksBridge_extensiveBookmarkChangesBeginning(env, obj.obj());
1049 void BookmarksBridge::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
1050 if (!IsLoaded())
1051 return;
1053 JNIEnv* env = AttachCurrentThread();
1054 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
1055 if (obj.is_null())
1056 return;
1057 Java_BookmarksBridge_extensiveBookmarkChangesEnded(env, obj.obj());
1060 void BookmarksBridge::PartnerShimChanged(PartnerBookmarksShim* shim) {
1061 if (!IsLoaded())
1062 return;
1064 BookmarkModelChanged();
1067 void BookmarksBridge::PartnerShimLoaded(PartnerBookmarksShim* shim) {
1068 NotifyIfDoneLoading();
1071 void BookmarksBridge::ShimBeingDeleted(PartnerBookmarksShim* shim) {
1072 partner_bookmarks_shim_ = NULL;