1 // Copyright (c) 2012 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/provider/chrome_browser_provider.h" 6 7 #include <list> 8 #include <utility> 9 10 #include "base/android/jni_android.h" 11 #include "base/android/jni_array.h" 12 #include "base/android/jni_string.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted_memory.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/task/cancelable_task_tracker.h" 17 #include "base/time/time.h" 18 #include "chrome/browser/android/provider/blocking_ui_thread_async_request.h" 19 #include "chrome/browser/android/provider/bookmark_model_observer_task.h" 20 #include "chrome/browser/android/provider/run_on_ui_thread_blocking.h" 21 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 22 #include "chrome/browser/bookmarks/chrome_bookmark_client.h" 23 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h" 24 #include "chrome/browser/browser_process.h" 25 #include "chrome/browser/chrome_notification_types.h" 26 #include "chrome/browser/favicon/favicon_service.h" 27 #include "chrome/browser/favicon/favicon_service_factory.h" 28 #include "chrome/browser/history/android/android_history_types.h" 29 #include "chrome/browser/history/android/sqlite_cursor.h" 30 #include "chrome/browser/history/top_sites.h" 31 #include "chrome/browser/profiles/profile.h" 32 #include "chrome/browser/profiles/profile_manager.h" 33 #include "chrome/browser/search_engines/template_url.h" 34 #include "chrome/browser/search_engines/template_url_service.h" 35 #include "chrome/browser/search_engines/template_url_service_factory.h" 36 #include "components/bookmarks/browser/bookmark_model.h" 37 #include "components/bookmarks/browser/bookmark_utils.h" 38 #include "content/public/browser/browser_thread.h" 39 #include "content/public/browser/notification_service.h" 40 #include "grit/generated_resources.h" 41 #include "jni/ChromeBrowserProvider_jni.h" 42 #include "sql/statement.h" 43 #include "ui/base/l10n/l10n_util.h" 44 #include "ui/base/layout.h" 45 #include "ui/base/resource/resource_bundle.h" 46 #include "ui/gfx/favicon_size.h" 47 48 using base::android::AttachCurrentThread; 49 using base::android::CheckException; 50 using base::android::ClearException; 51 using base::android::ConvertJavaStringToUTF16; 52 using base::android::ConvertJavaStringToUTF8; 53 using base::android::ConvertUTF8ToJavaString; 54 using base::android::ConvertUTF16ToJavaString; 55 using base::android::GetClass; 56 using base::android::MethodID; 57 using base::android::JavaRef; 58 using base::android::ScopedJavaGlobalRef; 59 using base::android::ScopedJavaLocalRef; 60 using content::BrowserThread; 61 62 // After refactoring the following class hierarchy has been created in order 63 // to avoid repeating code again for the same basic kind of tasks, to enforce 64 // the correct thread usage and to prevent known race conditions and deadlocks. 65 // 66 // - RunOnUIThreadBlocking: auxiliary class to run methods in the UI thread 67 // blocking the current one until finished. Because of the provider threading 68 // expectations this cannot be used from the UI thread. 69 // 70 // - BookmarkModelTask: base class for all tasks that operate in any way with 71 // the bookmark model. This class ensures that the model is loaded and 72 // prevents possible deadlocks. Derived classes should make use of 73 // RunOnUIThreadBlocking to perform any manipulation of the bookmark model in 74 // the UI thread. The Run method of these tasks cannot be invoked directly 75 // from the UI thread, but RunOnUIThread can be safely used from the UI 76 // thread code of other BookmarkModelTasks. 77 // 78 // - AsyncServiceRequest: base class for any asynchronous requests made to a 79 // Chromium service that require to block the current thread until completed. 80 // Derived classes should make use of RunAsyncRequestOnUIThreadBlocking to 81 // post their requests in the UI thread and return the results synchronously. 82 // All derived classes MUST ALWAYS call RequestCompleted when receiving the 83 // request response. These tasks cannot be invoked from the UI thread. 84 // 85 // - FaviconServiceTask: base class for asynchronous requests that make use of 86 // Chromium's favicon service. See AsyncServiceRequest for more details. 87 // 88 // - HistoryProviderTask: base class for asynchronous requests that make use of 89 // AndroidHistoryProviderService. See AsyncServiceRequest for mode details. 90 // 91 // - SearchTermTask: base class for asynchronous requests that involve the 92 // search term API. Works in the same way as HistoryProviderTask. 93 94 namespace { 95 96 const char kDefaultUrlScheme[] = "http://"; 97 const int64 kInvalidContentProviderId = 0; 98 const int64 kInvalidBookmarkId = -1; 99 100 // ------------- Java-related utility methods ------------- // 101 102 // Convert a BookmarkNode, |node|, to the java representation of a bookmark node 103 // stored in |*jnode|. Parent node information is optional. 104 void ConvertBookmarkNode( 105 const BookmarkNode* node, 106 const JavaRef<jobject>& parent_node, 107 ScopedJavaGlobalRef<jobject>* jnode) { 108 DCHECK(jnode); 109 if (!node) 110 return; 111 112 JNIEnv* env = AttachCurrentThread(); 113 ScopedJavaLocalRef<jstring> url; 114 if (node->is_url()) 115 url.Reset(ConvertUTF8ToJavaString(env, node->url().spec())); 116 ScopedJavaLocalRef<jstring> title( 117 ConvertUTF16ToJavaString(env, node->GetTitle())); 118 119 jnode->Reset( 120 Java_BookmarkNode_create( 121 env, node->id(), (jint) node->type(), title.obj(), url.obj(), 122 parent_node.obj())); 123 } 124 125 jlong ConvertJLongObjectToPrimitive(JNIEnv* env, jobject long_obj) { 126 ScopedJavaLocalRef<jclass> jlong_clazz = GetClass(env, "java/lang/Long"); 127 jmethodID long_value = MethodID::Get<MethodID::TYPE_INSTANCE>( 128 env, jlong_clazz.obj(), "longValue", "()J"); 129 return env->CallLongMethod(long_obj, long_value, NULL); 130 } 131 132 jboolean ConvertJBooleanObjectToPrimitive(JNIEnv* env, jobject boolean_object) { 133 ScopedJavaLocalRef<jclass> jboolean_clazz = 134 GetClass(env, "java/lang/Boolean"); 135 jmethodID boolean_value = MethodID::Get<MethodID::TYPE_INSTANCE>( 136 env, jboolean_clazz.obj(), "booleanValue", "()Z"); 137 return env->CallBooleanMethod(boolean_object, boolean_value, NULL); 138 } 139 140 base::Time ConvertJlongToTime(jlong value) { 141 return base::Time::UnixEpoch() + 142 base::TimeDelta::FromMilliseconds((int64)value); 143 } 144 145 jint ConvertJIntegerToJint(JNIEnv* env, jobject integer_obj) { 146 ScopedJavaLocalRef<jclass> jinteger_clazz = 147 GetClass(env, "java/lang/Integer"); 148 jmethodID int_value = MethodID::Get<MethodID::TYPE_INSTANCE>( 149 env, jinteger_clazz.obj(), "intValue", "()I"); 150 return env->CallIntMethod(integer_obj, int_value, NULL); 151 } 152 153 std::vector<base::string16> ConvertJStringArrayToString16Array( 154 JNIEnv* env, 155 jobjectArray array) { 156 std::vector<base::string16> results; 157 if (array) { 158 jsize len = env->GetArrayLength(array); 159 for (int i = 0; i < len; i++) { 160 results.push_back(ConvertJavaStringToUTF16(env, 161 static_cast<jstring>(env->GetObjectArrayElement(array, i)))); 162 } 163 } 164 return results; 165 } 166 167 // ------------- Utility methods used by tasks ------------- // 168 169 // Parse the given url and return a GURL, appending the default scheme 170 // if one is not present. 171 GURL ParseAndMaybeAppendScheme(const base::string16& url, 172 const char* default_scheme) { 173 GURL gurl(url); 174 if (!gurl.is_valid() && !gurl.has_scheme()) { 175 base::string16 refined_url(base::ASCIIToUTF16(default_scheme)); 176 refined_url.append(url); 177 gurl = GURL(refined_url); 178 } 179 return gurl; 180 } 181 182 const BookmarkNode* GetChildFolderByTitle(const BookmarkNode* parent, 183 const base::string16& title) { 184 for (int i = 0; i < parent->child_count(); ++i) { 185 if (parent->GetChild(i)->is_folder() && 186 parent->GetChild(i)->GetTitle() == title) { 187 return parent->GetChild(i); 188 } 189 } 190 return NULL; 191 } 192 193 // ------------- Synchronous task classes ------------- // 194 195 // Utility task to add a bookmark. 196 class AddBookmarkTask : public BookmarkModelTask { 197 public: 198 explicit AddBookmarkTask(BookmarkModel* model) : BookmarkModelTask(model) {} 199 200 int64 Run(const base::string16& title, 201 const base::string16& url, 202 const bool is_folder, 203 const int64 parent_id) { 204 int64 result = kInvalidBookmarkId; 205 RunOnUIThreadBlocking::Run( 206 base::Bind(&AddBookmarkTask::RunOnUIThread, 207 model(), title, url, is_folder, parent_id, &result)); 208 return result; 209 } 210 211 static void RunOnUIThread(BookmarkModel* model, 212 const base::string16& title, 213 const base::string16& url, 214 const bool is_folder, 215 const int64 parent_id, 216 int64* result) { 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 218 DCHECK(result); 219 GURL gurl = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme); 220 221 // Check if the bookmark already exists. 222 const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(gurl); 223 if (!node) { 224 const BookmarkNode* parent_node = NULL; 225 if (parent_id >= 0) 226 parent_node = GetBookmarkNodeByID(model, parent_id); 227 if (!parent_node) 228 parent_node = model->bookmark_bar_node(); 229 230 if (is_folder) 231 node = model->AddFolder(parent_node, parent_node->child_count(), title); 232 else 233 node = model->AddURL(parent_node, 0, title, gurl); 234 } 235 236 *result = node ? node ->id() : kInvalidBookmarkId; 237 } 238 239 private: 240 DISALLOW_COPY_AND_ASSIGN(AddBookmarkTask); 241 }; 242 243 // Utility method to remove a bookmark. 244 class RemoveBookmarkTask : public BookmarkModelObserverTask { 245 public: 246 explicit RemoveBookmarkTask(BookmarkModel* model) 247 : BookmarkModelObserverTask(model), 248 deleted_(0), 249 id_to_delete_(kInvalidBookmarkId) {} 250 virtual ~RemoveBookmarkTask() {} 251 252 int Run(const int64 id) { 253 id_to_delete_ = id; 254 RunOnUIThreadBlocking::Run( 255 base::Bind(&RemoveBookmarkTask::RunOnUIThread, model(), id)); 256 return deleted_; 257 } 258 259 static void RunOnUIThread(BookmarkModel* model, const int64 id) { 260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 261 const BookmarkNode* node = GetBookmarkNodeByID(model, id); 262 if (node && node->parent()) { 263 const BookmarkNode* parent_node = node->parent(); 264 model->Remove(parent_node, parent_node->GetIndexOf(node)); 265 } 266 } 267 268 // Verify that the bookmark was actually removed. Called synchronously. 269 virtual void BookmarkNodeRemoved( 270 BookmarkModel* bookmark_model, 271 const BookmarkNode* parent, 272 int old_index, 273 const BookmarkNode* node, 274 const std::set<GURL>& removed_urls) OVERRIDE { 275 if (bookmark_model == model() && node->id() == id_to_delete_) 276 ++deleted_; 277 } 278 279 private: 280 int deleted_; 281 int64 id_to_delete_; 282 283 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarkTask); 284 }; 285 286 // Utility method to remove all bookmarks that the user can edit. 287 class RemoveAllUserBookmarksTask : public BookmarkModelObserverTask { 288 public: 289 explicit RemoveAllUserBookmarksTask(BookmarkModel* model) 290 : BookmarkModelObserverTask(model) {} 291 292 virtual ~RemoveAllUserBookmarksTask() {} 293 294 void Run() { 295 RunOnUIThreadBlocking::Run( 296 base::Bind(&RemoveAllUserBookmarksTask::RunOnUIThread, model())); 297 } 298 299 static void RunOnUIThread(BookmarkModel* model) { 300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 301 model->RemoveAllUserBookmarks(); 302 } 303 304 private: 305 DISALLOW_COPY_AND_ASSIGN(RemoveAllUserBookmarksTask); 306 }; 307 308 // Utility method to update a bookmark. 309 class UpdateBookmarkTask : public BookmarkModelObserverTask { 310 public: 311 explicit UpdateBookmarkTask(BookmarkModel* model) 312 : BookmarkModelObserverTask(model), 313 updated_(0), 314 id_to_update_(kInvalidBookmarkId){} 315 virtual ~UpdateBookmarkTask() {} 316 317 int Run(const int64 id, 318 const base::string16& title, 319 const base::string16& url, 320 const int64 parent_id) { 321 id_to_update_ = id; 322 RunOnUIThreadBlocking::Run( 323 base::Bind(&UpdateBookmarkTask::RunOnUIThread, 324 model(), id, title, url, parent_id)); 325 return updated_; 326 } 327 328 static void RunOnUIThread(BookmarkModel* model, 329 const int64 id, 330 const base::string16& title, 331 const base::string16& url, 332 const int64 parent_id) { 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 334 const BookmarkNode* node = GetBookmarkNodeByID(model, id); 335 if (node) { 336 if (node->GetTitle() != title) 337 model->SetTitle(node, title); 338 339 if (node->type() == BookmarkNode::URL) { 340 GURL bookmark_url = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme); 341 if (bookmark_url != node->url()) 342 model->SetURL(node, bookmark_url); 343 } 344 345 if (parent_id >= 0 && 346 (!node->parent() || parent_id != node->parent()->id())) { 347 const BookmarkNode* new_parent = GetBookmarkNodeByID(model, parent_id); 348 349 if (new_parent) 350 model->Move(node, new_parent, 0); 351 } 352 } 353 } 354 355 // Verify that the bookmark was actually updated. Called synchronously. 356 virtual void BookmarkNodeChanged(BookmarkModel* bookmark_model, 357 const BookmarkNode* node) OVERRIDE { 358 if (bookmark_model == model() && node->id() == id_to_update_) 359 ++updated_; 360 } 361 362 private: 363 int updated_; 364 int64 id_to_update_; 365 366 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarkTask); 367 }; 368 369 // Checks if a node exists in the bookmark model. 370 class BookmarkNodeExistsTask : public BookmarkModelTask { 371 public: 372 explicit BookmarkNodeExistsTask(BookmarkModel* model) 373 : BookmarkModelTask(model) { 374 } 375 376 bool Run(const int64 id) { 377 bool result = false; 378 RunOnUIThreadBlocking::Run( 379 base::Bind(&BookmarkNodeExistsTask::RunOnUIThread, 380 model(), id, &result)); 381 return result; 382 } 383 384 static void RunOnUIThread(BookmarkModel* model, 385 const int64 id, 386 bool* result) { 387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 388 DCHECK(result); 389 *result = GetBookmarkNodeByID(model, id) != NULL; 390 } 391 392 private: 393 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeExistsTask); 394 }; 395 396 // Checks if a node belongs to the Mobile Bookmarks hierarchy branch. 397 class IsInMobileBookmarksBranchTask : public BookmarkModelTask { 398 public: 399 explicit IsInMobileBookmarksBranchTask(BookmarkModel* model) 400 : BookmarkModelTask(model) {} 401 402 bool Run(const int64 id) { 403 bool result = false; 404 RunOnUIThreadBlocking::Run( 405 base::Bind(&IsInMobileBookmarksBranchTask::RunOnUIThread, 406 model(), id, &result)); 407 return result; 408 } 409 410 static void RunOnUIThread(BookmarkModel* model, 411 const int64 id, 412 bool *result) { 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 414 DCHECK(result); 415 const BookmarkNode* node = GetBookmarkNodeByID(model, id); 416 const BookmarkNode* mobile_node = model->mobile_node(); 417 while (node && node != mobile_node) 418 node = node->parent(); 419 420 *result = node == mobile_node; 421 } 422 423 private: 424 DISALLOW_COPY_AND_ASSIGN(IsInMobileBookmarksBranchTask); 425 }; 426 427 // Creates folder or retrieves its id if already exists. 428 // An invalid parent id is assumed to represent the Mobile Bookmarks folder. 429 // Can only be used to create folders inside the Mobile Bookmarks branch. 430 class CreateBookmarksFolderOnceTask : public BookmarkModelTask { 431 public: 432 explicit CreateBookmarksFolderOnceTask(BookmarkModel* model) 433 : BookmarkModelTask(model) {} 434 435 int64 Run(const base::string16& title, const int64 parent_id) { 436 int64 result = kInvalidBookmarkId; 437 RunOnUIThreadBlocking::Run( 438 base::Bind(&CreateBookmarksFolderOnceTask::RunOnUIThread, 439 model(), title, parent_id, &result)); 440 return result; 441 } 442 443 static void RunOnUIThread(BookmarkModel* model, 444 const base::string16& title, 445 const int64 parent_id, 446 int64* result) { 447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 448 DCHECK(result); 449 450 // Invalid ids are assumed to refer to the Mobile Bookmarks folder. 451 const BookmarkNode* parent = parent_id >= 0 ? 452 GetBookmarkNodeByID(model, parent_id) : model->mobile_node(); 453 DCHECK(parent); 454 455 bool in_mobile_bookmarks; 456 IsInMobileBookmarksBranchTask::RunOnUIThread(model, parent->id(), 457 &in_mobile_bookmarks); 458 if (!in_mobile_bookmarks) { 459 // The parent folder must be inside the Mobile Bookmarks folder. 460 *result = kInvalidBookmarkId; 461 return; 462 } 463 464 const BookmarkNode* node = GetChildFolderByTitle(parent, title); 465 if (node) { 466 *result = node->id(); 467 return; 468 } 469 470 AddBookmarkTask::RunOnUIThread(model, title, base::string16(), true, 471 parent->id(), result); 472 } 473 474 private: 475 DISALLOW_COPY_AND_ASSIGN(CreateBookmarksFolderOnceTask); 476 }; 477 478 // Creates a Java BookmarkNode object for a node given its id. 479 class GetEditableBookmarkFoldersTask : public BookmarkModelTask { 480 public: 481 GetEditableBookmarkFoldersTask(ChromeBookmarkClient* client, 482 BookmarkModel* model) 483 : BookmarkModelTask(model), client_(client) {} 484 485 void Run(ScopedJavaGlobalRef<jobject>* jroot) { 486 RunOnUIThreadBlocking::Run( 487 base::Bind(&GetEditableBookmarkFoldersTask::RunOnUIThread, 488 client_, model(), jroot)); 489 } 490 491 static void RunOnUIThread(ChromeBookmarkClient* client, 492 BookmarkModel* model, 493 ScopedJavaGlobalRef<jobject>* jroot) { 494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 495 const BookmarkNode* root = model->root_node(); 496 if (!root || !root->is_folder()) 497 return; 498 499 // The iterative approach is not possible because ScopedGlobalJavaRefs 500 // cannot be copy-constructed, and therefore not used in STL containers. 501 ConvertFolderSubtree(client, AttachCurrentThread(), root, 502 ScopedJavaLocalRef<jobject>(), jroot); 503 } 504 505 private: 506 static void ConvertFolderSubtree(ChromeBookmarkClient* client, 507 JNIEnv* env, 508 const BookmarkNode* node, 509 const JavaRef<jobject>& parent_folder, 510 ScopedJavaGlobalRef<jobject>* jfolder) { 511 DCHECK(node); 512 DCHECK(node->is_folder()); 513 DCHECK(jfolder); 514 515 // Global refs should be used here for thread-safety reasons as this task 516 // might be invoked from a thread other than UI. All refs are scoped. 517 ConvertBookmarkNode(node, parent_folder, jfolder); 518 519 for (int i = 0; i < node->child_count(); ++i) { 520 const BookmarkNode* child = node->GetChild(i); 521 if (child->is_folder() && client->CanBeEditedByUser(child)) { 522 ScopedJavaGlobalRef<jobject> jchild; 523 ConvertFolderSubtree(client, env, child, *jfolder, &jchild); 524 525 Java_BookmarkNode_addChild(env, jfolder->obj(), jchild.obj()); 526 if (ClearException(env)) { 527 LOG(WARNING) << "Java exception while adding child node."; 528 return; 529 } 530 } 531 } 532 } 533 534 ChromeBookmarkClient* client_; 535 536 DISALLOW_COPY_AND_ASSIGN(GetEditableBookmarkFoldersTask); 537 }; 538 539 // Creates a Java BookmarkNode object for a node given its id. 540 class GetBookmarkNodeTask : public BookmarkModelTask { 541 public: 542 explicit GetBookmarkNodeTask(BookmarkModel* model) 543 : BookmarkModelTask(model) { 544 } 545 546 void Run(const int64 id, 547 bool get_parent, 548 bool get_children, 549 ScopedJavaGlobalRef<jobject>* jnode) { 550 return RunOnUIThreadBlocking::Run( 551 base::Bind(&GetBookmarkNodeTask::RunOnUIThread, 552 model(), id, get_parent, get_children, jnode)); 553 } 554 555 static void RunOnUIThread(BookmarkModel* model, 556 const int64 id, 557 bool get_parent, 558 bool get_children, 559 ScopedJavaGlobalRef<jobject>* jnode) { 560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 561 const BookmarkNode* node = GetBookmarkNodeByID(model, id); 562 if (!node || !jnode) 563 return; 564 565 ScopedJavaGlobalRef<jobject> jparent; 566 if (get_parent) { 567 ConvertBookmarkNode(node->parent(), ScopedJavaLocalRef<jobject>(), 568 &jparent); 569 } 570 571 ConvertBookmarkNode(node, jparent, jnode); 572 573 JNIEnv* env = AttachCurrentThread(); 574 if (!jparent.is_null()) { 575 Java_BookmarkNode_addChild(env, jparent.obj(), jnode->obj()); 576 if (ClearException(env)) { 577 LOG(WARNING) << "Java exception while adding child node."; 578 return; 579 } 580 } 581 582 if (get_children) { 583 for (int i = 0; i < node->child_count(); ++i) { 584 ScopedJavaGlobalRef<jobject> jchild; 585 ConvertBookmarkNode(node->GetChild(i), *jnode, &jchild); 586 Java_BookmarkNode_addChild(env, jnode->obj(), jchild.obj()); 587 if (ClearException(env)) { 588 LOG(WARNING) << "Java exception while adding child node."; 589 return; 590 } 591 } 592 } 593 } 594 595 private: 596 DISALLOW_COPY_AND_ASSIGN(GetBookmarkNodeTask); 597 }; 598 599 // Gets the Mobile Bookmarks node. Using this task ensures the correct 600 // initialization of the bookmark model. 601 class GetMobileBookmarksNodeTask : public BookmarkModelTask { 602 public: 603 explicit GetMobileBookmarksNodeTask(BookmarkModel* model) 604 : BookmarkModelTask(model) {} 605 606 const BookmarkNode* Run() { 607 const BookmarkNode* result = NULL; 608 RunOnUIThreadBlocking::Run( 609 base::Bind(&GetMobileBookmarksNodeTask::RunOnUIThread, 610 model(), &result)); 611 return result; 612 } 613 614 static void RunOnUIThread(BookmarkModel* model, const BookmarkNode** result) { 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 616 DCHECK(result); 617 *result = model->mobile_node(); 618 } 619 620 private: 621 DISALLOW_COPY_AND_ASSIGN(GetMobileBookmarksNodeTask); 622 }; 623 624 // ------------- Aynchronous requests classes ------------- // 625 626 // Base class for asynchronous blocking requests to Chromium services. 627 // Service: type of the service to use (e.g. HistoryService, FaviconService). 628 template <typename Service> 629 class AsyncServiceRequest : protected BlockingUIThreadAsyncRequest { 630 public: 631 AsyncServiceRequest(Service* service, 632 CancelableRequestConsumer* cancelable_consumer) 633 : service_(service), cancelable_consumer_(cancelable_consumer) {} 634 635 Service* service() const { return service_; } 636 637 CancelableRequestConsumer* cancelable_consumer() const { 638 return cancelable_consumer_; 639 } 640 641 private: 642 Service* service_; 643 CancelableRequestConsumer* cancelable_consumer_; 644 645 DISALLOW_COPY_AND_ASSIGN(AsyncServiceRequest); 646 }; 647 648 // Base class for all asynchronous blocking tasks that use the favicon service. 649 class FaviconServiceTask : public AsyncServiceRequest<FaviconService> { 650 public: 651 FaviconServiceTask(FaviconService* service, 652 Profile* profile, 653 CancelableRequestConsumer* cancelable_consumer, 654 base::CancelableTaskTracker* cancelable_tracker) 655 : AsyncServiceRequest<FaviconService>(service, cancelable_consumer), 656 profile_(profile), 657 cancelable_tracker_(cancelable_tracker) {} 658 659 Profile* profile() const { return profile_; } 660 base::CancelableTaskTracker* cancelable_tracker() const { 661 return cancelable_tracker_; 662 } 663 664 private: 665 Profile* profile_; 666 base::CancelableTaskTracker* cancelable_tracker_; 667 668 DISALLOW_COPY_AND_ASSIGN(FaviconServiceTask); 669 }; 670 671 // Retrieves the favicon or touch icon for a URL from the FaviconService. 672 class BookmarkIconFetchTask : public FaviconServiceTask { 673 public: 674 BookmarkIconFetchTask(FaviconService* favicon_service, 675 Profile* profile, 676 CancelableRequestConsumer* cancelable_consumer, 677 base::CancelableTaskTracker* cancelable_tracker) 678 : FaviconServiceTask(favicon_service, 679 profile, 680 cancelable_consumer, 681 cancelable_tracker) {} 682 683 favicon_base::FaviconRawBitmapResult Run(const GURL& url) { 684 float max_scale = ui::GetScaleForScaleFactor( 685 ResourceBundle::GetSharedInstance().GetMaxScaleFactor()); 686 RunAsyncRequestOnUIThreadBlocking( 687 base::Bind(&FaviconService::GetRawFaviconForPageURL, 688 base::Unretained(service()), 689 FaviconService::FaviconForPageURLParams( 690 url, 691 favicon_base::FAVICON | favicon_base::TOUCH_ICON, 692 gfx::kFaviconSize), 693 max_scale, 694 base::Bind(&BookmarkIconFetchTask::OnFaviconRetrieved, 695 base::Unretained(this)), 696 cancelable_tracker())); 697 return result_; 698 } 699 700 private: 701 void OnFaviconRetrieved( 702 const favicon_base::FaviconRawBitmapResult& bitmap_result) { 703 result_ = bitmap_result; 704 RequestCompleted(); 705 } 706 707 favicon_base::FaviconRawBitmapResult result_; 708 709 DISALLOW_COPY_AND_ASSIGN(BookmarkIconFetchTask); 710 }; 711 712 // Base class for all asynchronous blocking tasks that use the Android history 713 // provider service. 714 class HistoryProviderTask 715 : public AsyncServiceRequest<AndroidHistoryProviderService> { 716 public: 717 HistoryProviderTask(AndroidHistoryProviderService* service, 718 CancelableRequestConsumer* cancelable_consumer) 719 : AsyncServiceRequest<AndroidHistoryProviderService> 720 (service, cancelable_consumer) {} 721 722 private: 723 DISALLOW_COPY_AND_ASSIGN(HistoryProviderTask); 724 }; 725 726 // Adds a bookmark from the API. 727 class AddBookmarkFromAPITask : public HistoryProviderTask { 728 public: 729 AddBookmarkFromAPITask(AndroidHistoryProviderService* service, 730 CancelableRequestConsumer* cancelable_consumer) 731 : HistoryProviderTask(service, cancelable_consumer) {} 732 733 history::URLID Run(const history::HistoryAndBookmarkRow& row) { 734 RunAsyncRequestOnUIThreadBlocking( 735 base::Bind(&AndroidHistoryProviderService::InsertHistoryAndBookmark, 736 base::Unretained(service()), row, cancelable_consumer(), 737 base::Bind(&AddBookmarkFromAPITask::OnBookmarkInserted, 738 base::Unretained(this)))); 739 return result_; 740 } 741 742 private: 743 void OnBookmarkInserted(AndroidHistoryProviderService::Handle handle, 744 bool succeeded, 745 history::URLID id) { 746 // Note that here 0 means an invalid id too. 747 // This is because it represents a SQLite database row id. 748 result_ = id; 749 RequestCompleted(); 750 } 751 752 history::URLID result_; 753 754 DISALLOW_COPY_AND_ASSIGN(AddBookmarkFromAPITask); 755 }; 756 757 // Queries bookmarks from the API. 758 class QueryBookmarksFromAPITask : public HistoryProviderTask { 759 public: 760 QueryBookmarksFromAPITask(AndroidHistoryProviderService* service, 761 CancelableRequestConsumer* cancelable_consumer) 762 : HistoryProviderTask(service, cancelable_consumer), 763 result_(NULL) {} 764 765 history::AndroidStatement* Run( 766 const std::vector<history::HistoryAndBookmarkRow::ColumnID>& projections, 767 const std::string& selection, 768 const std::vector<base::string16>& selection_args, 769 const std::string& sort_order) { 770 RunAsyncRequestOnUIThreadBlocking( 771 base::Bind(&AndroidHistoryProviderService::QueryHistoryAndBookmarks, 772 base::Unretained(service()), projections, selection, 773 selection_args, sort_order, cancelable_consumer(), 774 base::Bind(&QueryBookmarksFromAPITask::OnBookmarksQueried, 775 base::Unretained(this)))); 776 return result_; 777 } 778 779 private: 780 void OnBookmarksQueried(AndroidHistoryProviderService::Handle handle, 781 bool succeeded, 782 history::AndroidStatement* statement) { 783 result_ = statement; 784 RequestCompleted(); 785 } 786 787 history::AndroidStatement* result_; 788 789 DISALLOW_COPY_AND_ASSIGN(QueryBookmarksFromAPITask); 790 }; 791 792 // Updates bookmarks from the API. 793 class UpdateBookmarksFromAPITask : public HistoryProviderTask { 794 public: 795 UpdateBookmarksFromAPITask(AndroidHistoryProviderService* service, 796 CancelableRequestConsumer* cancelable_consumer) 797 : HistoryProviderTask(service, cancelable_consumer), 798 result_(0) {} 799 800 int Run(const history::HistoryAndBookmarkRow& row, 801 const std::string& selection, 802 const std::vector<base::string16>& selection_args) { 803 RunAsyncRequestOnUIThreadBlocking( 804 base::Bind(&AndroidHistoryProviderService::UpdateHistoryAndBookmarks, 805 base::Unretained(service()), row, selection, 806 selection_args, cancelable_consumer(), 807 base::Bind(&UpdateBookmarksFromAPITask::OnBookmarksUpdated, 808 base::Unretained(this)))); 809 return result_; 810 } 811 812 private: 813 void OnBookmarksUpdated(AndroidHistoryProviderService::Handle handle, 814 bool succeeded, 815 int updated_row_count) { 816 result_ = updated_row_count; 817 RequestCompleted(); 818 } 819 820 int result_; 821 822 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarksFromAPITask); 823 }; 824 825 // Removes bookmarks from the API. 826 class RemoveBookmarksFromAPITask : public HistoryProviderTask { 827 public: 828 RemoveBookmarksFromAPITask(AndroidHistoryProviderService* service, 829 CancelableRequestConsumer* cancelable_consumer) 830 : HistoryProviderTask(service, cancelable_consumer), 831 result_(0) {} 832 833 int Run(const std::string& selection, 834 const std::vector<base::string16>& selection_args) { 835 RunAsyncRequestOnUIThreadBlocking( 836 base::Bind(&AndroidHistoryProviderService::DeleteHistoryAndBookmarks, 837 base::Unretained(service()), selection, selection_args, 838 cancelable_consumer(), 839 base::Bind(&RemoveBookmarksFromAPITask::OnBookmarksRemoved, 840 base::Unretained(this)))); 841 return result_; 842 } 843 844 private: 845 void OnBookmarksRemoved(AndroidHistoryProviderService::Handle handle, 846 bool succeeded, 847 int removed_row_count) { 848 result_ = removed_row_count; 849 RequestCompleted(); 850 } 851 852 int result_; 853 854 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarksFromAPITask); 855 }; 856 857 // Removes history from the API. 858 class RemoveHistoryFromAPITask : public HistoryProviderTask { 859 public: 860 RemoveHistoryFromAPITask(AndroidHistoryProviderService* service, 861 CancelableRequestConsumer* cancelable_consumer) 862 : HistoryProviderTask(service, cancelable_consumer), 863 result_(0) {} 864 865 int Run(const std::string& selection, 866 const std::vector<base::string16>& selection_args) { 867 RunAsyncRequestOnUIThreadBlocking( 868 base::Bind(&AndroidHistoryProviderService::DeleteHistory, 869 base::Unretained(service()), selection, 870 selection_args, cancelable_consumer(), 871 base::Bind(&RemoveHistoryFromAPITask::OnHistoryRemoved, 872 base::Unretained(this)))); 873 return result_; 874 } 875 876 private: 877 void OnHistoryRemoved(AndroidHistoryProviderService::Handle handle, 878 bool succeeded, 879 int removed_row_count) { 880 result_ = removed_row_count; 881 RequestCompleted(); 882 } 883 884 int result_; 885 886 DISALLOW_COPY_AND_ASSIGN(RemoveHistoryFromAPITask); 887 }; 888 889 // This class provides the common method for the SearchTermAPIHelper. 890 class SearchTermTask : public HistoryProviderTask { 891 protected: 892 SearchTermTask(AndroidHistoryProviderService* service, 893 CancelableRequestConsumer* cancelable_consumer, 894 Profile* profile) 895 : HistoryProviderTask(service, cancelable_consumer), 896 profile_(profile) {} 897 898 // Fill SearchRow's template_url_id and url fields according the given 899 // search_term. Return true if succeeded. 900 void BuildSearchRow(history::SearchRow* row) { 901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 902 903 TemplateURLService* template_service = 904 TemplateURLServiceFactory::GetForProfile(profile_); 905 template_service->Load(); 906 907 const TemplateURL* search_engine = 908 template_service->GetDefaultSearchProvider(); 909 if (search_engine) { 910 const TemplateURLRef* search_url = &search_engine->url_ref(); 911 TemplateURLRef::SearchTermsArgs search_terms_args(row->search_term()); 912 search_terms_args.append_extra_query_params = true; 913 std::string url = search_url->ReplaceSearchTerms( 914 search_terms_args, template_service->search_terms_data()); 915 if (!url.empty()) { 916 row->set_url(GURL(url)); 917 row->set_template_url_id(search_engine->id()); 918 } 919 } 920 } 921 922 private: 923 Profile* profile_; 924 925 DISALLOW_COPY_AND_ASSIGN(SearchTermTask); 926 }; 927 928 // Adds a search term from the API. 929 class AddSearchTermFromAPITask : public SearchTermTask { 930 public: 931 AddSearchTermFromAPITask(AndroidHistoryProviderService* service, 932 CancelableRequestConsumer* cancelable_consumer, 933 Profile* profile) 934 : SearchTermTask(service, cancelable_consumer, profile) {} 935 936 history::URLID Run(const history::SearchRow& row) { 937 RunAsyncRequestOnUIThreadBlocking( 938 base::Bind(&AddSearchTermFromAPITask::MakeRequestOnUIThread, 939 base::Unretained(this), row)); 940 return result_; 941 } 942 943 private: 944 void MakeRequestOnUIThread(const history::SearchRow& row) { 945 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 946 history::SearchRow internal_row = row; 947 BuildSearchRow(&internal_row); 948 service()->InsertSearchTerm(internal_row, cancelable_consumer(), 949 base::Bind(&AddSearchTermFromAPITask::OnSearchTermInserted, 950 base::Unretained(this))); 951 } 952 953 void OnSearchTermInserted(AndroidHistoryProviderService::Handle handle, 954 bool succeeded, 955 history::URLID id) { 956 // Note that here 0 means an invalid id too. 957 // This is because it represents a SQLite database row id. 958 result_ = id; 959 RequestCompleted(); 960 } 961 962 history::URLID result_; 963 964 DISALLOW_COPY_AND_ASSIGN(AddSearchTermFromAPITask); 965 }; 966 967 // Queries search terms from the API. 968 class QuerySearchTermsFromAPITask : public SearchTermTask { 969 public: 970 QuerySearchTermsFromAPITask(AndroidHistoryProviderService* service, 971 CancelableRequestConsumer* cancelable_consumer, 972 Profile* profile) 973 : SearchTermTask(service, cancelable_consumer, profile), 974 result_(NULL) {} 975 976 history::AndroidStatement* Run( 977 const std::vector<history::SearchRow::ColumnID>& projections, 978 const std::string& selection, 979 const std::vector<base::string16>& selection_args, 980 const std::string& sort_order) { 981 RunAsyncRequestOnUIThreadBlocking( 982 base::Bind(&AndroidHistoryProviderService::QuerySearchTerms, 983 base::Unretained(service()), projections, selection, 984 selection_args, sort_order, cancelable_consumer(), 985 base::Bind( 986 &QuerySearchTermsFromAPITask::OnSearchTermsQueried, 987 base::Unretained(this)))); 988 return result_; 989 } 990 991 private: 992 // Callback to return the result. 993 void OnSearchTermsQueried(AndroidHistoryProviderService::Handle handle, 994 bool succeeded, 995 history::AndroidStatement* statement) { 996 result_ = statement; 997 RequestCompleted(); 998 } 999 1000 history::AndroidStatement* result_; 1001 1002 DISALLOW_COPY_AND_ASSIGN(QuerySearchTermsFromAPITask); 1003 }; 1004 1005 // Updates search terms from the API. 1006 class UpdateSearchTermsFromAPITask : public SearchTermTask { 1007 public: 1008 UpdateSearchTermsFromAPITask(AndroidHistoryProviderService* service, 1009 CancelableRequestConsumer* cancelable_consumer, 1010 Profile* profile) 1011 : SearchTermTask(service, cancelable_consumer, profile), 1012 result_(0) {} 1013 1014 int Run(const history::SearchRow& row, 1015 const std::string& selection, 1016 const std::vector<base::string16>& selection_args) { 1017 RunAsyncRequestOnUIThreadBlocking( 1018 base::Bind(&UpdateSearchTermsFromAPITask::MakeRequestOnUIThread, 1019 base::Unretained(this), row, selection, selection_args)); 1020 return result_; 1021 } 1022 1023 private: 1024 void MakeRequestOnUIThread( 1025 const history::SearchRow& row, 1026 const std::string& selection, 1027 const std::vector<base::string16>& selection_args) { 1028 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1029 history::SearchRow internal_row = row; 1030 BuildSearchRow(&internal_row); 1031 service()->UpdateSearchTerms( 1032 internal_row, 1033 selection, 1034 selection_args, 1035 cancelable_consumer(), 1036 base::Bind(&UpdateSearchTermsFromAPITask::OnSearchTermsUpdated, 1037 base::Unretained(this))); 1038 } 1039 1040 1041 void OnSearchTermsUpdated(AndroidHistoryProviderService::Handle handle, 1042 bool succeeded, 1043 int updated_row_count) { 1044 result_ = updated_row_count; 1045 RequestCompleted(); 1046 } 1047 1048 int result_; 1049 1050 DISALLOW_COPY_AND_ASSIGN(UpdateSearchTermsFromAPITask); 1051 }; 1052 1053 // Removes search terms from the API. 1054 class RemoveSearchTermsFromAPITask : public SearchTermTask { 1055 public: 1056 RemoveSearchTermsFromAPITask(AndroidHistoryProviderService* service, 1057 CancelableRequestConsumer* cancelable_consumer, 1058 Profile* profile) 1059 : SearchTermTask(service, cancelable_consumer, profile), result_() {} 1060 1061 int Run(const std::string& selection, 1062 const std::vector<base::string16>& selection_args) { 1063 RunAsyncRequestOnUIThreadBlocking( 1064 base::Bind(&AndroidHistoryProviderService::DeleteSearchTerms, 1065 base::Unretained(service()), selection, selection_args, 1066 cancelable_consumer(), 1067 base::Bind( 1068 &RemoveSearchTermsFromAPITask::OnSearchTermsDeleted, 1069 base::Unretained(this)))); 1070 return result_; 1071 } 1072 1073 private: 1074 void OnSearchTermsDeleted(AndroidHistoryProviderService::Handle handle, 1075 bool succeeded, 1076 int deleted_row_count) { 1077 result_ = deleted_row_count; 1078 RequestCompleted(); 1079 } 1080 1081 int result_; 1082 1083 DISALLOW_COPY_AND_ASSIGN(RemoveSearchTermsFromAPITask); 1084 }; 1085 1086 // ------------- Other utility methods (may use tasks) ------------- // 1087 1088 // Fills the bookmark |row| with the given java objects. 1089 void FillBookmarkRow(JNIEnv* env, 1090 jobject obj, 1091 jstring url, 1092 jobject created, 1093 jobject isBookmark, 1094 jobject date, 1095 jbyteArray favicon, 1096 jstring title, 1097 jobject visits, 1098 jlong parent_id, 1099 history::HistoryAndBookmarkRow* row, 1100 BookmarkModel* model) { 1101 // Needed because of the internal bookmark model task invocation. 1102 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 1103 1104 if (url) { 1105 base::string16 raw_url = ConvertJavaStringToUTF16(env, url); 1106 // GURL doesn't accept the URL without protocol, but the Android CTS 1107 // allows it. We are trying to prefix with 'http://' to see whether 1108 // GURL thinks it is a valid URL. The original url will be stored in 1109 // history::BookmarkRow.raw_url_. 1110 GURL gurl = ParseAndMaybeAppendScheme(raw_url, kDefaultUrlScheme); 1111 row->set_url(gurl); 1112 row->set_raw_url(base::UTF16ToUTF8(raw_url)); 1113 } 1114 1115 if (created) 1116 row->set_created(ConvertJlongToTime( 1117 ConvertJLongObjectToPrimitive(env, created))); 1118 1119 if (isBookmark) 1120 row->set_is_bookmark(ConvertJBooleanObjectToPrimitive(env, isBookmark)); 1121 1122 if (date) 1123 row->set_last_visit_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive( 1124 env, date))); 1125 1126 if (favicon) { 1127 std::vector<uint8> bytes; 1128 base::android::JavaByteArrayToByteVector(env, favicon, &bytes); 1129 row->set_favicon(base::RefCountedBytes::TakeVector(&bytes)); 1130 } 1131 1132 if (title) 1133 row->set_title(ConvertJavaStringToUTF16(env, title)); 1134 1135 if (visits) 1136 row->set_visit_count(ConvertJIntegerToJint(env, visits)); 1137 1138 // Make sure parent_id is always in the mobile_node branch. 1139 IsInMobileBookmarksBranchTask task(model); 1140 if (task.Run(parent_id)) 1141 row->set_parent_id(parent_id); 1142 } 1143 1144 // Fills the bookmark |row| with the given java objects if it is not null. 1145 void FillSearchRow(JNIEnv* env, 1146 jobject obj, 1147 jstring search_term, 1148 jobject date, 1149 history::SearchRow* row) { 1150 if (search_term) 1151 row->set_search_term(ConvertJavaStringToUTF16(env, search_term)); 1152 1153 if (date) 1154 row->set_search_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive( 1155 env, date))); 1156 } 1157 1158 } // namespace 1159 1160 // ------------- Native initialization and destruction ------------- // 1161 1162 static jlong Init(JNIEnv* env, jobject obj) { 1163 ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj); 1164 return reinterpret_cast<intptr_t>(provider); 1165 } 1166 1167 bool ChromeBrowserProvider::RegisterChromeBrowserProvider(JNIEnv* env) { 1168 return RegisterNativesImpl(env); 1169 } 1170 1171 ChromeBrowserProvider::ChromeBrowserProvider(JNIEnv* env, jobject obj) 1172 : weak_java_provider_(env, obj), 1173 handling_extensive_changes_(false) { 1174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1175 profile_ = g_browser_process->profile_manager()->GetLastUsedProfile(); 1176 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_); 1177 top_sites_ = profile_->GetTopSites(); 1178 service_.reset(new AndroidHistoryProviderService(profile_)); 1179 favicon_service_.reset(FaviconServiceFactory::GetForProfile(profile_, 1180 Profile::EXPLICIT_ACCESS)); 1181 1182 // Registers the notifications we are interested. 1183 bookmark_model_->AddObserver(this); 1184 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, 1185 content::NotificationService::AllSources()); 1186 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, 1187 content::NotificationService::AllSources()); 1188 notification_registrar_.Add(this, 1189 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, 1190 content::NotificationService::AllSources()); 1191 TemplateURLService* template_service = 1192 TemplateURLServiceFactory::GetForProfile(profile_); 1193 if (!template_service->loaded()) 1194 template_service->Load(); 1195 } 1196 1197 ChromeBrowserProvider::~ChromeBrowserProvider() { 1198 bookmark_model_->RemoveObserver(this); 1199 } 1200 1201 void ChromeBrowserProvider::Destroy(JNIEnv*, jobject) { 1202 delete this; 1203 } 1204 1205 // ------------- Provider public APIs ------------- // 1206 1207 jlong ChromeBrowserProvider::AddBookmark(JNIEnv* env, 1208 jobject, 1209 jstring jurl, 1210 jstring jtitle, 1211 jboolean is_folder, 1212 jlong parent_id) { 1213 base::string16 url; 1214 if (jurl) 1215 url = ConvertJavaStringToUTF16(env, jurl); 1216 base::string16 title = ConvertJavaStringToUTF16(env, jtitle); 1217 1218 AddBookmarkTask task(bookmark_model_); 1219 return task.Run(title, url, is_folder, parent_id); 1220 } 1221 1222 jint ChromeBrowserProvider::RemoveBookmark(JNIEnv*, jobject, jlong id) { 1223 RemoveBookmarkTask task(bookmark_model_); 1224 return task.Run(id); 1225 } 1226 1227 jint ChromeBrowserProvider::UpdateBookmark(JNIEnv* env, 1228 jobject, 1229 jlong id, 1230 jstring jurl, 1231 jstring jtitle, 1232 jlong parent_id) { 1233 base::string16 url; 1234 if (jurl) 1235 url = ConvertJavaStringToUTF16(env, jurl); 1236 base::string16 title = ConvertJavaStringToUTF16(env, jtitle); 1237 1238 UpdateBookmarkTask task(bookmark_model_); 1239 return task.Run(id, title, url, parent_id); 1240 } 1241 1242 // Add the bookmark with the given column values. 1243 jlong ChromeBrowserProvider::AddBookmarkFromAPI(JNIEnv* env, 1244 jobject obj, 1245 jstring url, 1246 jobject created, 1247 jobject isBookmark, 1248 jobject date, 1249 jbyteArray favicon, 1250 jstring title, 1251 jobject visits, 1252 jlong parent_id) { 1253 DCHECK(url); 1254 1255 history::HistoryAndBookmarkRow row; 1256 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title, 1257 visits, parent_id, &row, bookmark_model_); 1258 1259 // URL must be valid. 1260 if (row.url().is_empty()) { 1261 LOG(ERROR) << "Not a valid URL " << row.raw_url(); 1262 return kInvalidContentProviderId; 1263 } 1264 1265 AddBookmarkFromAPITask task(service_.get(), &android_history_consumer_); 1266 return task.Run(row); 1267 } 1268 1269 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QueryBookmarkFromAPI( 1270 JNIEnv* env, 1271 jobject obj, 1272 jobjectArray projection, 1273 jstring selections, 1274 jobjectArray selection_args, 1275 jstring sort_order) { 1276 // Converts the projection to array of ColumnID and column name. 1277 // Used to store the projection column ID according their sequence. 1278 std::vector<history::HistoryAndBookmarkRow::ColumnID> query_columns; 1279 // Used to store the projection column names according their sequence. 1280 std::vector<std::string> columns_name; 1281 if (projection) { 1282 jsize len = env->GetArrayLength(projection); 1283 for (int i = 0; i < len; i++) { 1284 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>( 1285 env->GetObjectArrayElement(projection, i))); 1286 history::HistoryAndBookmarkRow::ColumnID id = 1287 history::HistoryAndBookmarkRow::GetColumnID(name); 1288 if (id == history::HistoryAndBookmarkRow::COLUMN_END) { 1289 // Ignore the unknown column; As Android platform will send us 1290 // the non public column. 1291 continue; 1292 } 1293 query_columns.push_back(id); 1294 columns_name.push_back(name); 1295 } 1296 } 1297 1298 std::vector<base::string16> where_args = 1299 ConvertJStringArrayToString16Array(env, selection_args); 1300 1301 std::string where_clause; 1302 if (selections) { 1303 where_clause = ConvertJavaStringToUTF8(env, selections); 1304 } 1305 1306 std::string sort_clause; 1307 if (sort_order) { 1308 sort_clause = ConvertJavaStringToUTF8(env, sort_order); 1309 } 1310 1311 QueryBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1312 history::AndroidStatement* statement = task.Run( 1313 query_columns, where_clause, where_args, sort_clause); 1314 if (!statement) 1315 return ScopedJavaLocalRef<jobject>(); 1316 1317 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor 1318 // Java object. 1319 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement, 1320 service_.get(), favicon_service_.get()); 1321 } 1322 1323 // Updates the bookmarks with the given column values. The value is not given if 1324 // it is NULL. 1325 jint ChromeBrowserProvider::UpdateBookmarkFromAPI(JNIEnv* env, 1326 jobject obj, 1327 jstring url, 1328 jobject created, 1329 jobject isBookmark, 1330 jobject date, 1331 jbyteArray favicon, 1332 jstring title, 1333 jobject visits, 1334 jlong parent_id, 1335 jstring selections, 1336 jobjectArray selection_args) { 1337 history::HistoryAndBookmarkRow row; 1338 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title, 1339 visits, parent_id, &row, bookmark_model_); 1340 1341 std::vector<base::string16> where_args = 1342 ConvertJStringArrayToString16Array(env, selection_args); 1343 1344 std::string where_clause; 1345 if (selections) 1346 where_clause = ConvertJavaStringToUTF8(env, selections); 1347 1348 UpdateBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1349 return task.Run(row, where_clause, where_args); 1350 } 1351 1352 jint ChromeBrowserProvider::RemoveBookmarkFromAPI(JNIEnv* env, 1353 jobject obj, 1354 jstring selections, 1355 jobjectArray selection_args) { 1356 std::vector<base::string16> where_args = 1357 ConvertJStringArrayToString16Array(env, selection_args); 1358 1359 std::string where_clause; 1360 if (selections) 1361 where_clause = ConvertJavaStringToUTF8(env, selections); 1362 1363 RemoveBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1364 return task.Run(where_clause, where_args); 1365 } 1366 1367 jint ChromeBrowserProvider::RemoveHistoryFromAPI(JNIEnv* env, 1368 jobject obj, 1369 jstring selections, 1370 jobjectArray selection_args) { 1371 std::vector<base::string16> where_args = 1372 ConvertJStringArrayToString16Array(env, selection_args); 1373 1374 std::string where_clause; 1375 if (selections) 1376 where_clause = ConvertJavaStringToUTF8(env, selections); 1377 1378 RemoveHistoryFromAPITask task(service_.get(), &android_history_consumer_); 1379 return task.Run(where_clause, where_args); 1380 } 1381 1382 // Add the search term with the given column values. The value is not given if 1383 // it is NULL. 1384 jlong ChromeBrowserProvider::AddSearchTermFromAPI(JNIEnv* env, 1385 jobject obj, 1386 jstring search_term, 1387 jobject date) { 1388 DCHECK(search_term); 1389 1390 history::SearchRow row; 1391 FillSearchRow(env, obj, search_term, date, &row); 1392 1393 // URL must be valid. 1394 if (row.search_term().empty()) { 1395 LOG(ERROR) << "Search term is empty."; 1396 return kInvalidContentProviderId; 1397 } 1398 1399 AddSearchTermFromAPITask task(service_.get(), &android_history_consumer_, 1400 profile_); 1401 return task.Run(row); 1402 } 1403 1404 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QuerySearchTermFromAPI( 1405 JNIEnv* env, 1406 jobject obj, 1407 jobjectArray projection, 1408 jstring selections, 1409 jobjectArray selection_args, 1410 jstring sort_order) { 1411 // Converts the projection to array of ColumnID and column name. 1412 // Used to store the projection column ID according their sequence. 1413 std::vector<history::SearchRow::ColumnID> query_columns; 1414 // Used to store the projection column names according their sequence. 1415 std::vector<std::string> columns_name; 1416 if (projection) { 1417 jsize len = env->GetArrayLength(projection); 1418 for (int i = 0; i < len; i++) { 1419 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>( 1420 env->GetObjectArrayElement(projection, i))); 1421 history::SearchRow::ColumnID id = 1422 history::SearchRow::GetColumnID(name); 1423 if (id == history::SearchRow::COLUMN_END) { 1424 LOG(ERROR) << "Can not find " << name; 1425 return ScopedJavaLocalRef<jobject>(); 1426 } 1427 query_columns.push_back(id); 1428 columns_name.push_back(name); 1429 } 1430 } 1431 1432 std::vector<base::string16> where_args = 1433 ConvertJStringArrayToString16Array(env, selection_args); 1434 1435 std::string where_clause; 1436 if (selections) { 1437 where_clause = ConvertJavaStringToUTF8(env, selections); 1438 } 1439 1440 std::string sort_clause; 1441 if (sort_order) { 1442 sort_clause = ConvertJavaStringToUTF8(env, sort_order); 1443 } 1444 1445 QuerySearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1446 profile_); 1447 history::AndroidStatement* statement = task.Run( 1448 query_columns, where_clause, where_args, sort_clause); 1449 if (!statement) 1450 return ScopedJavaLocalRef<jobject>(); 1451 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor 1452 // Java object. 1453 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement, 1454 service_.get(), favicon_service_.get()); 1455 } 1456 1457 // Updates the search terms with the given column values. The value is not 1458 // given if it is NULL. 1459 jint ChromeBrowserProvider::UpdateSearchTermFromAPI( 1460 JNIEnv* env, jobject obj, jstring search_term, jobject date, 1461 jstring selections, jobjectArray selection_args) { 1462 history::SearchRow row; 1463 FillSearchRow(env, obj, search_term, date, &row); 1464 1465 std::vector<base::string16> where_args = ConvertJStringArrayToString16Array( 1466 env, selection_args); 1467 1468 std::string where_clause; 1469 if (selections) 1470 where_clause = ConvertJavaStringToUTF8(env, selections); 1471 1472 UpdateSearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1473 profile_); 1474 return task.Run(row, where_clause, where_args); 1475 } 1476 1477 jint ChromeBrowserProvider::RemoveSearchTermFromAPI( 1478 JNIEnv* env, jobject obj, jstring selections, jobjectArray selection_args) { 1479 std::vector<base::string16> where_args = 1480 ConvertJStringArrayToString16Array(env, selection_args); 1481 1482 std::string where_clause; 1483 if (selections) 1484 where_clause = ConvertJavaStringToUTF8(env, selections); 1485 1486 RemoveSearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1487 profile_); 1488 return task.Run(where_clause, where_args); 1489 } 1490 1491 // ------------- Provider custom APIs ------------- // 1492 1493 jboolean ChromeBrowserProvider::BookmarkNodeExists( 1494 JNIEnv* env, 1495 jobject obj, 1496 jlong id) { 1497 BookmarkNodeExistsTask task(bookmark_model_); 1498 return task.Run(id); 1499 } 1500 1501 jlong ChromeBrowserProvider::CreateBookmarksFolderOnce( 1502 JNIEnv* env, 1503 jobject obj, 1504 jstring jtitle, 1505 jlong parent_id) { 1506 base::string16 title = ConvertJavaStringToUTF16(env, jtitle); 1507 if (title.empty()) 1508 return kInvalidBookmarkId; 1509 1510 CreateBookmarksFolderOnceTask task(bookmark_model_); 1511 return task.Run(title, parent_id); 1512 } 1513 1514 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetEditableBookmarkFolders( 1515 JNIEnv* env, 1516 jobject obj) { 1517 ScopedJavaGlobalRef<jobject> jroot; 1518 ChromeBookmarkClient* client = 1519 ChromeBookmarkClientFactory::GetForProfile(profile_); 1520 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); 1521 GetEditableBookmarkFoldersTask task(client, model); 1522 task.Run(&jroot); 1523 return ScopedJavaLocalRef<jobject>(jroot); 1524 } 1525 1526 void ChromeBrowserProvider::RemoveAllUserBookmarks(JNIEnv* env, jobject obj) { 1527 RemoveAllUserBookmarksTask task(bookmark_model_); 1528 task.Run(); 1529 } 1530 1531 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetBookmarkNode( 1532 JNIEnv* env, jobject obj, jlong id, jboolean get_parent, 1533 jboolean get_children) { 1534 ScopedJavaGlobalRef<jobject> jnode; 1535 GetBookmarkNodeTask task(bookmark_model_); 1536 task.Run(id, get_parent, get_children, &jnode); 1537 return ScopedJavaLocalRef<jobject>(jnode); 1538 } 1539 1540 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetMobileBookmarksFolder( 1541 JNIEnv* env, 1542 jobject obj) { 1543 ScopedJavaGlobalRef<jobject> jnode; 1544 GetMobileBookmarksNodeTask task(bookmark_model_); 1545 ConvertBookmarkNode(task.Run(), ScopedJavaLocalRef<jobject>(), &jnode); 1546 return ScopedJavaLocalRef<jobject>(jnode); 1547 } 1548 1549 jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch( 1550 JNIEnv* env, 1551 jobject obj, 1552 jlong id) { 1553 IsInMobileBookmarksBranchTask task(bookmark_model_); 1554 return task.Run(id); 1555 } 1556 1557 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetFaviconOrTouchIcon( 1558 JNIEnv* env, jobject obj, jstring jurl) { 1559 if (!jurl) 1560 return ScopedJavaLocalRef<jbyteArray>(); 1561 1562 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl)); 1563 BookmarkIconFetchTask favicon_task(favicon_service_.get(), 1564 profile_, 1565 &favicon_consumer_, 1566 &cancelable_task_tracker_); 1567 favicon_base::FaviconRawBitmapResult bitmap_result = favicon_task.Run(url); 1568 1569 if (!bitmap_result.is_valid() || !bitmap_result.bitmap_data.get()) 1570 return ScopedJavaLocalRef<jbyteArray>(); 1571 1572 return base::android::ToJavaByteArray(env, bitmap_result.bitmap_data->front(), 1573 bitmap_result.bitmap_data->size()); 1574 } 1575 1576 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail( 1577 JNIEnv* env, jobject obj, jstring jurl) { 1578 if (!jurl) 1579 return ScopedJavaLocalRef<jbyteArray>(); 1580 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl)); 1581 1582 // GetPageThumbnail is synchronous and can be called from any thread. 1583 scoped_refptr<base::RefCountedMemory> thumbnail; 1584 if (top_sites_) 1585 top_sites_->GetPageThumbnail(url, false, &thumbnail); 1586 1587 if (!thumbnail.get() || !thumbnail->front()) { 1588 return ScopedJavaLocalRef<jbyteArray>(); 1589 } 1590 1591 return base::android::ToJavaByteArray(env, thumbnail->front(), 1592 thumbnail->size()); 1593 } 1594 1595 // ------------- Observer-related methods ------------- // 1596 1597 void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning( 1598 BookmarkModel* model) { 1599 handling_extensive_changes_ = true; 1600 } 1601 1602 void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded( 1603 BookmarkModel* model) { 1604 handling_extensive_changes_ = false; 1605 BookmarkModelChanged(); 1606 } 1607 1608 void ChromeBrowserProvider::BookmarkModelChanged() { 1609 if (handling_extensive_changes_) 1610 return; 1611 1612 JNIEnv* env = AttachCurrentThread(); 1613 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1614 if (obj.is_null()) 1615 return; 1616 1617 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj()); 1618 } 1619 1620 void ChromeBrowserProvider::Observe( 1621 int type, 1622 const content::NotificationSource& source, 1623 const content::NotificationDetails& details) { 1624 if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED || 1625 type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) { 1626 JNIEnv* env = AttachCurrentThread(); 1627 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1628 if (obj.is_null()) 1629 return; 1630 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj()); 1631 } else if (type == 1632 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED) { 1633 JNIEnv* env = AttachCurrentThread(); 1634 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1635 if (obj.is_null()) 1636 return; 1637 Java_ChromeBrowserProvider_onSearchTermChanged(env, obj.obj()); 1638 } 1639 } 1640