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