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