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