chrome_browser_provider.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 45using base::android::AttachCurrentThread; 46using base::android::CheckException; 47using base::android::ClearException; 48using base::android::ConvertJavaStringToUTF16; 49using base::android::ConvertJavaStringToUTF8; 50using base::android::ConvertUTF8ToJavaString; 51using base::android::ConvertUTF16ToJavaString; 52using base::android::GetClass; 53using base::android::MethodID; 54using base::android::JavaRef; 55using base::android::ScopedJavaGlobalRef; 56using base::android::ScopedJavaLocalRef; 57using 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 91namespace { 92 93const char kDefaultUrlScheme[] = "http://"; 94const int64 kInvalidContentProviderId = 0; 95const 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. 101void 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 122jlong 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 129jboolean 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 137base::Time ConvertJlongToTime(jlong value) { 138 return base::Time::UnixEpoch() + 139 base::TimeDelta::FromMilliseconds((int64)value); 140} 141 142jint 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 150std::vector<string16> ConvertJStringArrayToString16Array(JNIEnv* env, 151 jobjectArray array) { 152 std::vector<string16> results; 153 if (array) { 154 jsize len = env->GetArrayLength(array); 155 for (int i = 0; i < len; i++) { 156 results.push_back(ConvertJavaStringToUTF16(env, 157 static_cast<jstring>(env->GetObjectArrayElement(array, i)))); 158 } 159 } 160 return results; 161} 162 163// ------------- Utility methods used by tasks ------------- // 164 165// Parse the given url and return a GURL, appending the default scheme 166// if one is not present. 167GURL ParseAndMaybeAppendScheme(const string16& url, 168 const char* default_scheme) { 169 GURL gurl(url); 170 if (!gurl.is_valid() && !gurl.has_scheme()) { 171 string16 refined_url(ASCIIToUTF16(default_scheme)); 172 refined_url.append(url); 173 gurl = GURL(refined_url); 174 } 175 return gurl; 176} 177 178const BookmarkNode* GetChildFolderByTitle(const BookmarkNode* parent, 179 const string16& title) { 180 for (int i = 0; i < parent->child_count(); ++i) { 181 if (parent->GetChild(i)->is_folder() && 182 parent->GetChild(i)->GetTitle() == title) { 183 return parent->GetChild(i); 184 } 185 } 186 return NULL; 187} 188 189// ------------- Synchronous task classes ------------- // 190 191// Utility task to add a bookmark. 192class AddBookmarkTask : public BookmarkModelTask { 193 public: 194 explicit AddBookmarkTask(BookmarkModel* model) : BookmarkModelTask(model) {} 195 196 int64 Run(const string16& title, 197 const string16& url, 198 const bool is_folder, 199 const int64 parent_id) { 200 int64 result = kInvalidBookmarkId; 201 RunOnUIThreadBlocking::Run( 202 base::Bind(&AddBookmarkTask::RunOnUIThread, 203 model(), title, url, is_folder, parent_id, &result)); 204 return result; 205 } 206 207 static void RunOnUIThread(BookmarkModel* model, 208 const string16& title, 209 const string16& url, 210 const bool is_folder, 211 const int64 parent_id, 212 int64* result) { 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 214 DCHECK(result); 215 GURL gurl = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme); 216 217 // Check if the bookmark already exists. 218 const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(gurl); 219 if (!node) { 220 const BookmarkNode* parent_node = NULL; 221 if (parent_id >= 0) 222 parent_node = model->GetNodeByID(parent_id); 223 if (!parent_node) 224 parent_node = model->bookmark_bar_node(); 225 226 if (is_folder) 227 node = model->AddFolder(parent_node, parent_node->child_count(), title); 228 else 229 node = model->AddURL(parent_node, 0, title, gurl); 230 } 231 232 *result = node ? node ->id() : kInvalidBookmarkId; 233 } 234 235 private: 236 DISALLOW_COPY_AND_ASSIGN(AddBookmarkTask); 237}; 238 239// Utility method to remove a bookmark. 240class RemoveBookmarkTask : public BookmarkModelObserverTask { 241 public: 242 explicit RemoveBookmarkTask(BookmarkModel* model) 243 : BookmarkModelObserverTask(model), 244 deleted_(0), 245 id_to_delete_(kInvalidBookmarkId) {} 246 virtual ~RemoveBookmarkTask() {} 247 248 int Run(const int64 id) { 249 id_to_delete_ = id; 250 RunOnUIThreadBlocking::Run( 251 base::Bind(&RemoveBookmarkTask::RunOnUIThread, model(), id)); 252 return deleted_; 253 } 254 255 static void RunOnUIThread(BookmarkModel* model, const int64 id) { 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 257 const BookmarkNode* node = model->GetNodeByID(id); 258 if (node && node->parent()) { 259 const BookmarkNode* parent_node = node->parent(); 260 model->Remove(parent_node, parent_node->GetIndexOf(node)); 261 } 262 } 263 264 // Verify that the bookmark was actually removed. Called synchronously. 265 virtual void BookmarkNodeRemoved(BookmarkModel* bookmark_model, 266 const BookmarkNode* parent, 267 int old_index, 268 const BookmarkNode* node) OVERRIDE { 269 if (bookmark_model == model() && node->id() == id_to_delete_) 270 ++deleted_; 271 } 272 273 private: 274 int deleted_; 275 int64 id_to_delete_; 276 277 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarkTask); 278}; 279 280// Utility method to remove all bookmarks. 281class RemoveAllBookmarksTask : public BookmarkModelObserverTask { 282 public: 283 explicit RemoveAllBookmarksTask(BookmarkModel* model) 284 : BookmarkModelObserverTask(model) {} 285 286 virtual ~RemoveAllBookmarksTask() {} 287 288 void Run() { 289 RunOnUIThreadBlocking::Run( 290 base::Bind(&RemoveAllBookmarksTask::RunOnUIThread, model())); 291 } 292 293 static void RunOnUIThread(BookmarkModel* model) { 294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 295 model->RemoveAll(); 296 } 297 298 private: 299 DISALLOW_COPY_AND_ASSIGN(RemoveAllBookmarksTask); 300}; 301 302// Utility method to update a bookmark. 303class UpdateBookmarkTask : public BookmarkModelObserverTask { 304 public: 305 explicit UpdateBookmarkTask(BookmarkModel* model) 306 : BookmarkModelObserverTask(model), 307 updated_(0), 308 id_to_update_(kInvalidBookmarkId){} 309 virtual ~UpdateBookmarkTask() {} 310 311 int Run(const int64 id, 312 const string16& title, 313 const string16& url, 314 const int64 parent_id) { 315 id_to_update_ = id; 316 RunOnUIThreadBlocking::Run( 317 base::Bind(&UpdateBookmarkTask::RunOnUIThread, 318 model(), id, title, url, parent_id)); 319 return updated_; 320 } 321 322 static void RunOnUIThread(BookmarkModel* model, 323 const int64 id, 324 const string16& title, 325 const string16& url, 326 const int64 parent_id) { 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 328 const BookmarkNode* node = model->GetNodeByID(id); 329 if (node) { 330 if (node->GetTitle() != title) 331 model->SetTitle(node, title); 332 333 if (node->type() == BookmarkNode::URL) { 334 GURL bookmark_url = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme); 335 if (bookmark_url != node->url()) 336 model->SetURL(node, bookmark_url); 337 } 338 339 if (parent_id >= 0 && 340 (!node->parent() || parent_id != node->parent()->id())) { 341 const BookmarkNode* new_parent = model->GetNodeByID(parent_id); 342 343 if (new_parent) 344 model->Move(node, new_parent, 0); 345 } 346 } 347 } 348 349 // Verify that the bookmark was actually updated. Called synchronously. 350 virtual void BookmarkNodeChanged(BookmarkModel* bookmark_model, 351 const BookmarkNode* node) OVERRIDE { 352 if (bookmark_model == model() && node->id() == id_to_update_) 353 ++updated_; 354 } 355 356 private: 357 int updated_; 358 int64 id_to_update_; 359 360 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarkTask); 361}; 362 363// Checks if a node exists in the bookmark model. 364class BookmarkNodeExistsTask : public BookmarkModelTask { 365 public: 366 explicit BookmarkNodeExistsTask(BookmarkModel* model) 367 : BookmarkModelTask(model) { 368 } 369 370 bool Run(const int64 id) { 371 bool result = false; 372 RunOnUIThreadBlocking::Run( 373 base::Bind(&BookmarkNodeExistsTask::RunOnUIThread, 374 model(), id, &result)); 375 return result; 376 } 377 378 static void RunOnUIThread(BookmarkModel* model, 379 const int64 id, 380 bool* result) { 381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 382 DCHECK(result); 383 *result = model->GetNodeByID(id) != NULL; 384 } 385 386 private: 387 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeExistsTask); 388}; 389 390// Checks if a node belongs to the Mobile Bookmarks hierarchy branch. 391class IsInMobileBookmarksBranchTask : public BookmarkModelTask { 392 public: 393 explicit IsInMobileBookmarksBranchTask(BookmarkModel* model) 394 : BookmarkModelTask(model) {} 395 396 bool Run(const int64 id) { 397 bool result = false; 398 RunOnUIThreadBlocking::Run( 399 base::Bind(&IsInMobileBookmarksBranchTask::RunOnUIThread, 400 model(), id, &result)); 401 return result; 402 } 403 404 static void RunOnUIThread(BookmarkModel* model, 405 const int64 id, 406 bool *result) { 407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 408 DCHECK(result); 409 const BookmarkNode* node = model->GetNodeByID(id); 410 const BookmarkNode* mobile_node = model->mobile_node(); 411 while (node && node != mobile_node) 412 node = node->parent(); 413 414 *result = node == mobile_node; 415 } 416 417 private: 418 DISALLOW_COPY_AND_ASSIGN(IsInMobileBookmarksBranchTask); 419}; 420 421// Creates folder or retrieves its id if already exists. 422// An invalid parent id is assumed to represent the Mobile Bookmarks folder. 423// Can only be used to create folders inside the Mobile Bookmarks branch. 424class CreateBookmarksFolderOnceTask : public BookmarkModelTask { 425 public: 426 explicit CreateBookmarksFolderOnceTask(BookmarkModel* model) 427 : BookmarkModelTask(model) {} 428 429 int64 Run(const string16& title, const int64 parent_id) { 430 int64 result = kInvalidBookmarkId; 431 RunOnUIThreadBlocking::Run( 432 base::Bind(&CreateBookmarksFolderOnceTask::RunOnUIThread, 433 model(), title, parent_id, &result)); 434 return result; 435 } 436 437 static void RunOnUIThread(BookmarkModel* model, 438 const string16& title, 439 const int64 parent_id, 440 int64* result) { 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 442 DCHECK(result); 443 444 // Invalid ids are assumed to refer to the Mobile Bookmarks folder. 445 const BookmarkNode* parent = parent_id >= 0 ? 446 model->GetNodeByID(parent_id) : model->mobile_node(); 447 DCHECK(parent); 448 449 bool in_mobile_bookmarks; 450 IsInMobileBookmarksBranchTask::RunOnUIThread(model, parent->id(), 451 &in_mobile_bookmarks); 452 if (!in_mobile_bookmarks) { 453 // The parent folder must be inside the Mobile Bookmarks folder. 454 *result = kInvalidBookmarkId; 455 return; 456 } 457 458 const BookmarkNode* node = GetChildFolderByTitle(parent, title); 459 if (node) { 460 *result = node->id(); 461 return; 462 } 463 464 AddBookmarkTask::RunOnUIThread(model, title, string16(), true, 465 parent->id(), result); 466 } 467 468 private: 469 DISALLOW_COPY_AND_ASSIGN(CreateBookmarksFolderOnceTask); 470}; 471 472// Creates a Java BookmarkNode object for a node given its id. 473class GetAllBookmarkFoldersTask : public BookmarkModelTask { 474 public: 475 explicit GetAllBookmarkFoldersTask(BookmarkModel* model) 476 : BookmarkModelTask(model) { 477 } 478 479 void Run(ScopedJavaGlobalRef<jobject>* jroot) { 480 RunOnUIThreadBlocking::Run( 481 base::Bind(&GetAllBookmarkFoldersTask::RunOnUIThread, model(), jroot)); 482 } 483 484 static void RunOnUIThread(BookmarkModel* model, 485 ScopedJavaGlobalRef<jobject>* jroot) { 486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 487 const BookmarkNode* root = model->root_node(); 488 if (!root || !root->is_folder()) 489 return; 490 491 // The iterative approach is not possible because ScopedGlobalJavaRefs 492 // cannot be copy-constructed, and therefore not used in STL containers. 493 ConvertFolderSubtree(AttachCurrentThread(), root, 494 ScopedJavaLocalRef<jobject>(), jroot); 495 } 496 497 private: 498 static void ConvertFolderSubtree(JNIEnv* env, 499 const BookmarkNode* node, 500 const JavaRef<jobject>& parent_folder, 501 ScopedJavaGlobalRef<jobject>* jfolder) { 502 DCHECK(node); 503 DCHECK(node->is_folder()); 504 DCHECK(jfolder); 505 506 // Global refs should be used here for thread-safety reasons as this task 507 // might be invoked from a thread other than UI. All refs are scoped. 508 ConvertBookmarkNode(node, parent_folder, jfolder); 509 510 for (int i = 0; i < node->child_count(); ++i) { 511 const BookmarkNode* child = node->GetChild(i); 512 if (child->is_folder()) { 513 ScopedJavaGlobalRef<jobject> jchild; 514 ConvertFolderSubtree(env, child, *jfolder, &jchild); 515 516 Java_BookmarkNode_addChild(env, jfolder->obj(), jchild.obj()); 517 if (ClearException(env)) { 518 LOG(WARNING) << "Java exception while adding child node."; 519 return; 520 } 521 } 522 } 523 } 524 525 DISALLOW_COPY_AND_ASSIGN(GetAllBookmarkFoldersTask); 526}; 527 528// Creates a Java BookmarkNode object for a node given its id. 529class GetBookmarkNodeTask : public BookmarkModelTask { 530 public: 531 explicit GetBookmarkNodeTask(BookmarkModel* model) 532 : BookmarkModelTask(model) { 533 } 534 535 void Run(const int64 id, 536 bool get_parent, 537 bool get_children, 538 ScopedJavaGlobalRef<jobject>* jnode) { 539 return RunOnUIThreadBlocking::Run( 540 base::Bind(&GetBookmarkNodeTask::RunOnUIThread, 541 model(), id, get_parent, get_children, jnode)); 542 } 543 544 static void RunOnUIThread(BookmarkModel* model, 545 const int64 id, 546 bool get_parent, 547 bool get_children, 548 ScopedJavaGlobalRef<jobject>* jnode) { 549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 550 const BookmarkNode* node = model->GetNodeByID(id); 551 if (!node || !jnode) 552 return; 553 554 ScopedJavaGlobalRef<jobject> jparent; 555 if (get_parent) { 556 ConvertBookmarkNode(node->parent(), ScopedJavaLocalRef<jobject>(), 557 &jparent); 558 } 559 560 ConvertBookmarkNode(node, jparent, jnode); 561 562 JNIEnv* env = AttachCurrentThread(); 563 if (!jparent.is_null()) { 564 Java_BookmarkNode_addChild(env, jparent.obj(), jnode->obj()); 565 if (ClearException(env)) { 566 LOG(WARNING) << "Java exception while adding child node."; 567 return; 568 } 569 } 570 571 if (get_children) { 572 for (int i = 0; i < node->child_count(); ++i) { 573 ScopedJavaGlobalRef<jobject> jchild; 574 ConvertBookmarkNode(node->GetChild(i), *jnode, &jchild); 575 Java_BookmarkNode_addChild(env, jnode->obj(), jchild.obj()); 576 if (ClearException(env)) { 577 LOG(WARNING) << "Java exception while adding child node."; 578 return; 579 } 580 } 581 } 582 } 583 584 private: 585 DISALLOW_COPY_AND_ASSIGN(GetBookmarkNodeTask); 586}; 587 588// Gets the Mobile Bookmarks node. Using this task ensures the correct 589// initialization of the bookmark model. 590class GetMobileBookmarksNodeTask : public BookmarkModelTask { 591 public: 592 explicit GetMobileBookmarksNodeTask(BookmarkModel* model) 593 : BookmarkModelTask(model) {} 594 595 const BookmarkNode* Run() { 596 const BookmarkNode* result = NULL; 597 RunOnUIThreadBlocking::Run( 598 base::Bind(&GetMobileBookmarksNodeTask::RunOnUIThread, 599 model(), &result)); 600 return result; 601 } 602 603 static void RunOnUIThread(BookmarkModel* model, const BookmarkNode** result) { 604 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 605 DCHECK(result); 606 *result = model->mobile_node(); 607 } 608 609 private: 610 DISALLOW_COPY_AND_ASSIGN(GetMobileBookmarksNodeTask); 611}; 612 613// ------------- Aynchronous requests classes ------------- // 614 615// Base class for asynchronous blocking requests to Chromium services. 616// Service: type of the service to use (e.g. HistoryService, FaviconService). 617template <typename Service> 618class AsyncServiceRequest : protected BlockingUIThreadAsyncRequest { 619 public: 620 AsyncServiceRequest(Service* service, 621 CancelableRequestConsumer* cancelable_consumer) 622 : service_(service), cancelable_consumer_(cancelable_consumer) {} 623 624 Service* service() const { return service_; } 625 626 CancelableRequestConsumer* cancelable_consumer() const { 627 return cancelable_consumer_; 628 } 629 630 private: 631 Service* service_; 632 CancelableRequestConsumer* cancelable_consumer_; 633 634 DISALLOW_COPY_AND_ASSIGN(AsyncServiceRequest); 635}; 636 637// Base class for all asynchronous blocking tasks that use the favicon service. 638class FaviconServiceTask : public AsyncServiceRequest<FaviconService> { 639 public: 640 FaviconServiceTask(FaviconService* service, 641 Profile* profile, 642 CancelableRequestConsumer* cancelable_consumer, 643 CancelableTaskTracker* cancelable_tracker) 644 : AsyncServiceRequest<FaviconService>(service, cancelable_consumer), 645 profile_(profile), 646 cancelable_tracker_(cancelable_tracker) {} 647 648 Profile* profile() const { return profile_; } 649 CancelableTaskTracker* cancelable_tracker() const { 650 return cancelable_tracker_; 651 } 652 653 private: 654 Profile* profile_; 655 CancelableTaskTracker* cancelable_tracker_; 656 657 DISALLOW_COPY_AND_ASSIGN(FaviconServiceTask); 658}; 659 660// Retrieves the favicon or touch icon for a URL from the FaviconService. 661class BookmarkIconFetchTask : public FaviconServiceTask { 662 public: 663 BookmarkIconFetchTask( 664 FaviconService* favicon_service, 665 Profile* profile, 666 CancelableRequestConsumer* cancelable_consumer, 667 CancelableTaskTracker* cancelable_tracker) 668 : FaviconServiceTask(favicon_service, profile, 669 cancelable_consumer, cancelable_tracker) {} 670 671 chrome::FaviconBitmapResult Run(const GURL& url) { 672 RunAsyncRequestOnUIThreadBlocking( 673 base::Bind(&FaviconService::GetRawFaviconForURL, 674 base::Unretained(service()), 675 FaviconService::FaviconForURLParams( 676 profile(), 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. 701class 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. 714class 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. 745class 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<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. 780class 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<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. 813class 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<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. 845class 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<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. 877class 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. 915class 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. 954class 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<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. 992class 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<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(const history::SearchRow& row, 1011 const std::string& selection, 1012 const std::vector<string16>& selection_args) { 1013 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1014 history::SearchRow internal_row = row; 1015 BuildSearchRow(&internal_row); 1016 service()->UpdateSearchTerms( 1017 internal_row, 1018 selection, 1019 selection_args, 1020 cancelable_consumer(), 1021 base::Bind(&UpdateSearchTermsFromAPITask::OnSearchTermsUpdated, 1022 base::Unretained(this))); 1023 } 1024 1025 1026 void OnSearchTermsUpdated(AndroidHistoryProviderService::Handle handle, 1027 bool succeeded, 1028 int updated_row_count) { 1029 result_ = updated_row_count; 1030 RequestCompleted(); 1031 } 1032 1033 int result_; 1034 1035 DISALLOW_COPY_AND_ASSIGN(UpdateSearchTermsFromAPITask); 1036}; 1037 1038// Removes search terms from the API. 1039class RemoveSearchTermsFromAPITask : public SearchTermTask { 1040 public: 1041 RemoveSearchTermsFromAPITask(AndroidHistoryProviderService* service, 1042 CancelableRequestConsumer* cancelable_consumer, 1043 Profile* profile) 1044 : SearchTermTask(service, cancelable_consumer, profile), result_() {} 1045 1046 int Run(const std::string& selection, 1047 const std::vector<string16>& selection_args) { 1048 RunAsyncRequestOnUIThreadBlocking( 1049 base::Bind(&AndroidHistoryProviderService::DeleteSearchTerms, 1050 base::Unretained(service()), selection, selection_args, 1051 cancelable_consumer(), 1052 base::Bind( 1053 &RemoveSearchTermsFromAPITask::OnSearchTermsDeleted, 1054 base::Unretained(this)))); 1055 return result_; 1056 } 1057 1058 private: 1059 void OnSearchTermsDeleted(AndroidHistoryProviderService::Handle handle, 1060 bool succeeded, 1061 int deleted_row_count) { 1062 result_ = deleted_row_count; 1063 RequestCompleted(); 1064 } 1065 1066 int result_; 1067 1068 DISALLOW_COPY_AND_ASSIGN(RemoveSearchTermsFromAPITask); 1069}; 1070 1071// ------------- Other utility methods (may use tasks) ------------- // 1072 1073// Fills the bookmark |row| with the given java objects. 1074void FillBookmarkRow(JNIEnv* env, 1075 jobject obj, 1076 jstring url, 1077 jobject created, 1078 jobject isBookmark, 1079 jobject date, 1080 jbyteArray favicon, 1081 jstring title, 1082 jobject visits, 1083 jlong parent_id, 1084 history::HistoryAndBookmarkRow* row, 1085 BookmarkModel* model) { 1086 // Needed because of the internal bookmark model task invocation. 1087 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 1088 1089 if (url) { 1090 string16 raw_url = ConvertJavaStringToUTF16(env, url); 1091 // GURL doesn't accept the URL without protocol, but the Android CTS 1092 // allows it. We are trying to prefix with 'http://' to see whether 1093 // GURL thinks it is a valid URL. The original url will be stored in 1094 // history::BookmarkRow.raw_url_. 1095 GURL gurl = ParseAndMaybeAppendScheme(raw_url, kDefaultUrlScheme); 1096 row->set_url(gurl); 1097 row->set_raw_url(UTF16ToUTF8(raw_url)); 1098 } 1099 1100 if (created) 1101 row->set_created(ConvertJlongToTime( 1102 ConvertJLongObjectToPrimitive(env, created))); 1103 1104 if (isBookmark) 1105 row->set_is_bookmark(ConvertJBooleanObjectToPrimitive(env, isBookmark)); 1106 1107 if (date) 1108 row->set_last_visit_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive( 1109 env, date))); 1110 1111 if (favicon) { 1112 std::vector<uint8> bytes; 1113 base::android::JavaByteArrayToByteVector(env, favicon, &bytes); 1114 row->set_favicon(base::RefCountedBytes::TakeVector(&bytes)); 1115 } 1116 1117 if (title) 1118 row->set_title(ConvertJavaStringToUTF16(env, title)); 1119 1120 if (visits) 1121 row->set_visit_count(ConvertJIntegerToJint(env, visits)); 1122 1123 // Make sure parent_id is always in the mobile_node branch. 1124 IsInMobileBookmarksBranchTask task(model); 1125 if (task.Run(parent_id)) 1126 row->set_parent_id(parent_id); 1127} 1128 1129// Fills the bookmark |row| with the given java objects if it is not null. 1130void FillSearchRow(JNIEnv* env, 1131 jobject obj, 1132 jstring search_term, 1133 jobject date, 1134 history::SearchRow* row) { 1135 if (search_term) 1136 row->set_search_term(ConvertJavaStringToUTF16(env, search_term)); 1137 1138 if (date) 1139 row->set_search_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive( 1140 env, date))); 1141} 1142 1143} // namespace 1144 1145// ------------- Native initialization and destruction ------------- // 1146 1147static jint Init(JNIEnv* env, jobject obj) { 1148 ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj); 1149 return reinterpret_cast<jint>(provider); 1150} 1151 1152bool ChromeBrowserProvider::RegisterChromeBrowserProvider(JNIEnv* env) { 1153 return RegisterNativesImpl(env); 1154} 1155 1156ChromeBrowserProvider::ChromeBrowserProvider(JNIEnv* env, jobject obj) 1157 : weak_java_provider_(env, obj), 1158 handling_extensive_changes_(false) { 1159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1160 profile_ = g_browser_process->profile_manager()->GetLastUsedProfile(); 1161 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_); 1162 top_sites_ = profile_->GetTopSites(); 1163 service_.reset(new AndroidHistoryProviderService(profile_)); 1164 favicon_service_.reset(FaviconServiceFactory::GetForProfile(profile_, 1165 Profile::EXPLICIT_ACCESS)); 1166 1167 // Registers the notifications we are interested. 1168 bookmark_model_->AddObserver(this); 1169 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, 1170 content::NotificationService::AllSources()); 1171 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, 1172 content::NotificationService::AllSources()); 1173 notification_registrar_.Add(this, 1174 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, 1175 content::NotificationService::AllSources()); 1176 TemplateURLService* template_service = 1177 TemplateURLServiceFactory::GetForProfile(profile_); 1178 if (!template_service->loaded()) 1179 template_service->Load(); 1180} 1181 1182ChromeBrowserProvider::~ChromeBrowserProvider() { 1183 bookmark_model_->RemoveObserver(this); 1184} 1185 1186void ChromeBrowserProvider::Destroy(JNIEnv*, jobject) { 1187 delete this; 1188} 1189 1190// ------------- Provider public APIs ------------- // 1191 1192jlong ChromeBrowserProvider::AddBookmark(JNIEnv* env, 1193 jobject, 1194 jstring jurl, 1195 jstring jtitle, 1196 jboolean is_folder, 1197 jlong parent_id) { 1198 string16 url; 1199 if (jurl) 1200 url = ConvertJavaStringToUTF16(env, jurl); 1201 string16 title = ConvertJavaStringToUTF16(env, jtitle); 1202 1203 AddBookmarkTask task(bookmark_model_); 1204 return task.Run(title, url, is_folder, parent_id); 1205} 1206 1207jint ChromeBrowserProvider::RemoveBookmark(JNIEnv*, jobject, jlong id) { 1208 RemoveBookmarkTask task(bookmark_model_); 1209 return task.Run(id); 1210} 1211 1212jint ChromeBrowserProvider::UpdateBookmark(JNIEnv* env, 1213 jobject, 1214 jlong id, 1215 jstring jurl, 1216 jstring jtitle, 1217 jlong parent_id) { 1218 string16 url; 1219 if (jurl) 1220 url = ConvertJavaStringToUTF16(env, jurl); 1221 string16 title = ConvertJavaStringToUTF16(env, jtitle); 1222 1223 UpdateBookmarkTask task(bookmark_model_); 1224 return task.Run(id, title, url, parent_id); 1225} 1226 1227// Add the bookmark with the given column values. 1228jlong ChromeBrowserProvider::AddBookmarkFromAPI(JNIEnv* env, 1229 jobject obj, 1230 jstring url, 1231 jobject created, 1232 jobject isBookmark, 1233 jobject date, 1234 jbyteArray favicon, 1235 jstring title, 1236 jobject visits, 1237 jlong parent_id) { 1238 DCHECK(url); 1239 1240 history::HistoryAndBookmarkRow row; 1241 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title, 1242 visits, parent_id, &row, bookmark_model_); 1243 1244 // URL must be valid. 1245 if (row.url().is_empty()) { 1246 LOG(ERROR) << "Not a valid URL " << row.raw_url(); 1247 return kInvalidContentProviderId; 1248 } 1249 1250 AddBookmarkFromAPITask task(service_.get(), &android_history_consumer_); 1251 return task.Run(row); 1252} 1253 1254ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QueryBookmarkFromAPI( 1255 JNIEnv* env, 1256 jobject obj, 1257 jobjectArray projection, 1258 jstring selections, 1259 jobjectArray selection_args, 1260 jstring sort_order) { 1261 // Converts the projection to array of ColumnID and column name. 1262 // Used to store the projection column ID according their sequence. 1263 std::vector<history::HistoryAndBookmarkRow::ColumnID> query_columns; 1264 // Used to store the projection column names according their sequence. 1265 std::vector<std::string> columns_name; 1266 if (projection) { 1267 jsize len = env->GetArrayLength(projection); 1268 for (int i = 0; i < len; i++) { 1269 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>( 1270 env->GetObjectArrayElement(projection, i))); 1271 history::HistoryAndBookmarkRow::ColumnID id = 1272 history::HistoryAndBookmarkRow::GetColumnID(name); 1273 if (id == history::HistoryAndBookmarkRow::COLUMN_END) { 1274 // Ignore the unknown column; As Android platform will send us 1275 // the non public column. 1276 continue; 1277 } 1278 query_columns.push_back(id); 1279 columns_name.push_back(name); 1280 } 1281 } 1282 1283 std::vector<string16> where_args = 1284 ConvertJStringArrayToString16Array(env, selection_args); 1285 1286 std::string where_clause; 1287 if (selections) { 1288 where_clause = ConvertJavaStringToUTF8(env, selections); 1289 } 1290 1291 std::string sort_clause; 1292 if (sort_order) { 1293 sort_clause = ConvertJavaStringToUTF8(env, sort_order); 1294 } 1295 1296 QueryBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1297 history::AndroidStatement* statement = task.Run( 1298 query_columns, where_clause, where_args, sort_clause); 1299 if (!statement) 1300 return ScopedJavaLocalRef<jobject>(); 1301 1302 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor 1303 // Java object. 1304 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement, 1305 service_.get(), favicon_service_.get()); 1306} 1307 1308// Updates the bookmarks with the given column values. The value is not given if 1309// it is NULL. 1310jint ChromeBrowserProvider::UpdateBookmarkFromAPI(JNIEnv* env, 1311 jobject obj, 1312 jstring url, 1313 jobject created, 1314 jobject isBookmark, 1315 jobject date, 1316 jbyteArray favicon, 1317 jstring title, 1318 jobject visits, 1319 jlong parent_id, 1320 jstring selections, 1321 jobjectArray selection_args) { 1322 history::HistoryAndBookmarkRow row; 1323 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title, 1324 visits, parent_id, &row, bookmark_model_); 1325 1326 std::vector<string16> where_args = 1327 ConvertJStringArrayToString16Array(env, selection_args); 1328 1329 std::string where_clause; 1330 if (selections) 1331 where_clause = ConvertJavaStringToUTF8(env, selections); 1332 1333 UpdateBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1334 return task.Run(row, where_clause, where_args); 1335} 1336 1337jint ChromeBrowserProvider::RemoveBookmarkFromAPI(JNIEnv* env, 1338 jobject obj, 1339 jstring selections, 1340 jobjectArray selection_args) { 1341 std::vector<string16> where_args = 1342 ConvertJStringArrayToString16Array(env, selection_args); 1343 1344 std::string where_clause; 1345 if (selections) 1346 where_clause = ConvertJavaStringToUTF8(env, selections); 1347 1348 RemoveBookmarksFromAPITask task(service_.get(), &android_history_consumer_); 1349 return task.Run(where_clause, where_args); 1350} 1351 1352jint ChromeBrowserProvider::RemoveHistoryFromAPI(JNIEnv* env, 1353 jobject obj, 1354 jstring selections, 1355 jobjectArray selection_args) { 1356 std::vector<string16> where_args = 1357 ConvertJStringArrayToString16Array(env, selection_args); 1358 1359 std::string where_clause; 1360 if (selections) 1361 where_clause = ConvertJavaStringToUTF8(env, selections); 1362 1363 RemoveHistoryFromAPITask task(service_.get(), &android_history_consumer_); 1364 return task.Run(where_clause, where_args); 1365} 1366 1367// Add the search term with the given column values. The value is not given if 1368// it is NULL. 1369jlong ChromeBrowserProvider::AddSearchTermFromAPI(JNIEnv* env, 1370 jobject obj, 1371 jstring search_term, 1372 jobject date) { 1373 DCHECK(search_term); 1374 1375 history::SearchRow row; 1376 FillSearchRow(env, obj, search_term, date, &row); 1377 1378 // URL must be valid. 1379 if (row.search_term().empty()) { 1380 LOG(ERROR) << "Search term is empty."; 1381 return kInvalidContentProviderId; 1382 } 1383 1384 AddSearchTermFromAPITask task(service_.get(), &android_history_consumer_, 1385 profile_); 1386 return task.Run(row); 1387} 1388 1389ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QuerySearchTermFromAPI( 1390 JNIEnv* env, 1391 jobject obj, 1392 jobjectArray projection, 1393 jstring selections, 1394 jobjectArray selection_args, 1395 jstring sort_order) { 1396 // Converts the projection to array of ColumnID and column name. 1397 // Used to store the projection column ID according their sequence. 1398 std::vector<history::SearchRow::ColumnID> query_columns; 1399 // Used to store the projection column names according their sequence. 1400 std::vector<std::string> columns_name; 1401 if (projection) { 1402 jsize len = env->GetArrayLength(projection); 1403 for (int i = 0; i < len; i++) { 1404 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>( 1405 env->GetObjectArrayElement(projection, i))); 1406 history::SearchRow::ColumnID id = 1407 history::SearchRow::GetColumnID(name); 1408 if (id == history::SearchRow::COLUMN_END) { 1409 LOG(ERROR) << "Can not find " << name; 1410 return ScopedJavaLocalRef<jobject>(); 1411 } 1412 query_columns.push_back(id); 1413 columns_name.push_back(name); 1414 } 1415 } 1416 1417 std::vector<string16> where_args = 1418 ConvertJStringArrayToString16Array(env, selection_args); 1419 1420 std::string where_clause; 1421 if (selections) { 1422 where_clause = ConvertJavaStringToUTF8(env, selections); 1423 } 1424 1425 std::string sort_clause; 1426 if (sort_order) { 1427 sort_clause = ConvertJavaStringToUTF8(env, sort_order); 1428 } 1429 1430 QuerySearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1431 profile_); 1432 history::AndroidStatement* statement = task.Run( 1433 query_columns, where_clause, where_args, sort_clause); 1434 if (!statement) 1435 return ScopedJavaLocalRef<jobject>(); 1436 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor 1437 // Java object. 1438 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement, 1439 service_.get(), favicon_service_.get()); 1440} 1441 1442// Updates the search terms with the given column values. The value is not 1443// given if it is NULL. 1444jint ChromeBrowserProvider::UpdateSearchTermFromAPI( 1445 JNIEnv* env, jobject obj, jstring search_term, jobject date, 1446 jstring selections, jobjectArray selection_args) { 1447 history::SearchRow row; 1448 FillSearchRow(env, obj, search_term, date, &row); 1449 1450 std::vector<string16> where_args = ConvertJStringArrayToString16Array( 1451 env, selection_args); 1452 1453 std::string where_clause; 1454 if (selections) 1455 where_clause = ConvertJavaStringToUTF8(env, selections); 1456 1457 UpdateSearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1458 profile_); 1459 return task.Run(row, where_clause, where_args); 1460} 1461 1462jint ChromeBrowserProvider::RemoveSearchTermFromAPI( 1463 JNIEnv* env, jobject obj, jstring selections, jobjectArray selection_args) { 1464 std::vector<string16> where_args = 1465 ConvertJStringArrayToString16Array(env, selection_args); 1466 1467 std::string where_clause; 1468 if (selections) 1469 where_clause = ConvertJavaStringToUTF8(env, selections); 1470 1471 RemoveSearchTermsFromAPITask task(service_.get(), &android_history_consumer_, 1472 profile_); 1473 return task.Run(where_clause, where_args); 1474} 1475 1476// ------------- Provider custom APIs ------------- // 1477 1478jboolean ChromeBrowserProvider::BookmarkNodeExists( 1479 JNIEnv* env, 1480 jobject obj, 1481 jlong id) { 1482 BookmarkNodeExistsTask task(bookmark_model_); 1483 return task.Run(id); 1484} 1485 1486jlong ChromeBrowserProvider::CreateBookmarksFolderOnce( 1487 JNIEnv* env, 1488 jobject obj, 1489 jstring jtitle, 1490 jlong parent_id) { 1491 string16 title = ConvertJavaStringToUTF16(env, jtitle); 1492 if (title.empty()) 1493 return kInvalidBookmarkId; 1494 1495 CreateBookmarksFolderOnceTask task(bookmark_model_); 1496 return task.Run(title, parent_id); 1497} 1498 1499ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetAllBookmarkFolders( 1500 JNIEnv* env, 1501 jobject obj) { 1502 ScopedJavaGlobalRef<jobject> jroot; 1503 GetAllBookmarkFoldersTask task(bookmark_model_); 1504 task.Run(&jroot); 1505 return ScopedJavaLocalRef<jobject>(jroot); 1506} 1507 1508void ChromeBrowserProvider::RemoveAllBookmarks(JNIEnv* env, jobject obj) { 1509 RemoveAllBookmarksTask task(bookmark_model_); 1510 task.Run(); 1511} 1512 1513ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetBookmarkNode( 1514 JNIEnv* env, jobject obj, jlong id, jboolean get_parent, 1515 jboolean get_children) { 1516 ScopedJavaGlobalRef<jobject> jnode; 1517 GetBookmarkNodeTask task(bookmark_model_); 1518 task.Run(id, get_parent, get_children, &jnode); 1519 return ScopedJavaLocalRef<jobject>(jnode); 1520} 1521 1522ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetMobileBookmarksFolder( 1523 JNIEnv* env, 1524 jobject obj) { 1525 ScopedJavaGlobalRef<jobject> jnode; 1526 GetMobileBookmarksNodeTask task(bookmark_model_); 1527 ConvertBookmarkNode(task.Run(), ScopedJavaLocalRef<jobject>(), &jnode); 1528 return ScopedJavaLocalRef<jobject>(jnode); 1529} 1530 1531jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch( 1532 JNIEnv* env, 1533 jobject obj, 1534 jlong id) { 1535 IsInMobileBookmarksBranchTask task(bookmark_model_); 1536 return task.Run(id); 1537} 1538 1539ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetFaviconOrTouchIcon( 1540 JNIEnv* env, jobject obj, jstring jurl) { 1541 if (!jurl) 1542 return ScopedJavaLocalRef<jbyteArray>(); 1543 1544 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl)); 1545 BookmarkIconFetchTask favicon_task(favicon_service_.get(), 1546 profile_, 1547 &favicon_consumer_, 1548 &cancelable_task_tracker_); 1549 chrome::FaviconBitmapResult bitmap_result = favicon_task.Run(url); 1550 1551 if (!bitmap_result.is_valid() || !bitmap_result.bitmap_data.get()) 1552 return ScopedJavaLocalRef<jbyteArray>(); 1553 1554 return base::android::ToJavaByteArray(env, bitmap_result.bitmap_data->front(), 1555 bitmap_result.bitmap_data->size()); 1556} 1557 1558ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail( 1559 JNIEnv* env, jobject obj, jstring jurl) { 1560 if (!jurl) 1561 return ScopedJavaLocalRef<jbyteArray>(); 1562 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl)); 1563 1564 // GetPageThumbnail is synchronous and can be called from any thread. 1565 scoped_refptr<base::RefCountedMemory> thumbnail; 1566 if (top_sites_) 1567 top_sites_->GetPageThumbnail(url, false, &thumbnail); 1568 1569 if (!thumbnail.get() || !thumbnail->front()) { 1570 return ScopedJavaLocalRef<jbyteArray>(); 1571 } 1572 1573 return base::android::ToJavaByteArray(env, thumbnail->front(), 1574 thumbnail->size()); 1575} 1576 1577// ------------- Observer-related methods ------------- // 1578 1579void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning( 1580 BookmarkModel* model) { 1581 handling_extensive_changes_ = true; 1582} 1583 1584void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded( 1585 BookmarkModel* model) { 1586 handling_extensive_changes_ = false; 1587 BookmarkModelChanged(); 1588} 1589 1590void ChromeBrowserProvider::BookmarkModelChanged() { 1591 if (handling_extensive_changes_) 1592 return; 1593 1594 JNIEnv* env = AttachCurrentThread(); 1595 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1596 if (obj.is_null()) 1597 return; 1598 1599 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj()); 1600} 1601 1602void ChromeBrowserProvider::Observe( 1603 int type, 1604 const content::NotificationSource& source, 1605 const content::NotificationDetails& details) { 1606 if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED || 1607 type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) { 1608 JNIEnv* env = AttachCurrentThread(); 1609 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1610 if (obj.is_null()) 1611 return; 1612 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj()); 1613 } else if (type == 1614 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED) { 1615 JNIEnv* env = AttachCurrentThread(); 1616 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env); 1617 if (obj.is_null()) 1618 return; 1619 Java_ChromeBrowserProvider_onSearchTermChanged(env, obj.obj()); 1620 } 1621} 1622