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