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