Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / android / provider / chrome_browser_provider.cc
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(FaviconService* service,
653                      base::CancelableTaskTracker* cancelable_tracker,
654                      Profile* profile)
655       : AsyncServiceRequest<FaviconService>(service, cancelable_tracker),
656         profile_(profile) {}
657
658   Profile* profile() const { return profile_; }
659
660  private:
661   Profile* profile_;
662
663   DISALLOW_COPY_AND_ASSIGN(FaviconServiceTask);
664 };
665
666 // Retrieves the favicon or touch icon for a URL from the FaviconService.
667 class BookmarkIconFetchTask : public FaviconServiceTask {
668  public:
669   BookmarkIconFetchTask(FaviconService* favicon_service,
670                         base::CancelableTaskTracker* cancelable_tracker,
671                         Profile* profile)
672       : FaviconServiceTask(favicon_service, cancelable_tracker, profile) {}
673
674   favicon_base::FaviconRawBitmapResult Run(const GURL& url) {
675     float max_scale = ui::GetScaleForScaleFactor(
676         ResourceBundle::GetSharedInstance().GetMaxScaleFactor());
677     int desired_size_in_pixel = std::ceil(gfx::kFaviconSize * max_scale);
678     RunAsyncRequestOnUIThreadBlocking(
679         base::Bind(&FaviconService::GetRawFaviconForPageURL,
680                    base::Unretained(service()),
681                    url,
682                    favicon_base::FAVICON | favicon_base::TOUCH_ICON,
683                    desired_size_in_pixel,
684                    base::Bind(&BookmarkIconFetchTask::OnFaviconRetrieved,
685                               base::Unretained(this)),
686                    cancelable_tracker()));
687     return result_;
688   }
689
690  private:
691   void OnFaviconRetrieved(
692       const favicon_base::FaviconRawBitmapResult& bitmap_result) {
693     result_ = bitmap_result;
694     RequestCompleted();
695   }
696
697   favicon_base::FaviconRawBitmapResult result_;
698
699   DISALLOW_COPY_AND_ASSIGN(BookmarkIconFetchTask);
700 };
701
702 // Base class for all asynchronous blocking tasks that use the Android history
703 // provider service.
704 class HistoryProviderTask
705     : public AsyncServiceRequest<AndroidHistoryProviderService> {
706  public:
707   HistoryProviderTask(AndroidHistoryProviderService* service,
708                       base::CancelableTaskTracker* cancelable_tracker)
709       : AsyncServiceRequest<AndroidHistoryProviderService>(service,
710                                                            cancelable_tracker) {
711   }
712
713  private:
714   DISALLOW_COPY_AND_ASSIGN(HistoryProviderTask);
715 };
716
717 // Adds a bookmark from the API.
718 class AddBookmarkFromAPITask : public HistoryProviderTask {
719  public:
720   AddBookmarkFromAPITask(AndroidHistoryProviderService* service,
721                          base::CancelableTaskTracker* cancelable_tracker)
722       : HistoryProviderTask(service, cancelable_tracker) {}
723
724   history::URLID Run(const history::HistoryAndBookmarkRow& row) {
725     RunAsyncRequestOnUIThreadBlocking(
726         base::Bind(&AndroidHistoryProviderService::InsertHistoryAndBookmark,
727                    base::Unretained(service()),
728                    row,
729                    base::Bind(&AddBookmarkFromAPITask::OnBookmarkInserted,
730                               base::Unretained(this)),
731                    cancelable_tracker()));
732     return result_;
733   }
734
735  private:
736   void OnBookmarkInserted(history::URLID id) {
737     // Note that here 0 means an invalid id.
738     // This is because it represents a SQLite database row id.
739     result_ = id;
740     RequestCompleted();
741   }
742
743   history::URLID result_;
744
745   DISALLOW_COPY_AND_ASSIGN(AddBookmarkFromAPITask);
746 };
747
748 // Queries bookmarks from the API.
749 class QueryBookmarksFromAPITask : public HistoryProviderTask {
750  public:
751   QueryBookmarksFromAPITask(AndroidHistoryProviderService* service,
752                             base::CancelableTaskTracker* cancelable_tracker)
753       : HistoryProviderTask(service, cancelable_tracker), result_(NULL) {}
754
755   history::AndroidStatement* Run(
756       const std::vector<history::HistoryAndBookmarkRow::ColumnID>& projections,
757       const std::string& selection,
758       const std::vector<base::string16>& selection_args,
759       const std::string& sort_order) {
760     RunAsyncRequestOnUIThreadBlocking(
761         base::Bind(&AndroidHistoryProviderService::QueryHistoryAndBookmarks,
762                    base::Unretained(service()),
763                    projections,
764                    selection,
765                    selection_args,
766                    sort_order,
767                    base::Bind(&QueryBookmarksFromAPITask::OnBookmarksQueried,
768                               base::Unretained(this)),
769                    cancelable_tracker()));
770     return result_;
771   }
772
773  private:
774   void OnBookmarksQueried(history::AndroidStatement* statement) {
775     result_ = statement;
776     RequestCompleted();
777   }
778
779   history::AndroidStatement* result_;
780
781   DISALLOW_COPY_AND_ASSIGN(QueryBookmarksFromAPITask);
782 };
783
784 // Updates bookmarks from the API.
785 class UpdateBookmarksFromAPITask : public HistoryProviderTask {
786  public:
787   UpdateBookmarksFromAPITask(AndroidHistoryProviderService* service,
788                              base::CancelableTaskTracker* cancelable_tracker)
789       : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
790
791   int Run(const history::HistoryAndBookmarkRow& row,
792           const std::string& selection,
793           const std::vector<base::string16>& selection_args) {
794     RunAsyncRequestOnUIThreadBlocking(
795         base::Bind(&AndroidHistoryProviderService::UpdateHistoryAndBookmarks,
796                    base::Unretained(service()),
797                    row,
798                    selection,
799                    selection_args,
800                    base::Bind(&UpdateBookmarksFromAPITask::OnBookmarksUpdated,
801                               base::Unretained(this)),
802                    cancelable_tracker()));
803     return result_;
804   }
805
806  private:
807   void OnBookmarksUpdated(int updated_row_count) {
808     result_ = updated_row_count;
809     RequestCompleted();
810   }
811
812   int result_;
813
814   DISALLOW_COPY_AND_ASSIGN(UpdateBookmarksFromAPITask);
815 };
816
817 // Removes bookmarks from the API.
818 class RemoveBookmarksFromAPITask : public HistoryProviderTask {
819  public:
820   RemoveBookmarksFromAPITask(AndroidHistoryProviderService* service,
821                              base::CancelableTaskTracker* cancelable_tracker)
822       : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
823
824   int Run(const std::string& selection,
825           const std::vector<base::string16>& selection_args) {
826     RunAsyncRequestOnUIThreadBlocking(
827         base::Bind(&AndroidHistoryProviderService::DeleteHistoryAndBookmarks,
828                    base::Unretained(service()),
829                    selection,
830                    selection_args,
831                    base::Bind(&RemoveBookmarksFromAPITask::OnBookmarksRemoved,
832                               base::Unretained(this)),
833                    cancelable_tracker()));
834     return result_;
835   }
836
837  private:
838   void OnBookmarksRemoved(int removed_row_count) {
839     result_ = removed_row_count;
840     RequestCompleted();
841   }
842
843   int result_;
844
845   DISALLOW_COPY_AND_ASSIGN(RemoveBookmarksFromAPITask);
846 };
847
848 // Removes history from the API.
849 class RemoveHistoryFromAPITask : public HistoryProviderTask {
850  public:
851   RemoveHistoryFromAPITask(AndroidHistoryProviderService* service,
852                            base::CancelableTaskTracker* cancelable_tracker)
853       : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
854
855   int Run(const std::string& selection,
856           const std::vector<base::string16>& selection_args) {
857     RunAsyncRequestOnUIThreadBlocking(
858         base::Bind(&AndroidHistoryProviderService::DeleteHistory,
859                    base::Unretained(service()),
860                    selection,
861                    selection_args,
862                    base::Bind(&RemoveHistoryFromAPITask::OnHistoryRemoved,
863                               base::Unretained(this)),
864                    cancelable_tracker()));
865     return result_;
866   }
867
868  private:
869   void OnHistoryRemoved(int removed_row_count) {
870     result_ = removed_row_count;
871     RequestCompleted();
872   }
873
874   int result_;
875
876   DISALLOW_COPY_AND_ASSIGN(RemoveHistoryFromAPITask);
877 };
878
879 // This class provides the common method for the SearchTermAPIHelper.
880 class SearchTermTask : public HistoryProviderTask {
881  protected:
882   SearchTermTask(AndroidHistoryProviderService* service,
883                  base::CancelableTaskTracker* cancelable_tracker,
884                  Profile* profile)
885       : HistoryProviderTask(service, cancelable_tracker), profile_(profile) {}
886
887   // Fill SearchRow's keyword_id and url fields according the given
888   // search_term. Return true if succeeded.
889   void BuildSearchRow(history::SearchRow* row) {
890     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
891
892     TemplateURLService* template_service =
893         TemplateURLServiceFactory::GetForProfile(profile_);
894     template_service->Load();
895
896     const TemplateURL* search_engine =
897         template_service->GetDefaultSearchProvider();
898     if (search_engine) {
899       const TemplateURLRef* search_url = &search_engine->url_ref();
900       TemplateURLRef::SearchTermsArgs search_terms_args(row->search_term());
901       search_terms_args.append_extra_query_params = true;
902       std::string url = search_url->ReplaceSearchTerms(
903           search_terms_args, template_service->search_terms_data());
904       if (!url.empty()) {
905         row->set_url(GURL(url));
906         row->set_keyword_id(search_engine->id());
907       }
908     }
909   }
910
911  private:
912   Profile* profile_;
913
914   DISALLOW_COPY_AND_ASSIGN(SearchTermTask);
915 };
916
917 // Adds a search term from the API.
918 class AddSearchTermFromAPITask : public SearchTermTask {
919  public:
920   AddSearchTermFromAPITask(AndroidHistoryProviderService* service,
921                            base::CancelableTaskTracker* cancelable_tracker,
922                            Profile* profile)
923       : SearchTermTask(service, cancelable_tracker, profile) {}
924
925   history::URLID Run(const history::SearchRow& row) {
926     RunAsyncRequestOnUIThreadBlocking(
927         base::Bind(&AddSearchTermFromAPITask::MakeRequestOnUIThread,
928                    base::Unretained(this), row));
929     return result_;
930   }
931
932  private:
933   void MakeRequestOnUIThread(const history::SearchRow& row) {
934     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
935     history::SearchRow internal_row = row;
936     BuildSearchRow(&internal_row);
937     service()->InsertSearchTerm(
938         internal_row,
939         base::Bind(&AddSearchTermFromAPITask::OnSearchTermInserted,
940                    base::Unretained(this)),
941         cancelable_tracker());
942   }
943
944   void OnSearchTermInserted(history::URLID id) {
945     // Note that here 0 means an invalid id.
946     // This is because it represents a SQLite database row id.
947     result_ = id;
948     RequestCompleted();
949   }
950
951   history::URLID result_;
952
953   DISALLOW_COPY_AND_ASSIGN(AddSearchTermFromAPITask);
954 };
955
956 // Queries search terms from the API.
957 class QuerySearchTermsFromAPITask : public SearchTermTask {
958  public:
959   QuerySearchTermsFromAPITask(AndroidHistoryProviderService* service,
960                               base::CancelableTaskTracker* cancelable_tracker,
961                               Profile* profile)
962       : SearchTermTask(service, cancelable_tracker, profile), result_(NULL) {}
963
964   history::AndroidStatement* Run(
965       const std::vector<history::SearchRow::ColumnID>& projections,
966       const std::string& selection,
967       const std::vector<base::string16>& selection_args,
968       const std::string& sort_order) {
969     RunAsyncRequestOnUIThreadBlocking(base::Bind(
970         &AndroidHistoryProviderService::QuerySearchTerms,
971         base::Unretained(service()),
972         projections,
973         selection,
974         selection_args,
975         sort_order,
976         base::Bind(&QuerySearchTermsFromAPITask::OnSearchTermsQueried,
977                    base::Unretained(this)),
978         cancelable_tracker()));
979     return result_;
980   }
981
982  private:
983   // Callback to return the result.
984   void OnSearchTermsQueried(history::AndroidStatement* statement) {
985     result_ = statement;
986     RequestCompleted();
987   }
988
989   history::AndroidStatement* result_;
990
991   DISALLOW_COPY_AND_ASSIGN(QuerySearchTermsFromAPITask);
992 };
993
994 // Updates search terms from the API.
995 class UpdateSearchTermsFromAPITask : public SearchTermTask {
996  public:
997   UpdateSearchTermsFromAPITask(AndroidHistoryProviderService* service,
998                                base::CancelableTaskTracker* cancelable_tracker,
999                                Profile* profile)
1000       : SearchTermTask(service, cancelable_tracker, profile), result_(0) {}
1001
1002   int Run(const history::SearchRow& row,
1003           const std::string& selection,
1004           const std::vector<base::string16>& selection_args) {
1005     RunAsyncRequestOnUIThreadBlocking(
1006         base::Bind(&UpdateSearchTermsFromAPITask::MakeRequestOnUIThread,
1007                    base::Unretained(this), row, selection, selection_args));
1008     return result_;
1009   }
1010
1011  private:
1012   void MakeRequestOnUIThread(
1013       const history::SearchRow& row,
1014       const std::string& selection,
1015       const std::vector<base::string16>& selection_args) {
1016     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1017     history::SearchRow internal_row = row;
1018     BuildSearchRow(&internal_row);
1019     service()->UpdateSearchTerms(
1020         internal_row,
1021         selection,
1022         selection_args,
1023         base::Bind(&UpdateSearchTermsFromAPITask::OnSearchTermsUpdated,
1024                    base::Unretained(this)),
1025         cancelable_tracker());
1026   }
1027
1028   void OnSearchTermsUpdated(int updated_row_count) {
1029     result_ = updated_row_count;
1030     RequestCompleted();
1031   }
1032
1033   int result_;
1034
1035   DISALLOW_COPY_AND_ASSIGN(UpdateSearchTermsFromAPITask);
1036 };
1037
1038 // Removes search terms from the API.
1039 class RemoveSearchTermsFromAPITask : public SearchTermTask {
1040  public:
1041   RemoveSearchTermsFromAPITask(AndroidHistoryProviderService* service,
1042                                base::CancelableTaskTracker* cancelable_tracker,
1043                                Profile* profile)
1044       : SearchTermTask(service, cancelable_tracker, profile), result_() {}
1045
1046   int Run(const std::string& selection,
1047           const std::vector<base::string16>& selection_args) {
1048     RunAsyncRequestOnUIThreadBlocking(base::Bind(
1049         &AndroidHistoryProviderService::DeleteSearchTerms,
1050         base::Unretained(service()),
1051         selection,
1052         selection_args,
1053         base::Bind(&RemoveSearchTermsFromAPITask::OnSearchTermsDeleted,
1054                    base::Unretained(this)),
1055         cancelable_tracker()));
1056     return result_;
1057   }
1058
1059  private:
1060   void OnSearchTermsDeleted(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     base::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(base::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 jlong Init(JNIEnv* env, jobject obj) {
1147   ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj);
1148   return reinterpret_cast<intptr_t>(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   base::string16 url;
1198   if (jurl)
1199     url = ConvertJavaStringToUTF16(env, jurl);
1200   base::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   base::string16 url;
1218   if (jurl)
1219     url = ConvertJavaStringToUTF16(env, jurl);
1220   base::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(), &cancelable_task_tracker_);
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<base::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(), &cancelable_task_tracker_);
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<base::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(), &cancelable_task_tracker_);
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<base::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(), &cancelable_task_tracker_);
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<base::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(), &cancelable_task_tracker_);
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(),
1384                                 &cancelable_task_tracker_,
1385                                 profile_);
1386   return task.Run(row);
1387 }
1388
1389 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QuerySearchTermFromAPI(
1390     JNIEnv* env,
1391     jobject obj,
1392     jobjectArray projection,
1393     jstring selections,
1394     jobjectArray selection_args,
1395     jstring sort_order) {
1396   // Converts the projection to array of ColumnID and column name.
1397   // Used to store the projection column ID according their sequence.
1398   std::vector<history::SearchRow::ColumnID> query_columns;
1399   // Used to store the projection column names according their sequence.
1400   std::vector<std::string> columns_name;
1401   if (projection) {
1402     jsize len = env->GetArrayLength(projection);
1403     for (int i = 0; i < len; i++) {
1404       std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>(
1405           env->GetObjectArrayElement(projection, i)));
1406       history::SearchRow::ColumnID id =
1407           history::SearchRow::GetColumnID(name);
1408       if (id == history::SearchRow::COLUMN_END) {
1409         LOG(ERROR) << "Can not find " << name;
1410         return ScopedJavaLocalRef<jobject>();
1411       }
1412       query_columns.push_back(id);
1413       columns_name.push_back(name);
1414     }
1415   }
1416
1417   std::vector<base::string16> where_args =
1418       ConvertJStringArrayToString16Array(env, selection_args);
1419
1420   std::string where_clause;
1421   if (selections) {
1422     where_clause = ConvertJavaStringToUTF8(env, selections);
1423   }
1424
1425   std::string sort_clause;
1426   if (sort_order) {
1427     sort_clause = ConvertJavaStringToUTF8(env, sort_order);
1428   }
1429
1430   QuerySearchTermsFromAPITask task(service_.get(),
1431                                    &cancelable_task_tracker_,
1432                                    profile_);
1433   history::AndroidStatement* statement = task.Run(
1434       query_columns, where_clause, where_args, sort_clause);
1435   if (!statement)
1436     return ScopedJavaLocalRef<jobject>();
1437   // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor
1438   // Java object.
1439   return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement,
1440              service_.get(), favicon_service_.get());
1441 }
1442
1443 // Updates the search terms with the given column values. The value is not
1444 // given if it is NULL.
1445 jint ChromeBrowserProvider::UpdateSearchTermFromAPI(
1446     JNIEnv* env, jobject obj, jstring search_term, jobject date,
1447     jstring selections, jobjectArray selection_args) {
1448   history::SearchRow row;
1449   FillSearchRow(env, obj, search_term, date, &row);
1450
1451   std::vector<base::string16> where_args = ConvertJStringArrayToString16Array(
1452       env, selection_args);
1453
1454   std::string where_clause;
1455   if (selections)
1456     where_clause = ConvertJavaStringToUTF8(env, selections);
1457
1458   UpdateSearchTermsFromAPITask task(service_.get(),
1459                                     &cancelable_task_tracker_,
1460                                     profile_);
1461   return task.Run(row, where_clause, where_args);
1462 }
1463
1464 jint ChromeBrowserProvider::RemoveSearchTermFromAPI(
1465     JNIEnv* env, jobject obj, jstring selections, jobjectArray selection_args) {
1466   std::vector<base::string16> where_args =
1467       ConvertJStringArrayToString16Array(env, selection_args);
1468
1469   std::string where_clause;
1470   if (selections)
1471     where_clause = ConvertJavaStringToUTF8(env, selections);
1472
1473   RemoveSearchTermsFromAPITask task(service_.get(),
1474                                     &cancelable_task_tracker_,
1475                                     profile_);
1476   return task.Run(where_clause, where_args);
1477 }
1478
1479 // ------------- Provider custom APIs ------------- //
1480
1481 jboolean ChromeBrowserProvider::BookmarkNodeExists(
1482     JNIEnv* env,
1483     jobject obj,
1484     jlong id) {
1485   BookmarkNodeExistsTask task(bookmark_model_);
1486   return task.Run(id);
1487 }
1488
1489 jlong ChromeBrowserProvider::CreateBookmarksFolderOnce(
1490     JNIEnv* env,
1491     jobject obj,
1492     jstring jtitle,
1493     jlong parent_id) {
1494   base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1495   if (title.empty())
1496     return kInvalidBookmarkId;
1497
1498   CreateBookmarksFolderOnceTask task(bookmark_model_);
1499   return task.Run(title, parent_id);
1500 }
1501
1502 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetEditableBookmarkFolders(
1503     JNIEnv* env,
1504     jobject obj) {
1505   ScopedJavaGlobalRef<jobject> jroot;
1506   ChromeBookmarkClient* client =
1507       ChromeBookmarkClientFactory::GetForProfile(profile_);
1508   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
1509   GetEditableBookmarkFoldersTask task(client, model);
1510   task.Run(&jroot);
1511   return ScopedJavaLocalRef<jobject>(jroot);
1512 }
1513
1514 void ChromeBrowserProvider::RemoveAllUserBookmarks(JNIEnv* env, jobject obj) {
1515   RemoveAllUserBookmarksTask task(bookmark_model_);
1516   task.Run();
1517 }
1518
1519 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetBookmarkNode(
1520     JNIEnv* env, jobject obj, jlong id, jboolean get_parent,
1521     jboolean get_children) {
1522   ScopedJavaGlobalRef<jobject> jnode;
1523   GetBookmarkNodeTask task(bookmark_model_);
1524   task.Run(id, get_parent, get_children, &jnode);
1525   return ScopedJavaLocalRef<jobject>(jnode);
1526 }
1527
1528 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetMobileBookmarksFolder(
1529     JNIEnv* env,
1530     jobject obj) {
1531   ScopedJavaGlobalRef<jobject> jnode;
1532   GetMobileBookmarksNodeTask task(bookmark_model_);
1533   ConvertBookmarkNode(task.Run(), ScopedJavaLocalRef<jobject>(), &jnode);
1534   return ScopedJavaLocalRef<jobject>(jnode);
1535 }
1536
1537 jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch(
1538     JNIEnv* env,
1539     jobject obj,
1540     jlong id) {
1541   IsInMobileBookmarksBranchTask task(bookmark_model_);
1542   return task.Run(id);
1543 }
1544
1545 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetFaviconOrTouchIcon(
1546     JNIEnv* env, jobject obj, jstring jurl) {
1547   if (!jurl)
1548     return ScopedJavaLocalRef<jbyteArray>();
1549
1550   GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1551   BookmarkIconFetchTask favicon_task(
1552       favicon_service_.get(), &cancelable_task_tracker_, profile_);
1553   favicon_base::FaviconRawBitmapResult bitmap_result = favicon_task.Run(url);
1554
1555   if (!bitmap_result.is_valid() || !bitmap_result.bitmap_data.get())
1556     return ScopedJavaLocalRef<jbyteArray>();
1557
1558   return base::android::ToJavaByteArray(env, bitmap_result.bitmap_data->front(),
1559                                         bitmap_result.bitmap_data->size());
1560 }
1561
1562 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail(
1563     JNIEnv* env, jobject obj, jstring jurl) {
1564   if (!jurl)
1565     return ScopedJavaLocalRef<jbyteArray>();
1566   GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1567
1568   // GetPageThumbnail is synchronous and can be called from any thread.
1569   scoped_refptr<base::RefCountedMemory> thumbnail;
1570   if (top_sites_)
1571     top_sites_->GetPageThumbnail(url, false, &thumbnail);
1572
1573   if (!thumbnail.get() || !thumbnail->front()) {
1574     return ScopedJavaLocalRef<jbyteArray>();
1575   }
1576
1577   return base::android::ToJavaByteArray(env, thumbnail->front(),
1578       thumbnail->size());
1579 }
1580
1581 // ------------- Observer-related methods ------------- //
1582
1583 void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning(
1584     BookmarkModel* model) {
1585   handling_extensive_changes_ = true;
1586 }
1587
1588 void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded(
1589     BookmarkModel* model) {
1590   handling_extensive_changes_ = false;
1591   BookmarkModelChanged();
1592 }
1593
1594 void ChromeBrowserProvider::BookmarkModelChanged() {
1595   if (handling_extensive_changes_)
1596     return;
1597
1598   JNIEnv* env = AttachCurrentThread();
1599   ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1600   if (obj.is_null())
1601     return;
1602
1603   Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj());
1604 }
1605
1606 void ChromeBrowserProvider::Observe(
1607     int type,
1608     const content::NotificationSource& source,
1609     const content::NotificationDetails& details) {
1610   if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED ||
1611       type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
1612     JNIEnv* env = AttachCurrentThread();
1613     ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1614     if (obj.is_null())
1615       return;
1616     Java_ChromeBrowserProvider_onHistoryChanged(env, obj.obj());
1617   } else if (type ==
1618       chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED) {
1619     JNIEnv* env = AttachCurrentThread();
1620     ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1621     if (obj.is_null())
1622       return;
1623     Java_ChromeBrowserProvider_onSearchTermChanged(env, obj.obj());
1624   }
1625 }