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