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