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