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