Home | History | Annotate | Download | only in bookmarks
      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