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