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