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/containers/stack_container.h" 9 #include "base/i18n/string_compare.h" 10 #include "base/prefs/pref_service.h" 11 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 12 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h" 13 #include "chrome/browser/bookmarks/enhanced_bookmarks_features.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/profiles/profile_manager.h" 18 #include "chrome/browser/signin/signin_manager_factory.h" 19 #include "chrome/browser/undo/bookmark_undo_service.h" 20 #include "chrome/browser/undo/bookmark_undo_service_factory.h" 21 #include "chrome/browser/undo/undo_manager.h" 22 #include "chrome/common/pref_names.h" 23 #include "components/bookmarks/browser/bookmark_model.h" 24 #include "components/bookmarks/browser/bookmark_utils.h" 25 #include "components/bookmarks/browser/scoped_group_bookmark_actions.h" 26 #include "components/bookmarks/common/android/bookmark_type.h" 27 #include "components/signin/core/browser/signin_manager.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "jni/BookmarksBridge_jni.h" 30 31 using base::android::AttachCurrentThread; 32 using base::android::ConvertUTF8ToJavaString; 33 using base::android::ConvertUTF16ToJavaString; 34 using base::android::ScopedJavaLocalRef; 35 using base::android::ScopedJavaGlobalRef; 36 using bookmarks::android::JavaBookmarkIdGetId; 37 using bookmarks::android::JavaBookmarkIdGetType; 38 using bookmarks::BookmarkType; 39 using content::BrowserThread; 40 41 namespace { 42 43 class BookmarkNodeCreationTimeCompareFunctor { 44 public: 45 bool operator()(const BookmarkNode* lhs, const BookmarkNode* rhs) { 46 return lhs->date_added().ToJavaTime() > rhs->date_added().ToJavaTime(); 47 } 48 }; 49 50 class BookmarkTitleComparer { 51 public: 52 explicit BookmarkTitleComparer(const icu::Collator* collator) 53 : collator_(collator) {} 54 55 bool operator()(const BookmarkNode* lhs, const BookmarkNode* rhs) { 56 if (collator_) { 57 return base::i18n::CompareString16WithCollator( 58 collator_, lhs->GetTitle(), rhs->GetTitle()) == UCOL_LESS; 59 } else { 60 return lhs->GetTitle() < rhs->GetTitle(); 61 } 62 } 63 64 private: 65 const icu::Collator* collator_; 66 }; 67 68 scoped_ptr<icu::Collator> GetICUCollator() { 69 UErrorCode error = U_ZERO_ERROR; 70 scoped_ptr<icu::Collator> collator_; 71 collator_.reset(icu::Collator::createInstance(error)); 72 if (U_FAILURE(error)) 73 collator_.reset(NULL); 74 75 return collator_.Pass(); 76 } 77 78 } // namespace 79 80 BookmarksBridge::BookmarksBridge(JNIEnv* env, 81 jobject obj, 82 jobject j_profile) 83 : weak_java_ref_(env, obj), 84 bookmark_model_(NULL), 85 client_(NULL), 86 partner_bookmarks_shim_(NULL) { 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 88 profile_ = ProfileAndroid::FromProfileAndroid(j_profile); 89 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_); 90 client_ = ChromeBookmarkClientFactory::GetForProfile(profile_); 91 92 // Registers the notifications we are interested. 93 bookmark_model_->AddObserver(this); 94 95 // Create the partner Bookmarks shim as early as possible (but don't attach). 96 partner_bookmarks_shim_ = PartnerBookmarksShim::BuildForBrowserContext( 97 chrome::GetBrowserContextRedirectedInIncognito(profile_)); 98 partner_bookmarks_shim_->AddObserver(this); 99 100 NotifyIfDoneLoading(); 101 102 // Since a sync or import could have started before this class is 103 // initialized, we need to make sure that our initial state is 104 // up to date. 105 if (bookmark_model_->IsDoingExtensiveChanges()) 106 ExtensiveBookmarkChangesBeginning(bookmark_model_); 107 } 108 109 BookmarksBridge::~BookmarksBridge() { 110 bookmark_model_->RemoveObserver(this); 111 if (partner_bookmarks_shim_) 112 partner_bookmarks_shim_->RemoveObserver(this); 113 } 114 115 void BookmarksBridge::Destroy(JNIEnv*, jobject) { 116 delete this; 117 } 118 119 // static 120 bool BookmarksBridge::RegisterBookmarksBridge(JNIEnv* env) { 121 return RegisterNativesImpl(env); 122 } 123 124 static jlong Init(JNIEnv* env, jobject obj, jobject j_profile) { 125 BookmarksBridge* delegate = new BookmarksBridge(env, obj, j_profile); 126 return reinterpret_cast<intptr_t>(delegate); 127 } 128 129 static jlong GetNativeBookmarkModel(JNIEnv* env, 130 jclass caller, 131 jobject j_profile) { 132 Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile); 133 BookmarkModel *bookmark_model_ = BookmarkModelFactory::GetForProfile(profile); 134 return reinterpret_cast<jlong>(bookmark_model_); 135 } 136 137 static jboolean IsEnhancedBookmarksFeatureEnabled(JNIEnv* env, 138 jclass clazz, 139 jobject j_profile) { 140 Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile); 141 return IsEnhancedBookmarksEnabled(profile->GetPrefs()); 142 } 143 144 static bool IsEditBookmarksEnabled() { 145 return ProfileManager::GetLastUsedProfile()->GetPrefs()->GetBoolean( 146 bookmarks::prefs::kEditBookmarksEnabled); 147 } 148 149 static jboolean IsEditBookmarksEnabled(JNIEnv* env, jclass clazz) { 150 return IsEditBookmarksEnabled(); 151 } 152 153 void BookmarksBridge::LoadEmptyPartnerBookmarkShimForTesting(JNIEnv* env, 154 jobject obj) { 155 if (partner_bookmarks_shim_->IsLoaded()) 156 return; 157 partner_bookmarks_shim_->SetPartnerBookmarksRoot( 158 new BookmarkPermanentNode(0)); 159 DCHECK(partner_bookmarks_shim_->IsLoaded()); 160 } 161 162 ScopedJavaLocalRef<jobject> BookmarksBridge::GetBookmarkByID(JNIEnv* env, 163 jobject obj, 164 jlong id, 165 jint type) { 166 DCHECK(IsLoaded()); 167 return CreateJavaBookmark(GetNodeByID(id, type)); 168 } 169 170 bool BookmarksBridge::IsDoingExtensiveChanges(JNIEnv* env, jobject obj) { 171 return bookmark_model_->IsDoingExtensiveChanges(); 172 } 173 174 void BookmarksBridge::GetPermanentNodeIDs(JNIEnv* env, 175 jobject obj, 176 jobject j_result_obj) { 177 // TODO(kkimlabs): Remove this function. 178 DCHECK(IsLoaded()); 179 180 base::StackVector<const BookmarkNode*, 8> permanent_nodes; 181 182 // Save all the permanent nodes. 183 const BookmarkNode* root_node = bookmark_model_->root_node(); 184 permanent_nodes->push_back(root_node); 185 for (int i = 0; i < root_node->child_count(); ++i) { 186 permanent_nodes->push_back(root_node->GetChild(i)); 187 } 188 permanent_nodes->push_back( 189 partner_bookmarks_shim_->GetPartnerBookmarksRoot()); 190 191 // Write the permanent nodes to |j_result_obj|. 192 for (base::StackVector<const BookmarkNode*, 8>::ContainerType::const_iterator 193 it = permanent_nodes->begin(); 194 it != permanent_nodes->end(); 195 ++it) { 196 if (*it != NULL) { 197 Java_BookmarksBridge_addToBookmarkIdList( 198 env, j_result_obj, (*it)->id(), GetBookmarkType(*it)); 199 } 200 } 201 } 202 203 void BookmarksBridge::GetTopLevelFolderParentIDs(JNIEnv* env, 204 jobject obj, 205 jobject j_result_obj) { 206 Java_BookmarksBridge_addToBookmarkIdList( 207 env, j_result_obj, bookmark_model_->root_node()->id(), 208 GetBookmarkType(bookmark_model_->root_node())); 209 Java_BookmarksBridge_addToBookmarkIdList( 210 env, j_result_obj, bookmark_model_->mobile_node()->id(), 211 GetBookmarkType(bookmark_model_->mobile_node())); 212 Java_BookmarksBridge_addToBookmarkIdList( 213 env, j_result_obj, bookmark_model_->other_node()->id(), 214 GetBookmarkType(bookmark_model_->other_node())); 215 } 216 217 void BookmarksBridge::GetTopLevelFolderIDs(JNIEnv* env, 218 jobject obj, 219 jboolean get_special, 220 jboolean get_normal, 221 jobject j_result_obj) { 222 DCHECK(IsLoaded()); 223 std::vector<const BookmarkNode*> top_level_folders; 224 225 if (get_special) { 226 if (client_->managed_node() && 227 client_->managed_node()->child_count() > 0) { 228 top_level_folders.push_back(client_->managed_node()); 229 } 230 if (partner_bookmarks_shim_->HasPartnerBookmarks()) { 231 top_level_folders.push_back( 232 partner_bookmarks_shim_->GetPartnerBookmarksRoot()); 233 } 234 } 235 std::size_t special_count = top_level_folders.size(); 236 237 if (get_normal) { 238 DCHECK_EQ(bookmark_model_->root_node()->child_count(), 4); 239 240 top_level_folders.push_back(bookmark_model_->bookmark_bar_node()); 241 242 const BookmarkNode* mobile_node = bookmark_model_->mobile_node(); 243 for (int i = 0; i < mobile_node->child_count(); ++i) { 244 const BookmarkNode* node = mobile_node->GetChild(i); 245 if (node->is_folder()) { 246 top_level_folders.push_back(node); 247 } 248 } 249 250 const BookmarkNode* other_node = bookmark_model_->other_node(); 251 for (int i = 0; i < other_node->child_count(); ++i) { 252 const BookmarkNode* node = other_node->GetChild(i); 253 if (node->is_folder()) { 254 top_level_folders.push_back(node); 255 } 256 } 257 258 scoped_ptr<icu::Collator> collator = GetICUCollator(); 259 std::stable_sort(top_level_folders.begin() + special_count, 260 top_level_folders.end(), 261 BookmarkTitleComparer(collator.get())); 262 } 263 264 for (std::vector<const BookmarkNode*>::const_iterator it = 265 top_level_folders.begin(); it != top_level_folders.end(); ++it) { 266 Java_BookmarksBridge_addToBookmarkIdList(env, 267 j_result_obj, 268 (*it)->id(), 269 GetBookmarkType(*it)); 270 } 271 } 272 273 void BookmarksBridge::GetUncategorizedBookmarkIDs(JNIEnv* env, 274 jobject obj, 275 jobject j_result_obj) { 276 const BookmarkNode* mobile_node = bookmark_model_->mobile_node(); 277 for (int i = 0; i < mobile_node->child_count(); ++i) { 278 const BookmarkNode* node = mobile_node->GetChild(i); 279 if (!node->is_folder()) { 280 Java_BookmarksBridge_addToBookmarkIdList(env, 281 j_result_obj, 282 node->id(), 283 GetBookmarkType(node)); 284 } 285 } 286 287 const BookmarkNode* other_node = bookmark_model_->other_node(); 288 for (int i = 0; i < other_node->child_count(); ++i) { 289 const BookmarkNode* node = other_node->GetChild(i); 290 if (!node->is_folder()) { 291 Java_BookmarksBridge_addToBookmarkIdList(env, 292 j_result_obj, 293 node->id(), 294 GetBookmarkType(node)); 295 } 296 } 297 } 298 299 void BookmarksBridge::GetAllFoldersWithDepths(JNIEnv* env, 300 jobject obj, 301 jobject j_folders_obj, 302 jobject j_depths_obj) { 303 DCHECK(IsLoaded()); 304 305 const BookmarkNode* desktop = bookmark_model_->bookmark_bar_node(); 306 const BookmarkNode* mobile = bookmark_model_->mobile_node(); 307 const BookmarkNode* other = bookmark_model_->other_node(); 308 309 scoped_ptr<icu::Collator> collator = GetICUCollator(); 310 311 // Vector to temporarily contain all child bookmarks at same level for sorting 312 std::vector<const BookmarkNode*> bookmarkList; 313 // Stack for Depth-First Search of bookmark model. It stores nodes and their 314 // heights. 315 std::stack<std::pair<const BookmarkNode*, int> > stk; 316 317 for (int i = 0; i < mobile->child_count(); ++i) { 318 const BookmarkNode* child = mobile->GetChild(i); 319 if (child->is_folder() && client_->CanBeEditedByUser(child)) 320 bookmarkList.push_back(child); 321 } 322 for (int i = 0; i < other->child_count(); ++i) { 323 const BookmarkNode* child = other->GetChild(i); 324 if (child->is_folder() && client_->CanBeEditedByUser(child)) 325 bookmarkList.push_back(child); 326 } 327 bookmarkList.push_back(desktop); 328 std::stable_sort(bookmarkList.begin(), 329 bookmarkList.end(), 330 BookmarkTitleComparer(collator.get())); 331 332 // Push all sorted top folders in stack and give them depth of 0. 333 // Note the order to push folders to stack should be opposite to the order in 334 // output. 335 for (std::vector<const BookmarkNode*>::reverse_iterator it = 336 bookmarkList.rbegin(); 337 it != bookmarkList.rend(); 338 ++it) { 339 stk.push(std::make_pair(*it, 0)); 340 } 341 342 while (!stk.empty()) { 343 const BookmarkNode* node = stk.top().first; 344 int depth = stk.top().second; 345 stk.pop(); 346 Java_BookmarksBridge_addToBookmarkIdListWithDepth(env, 347 j_folders_obj, 348 node->id(), 349 GetBookmarkType(node), 350 j_depths_obj, 351 depth); 352 bookmarkList.clear(); 353 for (int i = 0; i < node->child_count(); ++i) { 354 const BookmarkNode* child = node->GetChild(i); 355 if (child->is_folder() && client_->CanBeEditedByUser(child)) 356 bookmarkList.push_back(node->GetChild(i)); 357 } 358 std::stable_sort(bookmarkList.begin(), 359 bookmarkList.end(), 360 BookmarkTitleComparer(collator.get())); 361 for (std::vector<const BookmarkNode*>::reverse_iterator it = 362 bookmarkList.rbegin(); 363 it != bookmarkList.rend(); 364 ++it) { 365 stk.push(std::make_pair(*it, depth + 1)); 366 } 367 } 368 } 369 370 ScopedJavaLocalRef<jobject> BookmarksBridge::GetMobileFolderId(JNIEnv* env, 371 jobject obj) { 372 const BookmarkNode* mobile_node = bookmark_model_->mobile_node(); 373 ScopedJavaLocalRef<jobject> folder_id_obj = 374 Java_BookmarksBridge_createBookmarkId( 375 env, mobile_node->id(), GetBookmarkType(mobile_node)); 376 return folder_id_obj; 377 } 378 379 ScopedJavaLocalRef<jobject> BookmarksBridge::GetOtherFolderId(JNIEnv* env, 380 jobject obj) { 381 const BookmarkNode* other_node = bookmark_model_->other_node(); 382 ScopedJavaLocalRef<jobject> folder_id_obj = 383 Java_BookmarksBridge_createBookmarkId( 384 env, other_node->id(), GetBookmarkType(other_node)); 385 return folder_id_obj; 386 } 387 388 ScopedJavaLocalRef<jobject> BookmarksBridge::GetDesktopFolderId(JNIEnv* env, 389 jobject obj) { 390 const BookmarkNode* desktop_node = bookmark_model_->bookmark_bar_node(); 391 ScopedJavaLocalRef<jobject> folder_id_obj = 392 Java_BookmarksBridge_createBookmarkId( 393 env, desktop_node->id(), GetBookmarkType(desktop_node)); 394 return folder_id_obj; 395 } 396 397 void BookmarksBridge::GetChildIDs(JNIEnv* env, 398 jobject obj, 399 jlong id, 400 jint type, 401 jboolean get_folders, 402 jboolean get_bookmarks, 403 jobject j_result_obj) { 404 DCHECK(IsLoaded()); 405 406 const BookmarkNode* parent = GetNodeByID(id, type); 407 if (!parent->is_folder() || !IsReachable(parent)) 408 return; 409 410 // Get the folder contents 411 for (int i = 0; i < parent->child_count(); ++i) { 412 const BookmarkNode* child = parent->GetChild(i); 413 if (!IsFolderAvailable(child) || !IsReachable(child)) 414 continue; 415 416 if ((child->is_folder() && get_folders) || 417 (!child->is_folder() && get_bookmarks)) { 418 Java_BookmarksBridge_addToBookmarkIdList( 419 env, j_result_obj, child->id(), GetBookmarkType(child)); 420 } 421 } 422 423 // Partner bookmark root node is under mobile node. 424 if (parent == bookmark_model_->mobile_node() && get_folders && 425 partner_bookmarks_shim_->HasPartnerBookmarks() && 426 IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) { 427 Java_BookmarksBridge_addToBookmarkIdList( 428 env, 429 j_result_obj, 430 partner_bookmarks_shim_->GetPartnerBookmarksRoot()->id(), 431 BookmarkType::PARTNER); 432 } 433 } 434 435 void BookmarksBridge::GetAllBookmarkIDsOrderedByCreationDate( 436 JNIEnv* env, 437 jobject obj, 438 jobject j_result_obj) { 439 DCHECK(IsLoaded()); 440 std::list<const BookmarkNode*> folders; 441 std::vector<const BookmarkNode*> result; 442 folders.push_back(bookmark_model_->root_node()); 443 folders.push_back(partner_bookmarks_shim_->GetPartnerBookmarksRoot()); 444 445 for (std::list<const BookmarkNode*>::iterator folder_iter = folders.begin(); 446 folder_iter != folders.end(); ++folder_iter) { 447 if (*folder_iter == NULL) 448 continue; 449 450 std::list<const BookmarkNode*>::iterator insert_iter = folder_iter; 451 ++insert_iter; 452 453 for (int i = 0; i < (*folder_iter)->child_count(); ++i) { 454 const BookmarkNode* child = (*folder_iter)->GetChild(i); 455 if (!IsFolderAvailable(child) || !IsReachable(child)) 456 continue; 457 458 if (child->is_folder()) { 459 insert_iter = folders.insert(insert_iter, child); 460 } else { 461 result.push_back(child); 462 } 463 } 464 } 465 466 std::sort( 467 result.begin(), result.end(), BookmarkNodeCreationTimeCompareFunctor()); 468 469 for (std::vector<const BookmarkNode*>::const_iterator iter = result.begin(); 470 iter != result.end(); 471 ++iter) { 472 const BookmarkNode* bookmark = *iter; 473 Java_BookmarksBridge_addToBookmarkIdList( 474 env, j_result_obj, bookmark->id(), GetBookmarkType(bookmark)); 475 } 476 } 477 478 void BookmarksBridge::SetBookmarkTitle(JNIEnv* env, 479 jobject obj, 480 jlong id, 481 jint type, 482 jstring j_title) { 483 DCHECK(IsLoaded()); 484 const BookmarkNode* bookmark = GetNodeByID(id, type); 485 const base::string16 title = 486 base::android::ConvertJavaStringToUTF16(env, j_title); 487 488 if (partner_bookmarks_shim_->IsPartnerBookmark(bookmark)) { 489 partner_bookmarks_shim_->RenameBookmark(bookmark, title); 490 } else { 491 bookmark_model_->SetTitle(bookmark, title); 492 } 493 } 494 495 void BookmarksBridge::SetBookmarkUrl(JNIEnv* env, 496 jobject obj, 497 jlong id, 498 jint type, 499 jstring url) { 500 DCHECK(IsLoaded()); 501 bookmark_model_->SetURL( 502 GetNodeByID(id, type), 503 GURL(base::android::ConvertJavaStringToUTF16(env, url))); 504 } 505 506 bool BookmarksBridge::DoesBookmarkExist(JNIEnv* env, 507 jobject obj, 508 jlong id, 509 jint type) { 510 DCHECK(IsLoaded()); 511 return GetNodeByID(id, type); 512 } 513 514 void BookmarksBridge::GetBookmarksForFolder(JNIEnv* env, 515 jobject obj, 516 jobject j_folder_id_obj, 517 jobject j_callback_obj, 518 jobject j_result_obj) { 519 DCHECK(IsLoaded()); 520 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj); 521 int type = JavaBookmarkIdGetType(env, j_folder_id_obj); 522 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type); 523 524 if (!folder->is_folder() || !IsReachable(folder)) 525 return; 526 527 // Recreate the java bookmarkId object due to fallback. 528 ScopedJavaLocalRef<jobject> folder_id_obj = 529 Java_BookmarksBridge_createBookmarkId( 530 env, folder->id(), GetBookmarkType(folder)); 531 j_folder_id_obj = folder_id_obj.obj(); 532 533 // Get the folder contents. 534 for (int i = 0; i < folder->child_count(); ++i) { 535 const BookmarkNode* node = folder->GetChild(i); 536 if (!IsFolderAvailable(node)) 537 continue; 538 ExtractBookmarkNodeInformation(node, j_result_obj); 539 } 540 541 if (folder == bookmark_model_->mobile_node() && 542 partner_bookmarks_shim_->HasPartnerBookmarks()) { 543 ExtractBookmarkNodeInformation( 544 partner_bookmarks_shim_->GetPartnerBookmarksRoot(), 545 j_result_obj); 546 } 547 548 if (j_callback_obj) { 549 Java_BookmarksCallback_onBookmarksAvailable( 550 env, j_callback_obj, j_folder_id_obj, j_result_obj); 551 } 552 } 553 554 void BookmarksBridge::GetCurrentFolderHierarchy(JNIEnv* env, 555 jobject obj, 556 jobject j_folder_id_obj, 557 jobject j_callback_obj, 558 jobject j_result_obj) { 559 DCHECK(IsLoaded()); 560 long folder_id = JavaBookmarkIdGetId(env, j_folder_id_obj); 561 int type = JavaBookmarkIdGetType(env, j_folder_id_obj); 562 const BookmarkNode* folder = GetFolderWithFallback(folder_id, type); 563 564 if (!folder->is_folder() || !IsReachable(folder)) 565 return; 566 567 // Recreate the java bookmarkId object due to fallback. 568 ScopedJavaLocalRef<jobject> folder_id_obj = 569 Java_BookmarksBridge_createBookmarkId( 570 env, folder->id(), GetBookmarkType(folder)); 571 j_folder_id_obj = folder_id_obj.obj(); 572 573 // Get the folder hierarchy. 574 const BookmarkNode* node = folder; 575 while (node) { 576 ExtractBookmarkNodeInformation(node, j_result_obj); 577 node = GetParentNode(node); 578 } 579 580 Java_BookmarksCallback_onBookmarksFolderHierarchyAvailable( 581 env, j_callback_obj, j_folder_id_obj, j_result_obj); 582 } 583 584 ScopedJavaLocalRef<jobject> BookmarksBridge::AddFolder(JNIEnv* env, 585 jobject obj, 586 jobject j_parent_id_obj, 587 jint index, 588 jstring j_title) { 589 DCHECK(IsLoaded()); 590 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj); 591 int type = JavaBookmarkIdGetType(env, j_parent_id_obj); 592 const BookmarkNode* parent = GetNodeByID(bookmark_id, type); 593 594 const BookmarkNode* new_node = bookmark_model_->AddFolder( 595 parent, index, base::android::ConvertJavaStringToUTF16(env, j_title)); 596 if (!new_node) { 597 NOTREACHED(); 598 return ScopedJavaLocalRef<jobject>(); 599 } 600 ScopedJavaLocalRef<jobject> new_java_obj = 601 Java_BookmarksBridge_createBookmarkId( 602 env, new_node->id(), GetBookmarkType(new_node)); 603 return new_java_obj; 604 } 605 606 void BookmarksBridge::DeleteBookmark(JNIEnv* env, 607 jobject obj, 608 jobject j_bookmark_id_obj) { 609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 610 DCHECK(IsLoaded()); 611 612 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj); 613 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj); 614 const BookmarkNode* node = GetNodeByID(bookmark_id, type); 615 if (!IsEditable(node)) { 616 NOTREACHED(); 617 return; 618 } 619 620 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) { 621 partner_bookmarks_shim_->RemoveBookmark(node); 622 } else { 623 const BookmarkNode* parent_node = GetParentNode(node); 624 bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node)); 625 } 626 } 627 628 void BookmarksBridge::MoveBookmark(JNIEnv* env, 629 jobject obj, 630 jobject j_bookmark_id_obj, 631 jobject j_parent_id_obj, 632 jint index) { 633 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 634 DCHECK(IsLoaded()); 635 636 long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj); 637 int type = JavaBookmarkIdGetType(env, j_bookmark_id_obj); 638 const BookmarkNode* node = GetNodeByID(bookmark_id, type); 639 if (!IsEditable(node)) { 640 NOTREACHED(); 641 return; 642 } 643 bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj); 644 type = JavaBookmarkIdGetType(env, j_parent_id_obj); 645 const BookmarkNode* new_parent_node = GetNodeByID(bookmark_id, type); 646 bookmark_model_->Move(node, new_parent_node, index); 647 } 648 649 ScopedJavaLocalRef<jobject> BookmarksBridge::AddBookmark( 650 JNIEnv* env, 651 jobject obj, 652 jobject j_parent_id_obj, 653 jint index, 654 jstring j_title, 655 jstring j_url) { 656 DCHECK(IsLoaded()); 657 long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj); 658 int type = JavaBookmarkIdGetType(env, j_parent_id_obj); 659 const BookmarkNode* parent = GetNodeByID(bookmark_id, type); 660 661 const BookmarkNode* new_node = bookmark_model_->AddURL( 662 parent, 663 index, 664 base::android::ConvertJavaStringToUTF16(env, j_title), 665 GURL(base::android::ConvertJavaStringToUTF16(env, j_url))); 666 if (!new_node) { 667 NOTREACHED(); 668 return ScopedJavaLocalRef<jobject>(); 669 } 670 ScopedJavaLocalRef<jobject> new_java_obj = 671 Java_BookmarksBridge_createBookmarkId( 672 env, new_node->id(), GetBookmarkType(new_node)); 673 return new_java_obj; 674 } 675 676 void BookmarksBridge::Undo(JNIEnv* env, jobject obj) { 677 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 678 DCHECK(IsLoaded()); 679 BookmarkUndoService* undo_service = 680 BookmarkUndoServiceFactory::GetForProfile(profile_); 681 UndoManager* undo_manager = undo_service->undo_manager(); 682 undo_manager->Undo(); 683 } 684 685 void BookmarksBridge::StartGroupingUndos(JNIEnv* env, jobject obj) { 686 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 687 DCHECK(IsLoaded()); 688 DCHECK(!grouped_bookmark_actions_.get()); // shouldn't have started already 689 grouped_bookmark_actions_.reset( 690 new bookmarks::ScopedGroupBookmarkActions(bookmark_model_)); 691 } 692 693 void BookmarksBridge::EndGroupingUndos(JNIEnv* env, jobject obj) { 694 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 695 DCHECK(IsLoaded()); 696 DCHECK(grouped_bookmark_actions_.get()); // should only call after start 697 grouped_bookmark_actions_.reset(); 698 } 699 700 ScopedJavaLocalRef<jobject> BookmarksBridge::CreateJavaBookmark( 701 const BookmarkNode* node) { 702 JNIEnv* env = AttachCurrentThread(); 703 704 const BookmarkNode* parent = GetParentNode(node); 705 int64 parent_id = parent ? parent->id() : -1; 706 707 std::string url; 708 if (node->is_url()) 709 url = node->url().spec(); 710 711 return Java_BookmarksBridge_createBookmarkItem( 712 env, 713 node->id(), 714 GetBookmarkType(node), 715 ConvertUTF16ToJavaString(env, GetTitle(node)).obj(), 716 ConvertUTF8ToJavaString(env, url).obj(), 717 node->is_folder(), 718 parent_id, 719 GetBookmarkType(parent), 720 IsEditable(node), 721 IsManaged(node)); 722 } 723 724 void BookmarksBridge::ExtractBookmarkNodeInformation(const BookmarkNode* node, 725 jobject j_result_obj) { 726 JNIEnv* env = AttachCurrentThread(); 727 if (!IsReachable(node)) 728 return; 729 Java_BookmarksBridge_addToList( 730 env, j_result_obj, CreateJavaBookmark(node).obj()); 731 } 732 733 const BookmarkNode* BookmarksBridge::GetNodeByID(long node_id, int type) { 734 const BookmarkNode* node; 735 if (type == BookmarkType::PARTNER) { 736 node = partner_bookmarks_shim_->GetNodeByID( 737 static_cast<int64>(node_id)); 738 } else { 739 node = bookmarks::GetBookmarkNodeByID(bookmark_model_, 740 static_cast<int64>(node_id)); 741 } 742 return node; 743 } 744 745 const BookmarkNode* BookmarksBridge::GetFolderWithFallback(long folder_id, 746 int type) { 747 const BookmarkNode* folder = GetNodeByID(folder_id, type); 748 if (!folder || folder->type() == BookmarkNode::URL || 749 !IsFolderAvailable(folder)) { 750 if (!client_->managed_node()->empty()) 751 folder = client_->managed_node(); 752 else 753 folder = bookmark_model_->mobile_node(); 754 } 755 return folder; 756 } 757 758 bool BookmarksBridge::IsEditable(const BookmarkNode* node) const { 759 if (!node || (node->type() != BookmarkNode::FOLDER && 760 node->type() != BookmarkNode::URL)) { 761 return false; 762 } 763 if (!IsEditBookmarksEnabled()) 764 return false; 765 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) 766 return partner_bookmarks_shim_->IsEditable(node); 767 return client_->CanBeEditedByUser(node); 768 } 769 770 bool BookmarksBridge::IsManaged(const BookmarkNode* node) const { 771 return client_->IsDescendantOfManagedNode(node); 772 } 773 774 const BookmarkNode* BookmarksBridge::GetParentNode(const BookmarkNode* node) { 775 DCHECK(IsLoaded()); 776 if (node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) { 777 return bookmark_model_->mobile_node(); 778 } else { 779 return node->parent(); 780 } 781 } 782 783 int BookmarksBridge::GetBookmarkType(const BookmarkNode* node) { 784 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) 785 return BookmarkType::PARTNER; 786 else 787 return BookmarkType::NORMAL; 788 } 789 790 base::string16 BookmarksBridge::GetTitle(const BookmarkNode* node) const { 791 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) 792 return partner_bookmarks_shim_->GetTitle(node); 793 return node->GetTitle(); 794 } 795 796 bool BookmarksBridge::IsReachable(const BookmarkNode* node) const { 797 if (!partner_bookmarks_shim_->IsPartnerBookmark(node)) 798 return true; 799 return partner_bookmarks_shim_->IsReachable(node); 800 } 801 802 bool BookmarksBridge::IsLoaded() const { 803 return (bookmark_model_->loaded() && partner_bookmarks_shim_->IsLoaded()); 804 } 805 806 bool BookmarksBridge::IsFolderAvailable( 807 const BookmarkNode* folder) const { 808 // The managed bookmarks folder is not shown if there are no bookmarks 809 // configured via policy. 810 if (folder == client_->managed_node() && folder->empty()) 811 return false; 812 813 SigninManager* signin = SigninManagerFactory::GetForProfile( 814 profile_->GetOriginalProfile()); 815 return (folder->type() != BookmarkNode::BOOKMARK_BAR && 816 folder->type() != BookmarkNode::OTHER_NODE) || 817 (signin && signin->IsAuthenticated()); 818 } 819 820 void BookmarksBridge::NotifyIfDoneLoading() { 821 if (!IsLoaded()) 822 return; 823 JNIEnv* env = AttachCurrentThread(); 824 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 825 if (obj.is_null()) 826 return; 827 Java_BookmarksBridge_bookmarkModelLoaded(env, obj.obj()); 828 } 829 830 // ------------- Observer-related methods ------------- // 831 832 void BookmarksBridge::BookmarkModelChanged() { 833 if (!IsLoaded()) 834 return; 835 836 // Called when there are changes to the bookmark model. It is most 837 // likely changes to the partner bookmarks. 838 JNIEnv* env = AttachCurrentThread(); 839 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 840 if (obj.is_null()) 841 return; 842 Java_BookmarksBridge_bookmarkModelChanged(env, obj.obj()); 843 } 844 845 void BookmarksBridge::BookmarkModelLoaded(BookmarkModel* model, 846 bool ids_reassigned) { 847 NotifyIfDoneLoading(); 848 } 849 850 void BookmarksBridge::BookmarkModelBeingDeleted(BookmarkModel* model) { 851 if (!IsLoaded()) 852 return; 853 854 JNIEnv* env = AttachCurrentThread(); 855 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 856 if (obj.is_null()) 857 return; 858 Java_BookmarksBridge_bookmarkModelDeleted(env, obj.obj()); 859 } 860 861 void BookmarksBridge::BookmarkNodeMoved(BookmarkModel* model, 862 const BookmarkNode* old_parent, 863 int old_index, 864 const BookmarkNode* new_parent, 865 int new_index) { 866 if (!IsLoaded()) 867 return; 868 869 JNIEnv* env = AttachCurrentThread(); 870 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 871 if (obj.is_null()) 872 return; 873 Java_BookmarksBridge_bookmarkNodeMoved( 874 env, 875 obj.obj(), 876 CreateJavaBookmark(old_parent).obj(), 877 old_index, 878 CreateJavaBookmark(new_parent).obj(), 879 new_index); 880 } 881 882 void BookmarksBridge::BookmarkNodeAdded(BookmarkModel* model, 883 const BookmarkNode* parent, 884 int index) { 885 if (!IsLoaded()) 886 return; 887 888 JNIEnv* env = AttachCurrentThread(); 889 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 890 if (obj.is_null()) 891 return; 892 Java_BookmarksBridge_bookmarkNodeAdded( 893 env, 894 obj.obj(), 895 CreateJavaBookmark(parent).obj(), 896 index); 897 } 898 899 void BookmarksBridge::BookmarkNodeRemoved(BookmarkModel* model, 900 const BookmarkNode* parent, 901 int old_index, 902 const BookmarkNode* node, 903 const std::set<GURL>& removed_urls) { 904 if (!IsLoaded()) 905 return; 906 907 JNIEnv* env = AttachCurrentThread(); 908 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 909 if (obj.is_null()) 910 return; 911 Java_BookmarksBridge_bookmarkNodeRemoved( 912 env, 913 obj.obj(), 914 CreateJavaBookmark(parent).obj(), 915 old_index, 916 CreateJavaBookmark(node).obj()); 917 } 918 919 void BookmarksBridge::BookmarkNodeChanged(BookmarkModel* model, 920 const BookmarkNode* node) { 921 if (!IsLoaded()) 922 return; 923 924 JNIEnv* env = AttachCurrentThread(); 925 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 926 if (obj.is_null()) 927 return; 928 Java_BookmarksBridge_bookmarkNodeChanged( 929 env, 930 obj.obj(), 931 CreateJavaBookmark(node).obj()); 932 } 933 934 void BookmarksBridge::BookmarkNodeChildrenReordered(BookmarkModel* model, 935 const BookmarkNode* node) { 936 if (!IsLoaded()) 937 return; 938 939 JNIEnv* env = AttachCurrentThread(); 940 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 941 if (obj.is_null()) 942 return; 943 Java_BookmarksBridge_bookmarkNodeChildrenReordered( 944 env, 945 obj.obj(), 946 CreateJavaBookmark(node).obj()); 947 } 948 949 void BookmarksBridge::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) { 950 if (!IsLoaded()) 951 return; 952 953 JNIEnv* env = AttachCurrentThread(); 954 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 955 if (obj.is_null()) 956 return; 957 Java_BookmarksBridge_extensiveBookmarkChangesBeginning(env, obj.obj()); 958 } 959 960 void BookmarksBridge::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { 961 if (!IsLoaded()) 962 return; 963 964 JNIEnv* env = AttachCurrentThread(); 965 ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env); 966 if (obj.is_null()) 967 return; 968 Java_BookmarksBridge_extensiveBookmarkChangesEnded(env, obj.obj()); 969 } 970 971 void BookmarksBridge::PartnerShimChanged(PartnerBookmarksShim* shim) { 972 if (!IsLoaded()) 973 return; 974 975 BookmarkModelChanged(); 976 } 977 978 void BookmarksBridge::PartnerShimLoaded(PartnerBookmarksShim* shim) { 979 NotifyIfDoneLoading(); 980 } 981 982 void BookmarksBridge::ShimBeingDeleted(PartnerBookmarksShim* shim) { 983 partner_bookmarks_shim_ = NULL; 984 } 985