chrome_browser_provider.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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
45using base::android::AttachCurrentThread;
46using base::android::CheckException;
47using base::android::ClearException;
48using base::android::ConvertJavaStringToUTF16;
49using base::android::ConvertJavaStringToUTF8;
50using base::android::ConvertUTF8ToJavaString;
51using base::android::ConvertUTF16ToJavaString;
52using base::android::GetClass;
53using base::android::MethodID;
54using base::android::JavaRef;
55using base::android::ScopedJavaGlobalRef;
56using base::android::ScopedJavaLocalRef;
57using 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
91namespace {
92
93const char kDefaultUrlScheme[] = "http://";
94const int64 kInvalidContentProviderId = 0;
95const 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.
101void 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
122jlong 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
129jboolean 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
137base::Time ConvertJlongToTime(jlong value) {
138  return base::Time::UnixEpoch() +
139      base::TimeDelta::FromMilliseconds((int64)value);
140}
141
142jint 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
150std::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.
167GURL 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
178const 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.
192class 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.
240class 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.
281class 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.
303class 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.
364class 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.
391class 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.
424class 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.
473class 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.
529class 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.
590class 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).
617template <typename Service>
618class 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.
638class 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.
661class 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.
701class 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.
714class 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.
745class 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.
780class 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.
813class 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.
845class 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.
877class 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.
915class 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.
954class 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.
992class 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.
1039class 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.
1074void 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.
1130void 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
1147static jint Init(JNIEnv* env, jobject obj) {
1148  ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj);
1149  return reinterpret_cast<jint>(provider);
1150}
1151
1152bool ChromeBrowserProvider::RegisterChromeBrowserProvider(JNIEnv* env) {
1153  return RegisterNativesImpl(env);
1154}
1155
1156ChromeBrowserProvider::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
1182ChromeBrowserProvider::~ChromeBrowserProvider() {
1183  bookmark_model_->RemoveObserver(this);
1184}
1185
1186void ChromeBrowserProvider::Destroy(JNIEnv*, jobject) {
1187  delete this;
1188}
1189
1190// ------------- Provider public APIs ------------- //
1191
1192jlong 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
1207jint ChromeBrowserProvider::RemoveBookmark(JNIEnv*, jobject, jlong id) {
1208  RemoveBookmarkTask task(bookmark_model_);
1209  return task.Run(id);
1210}
1211
1212jint 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.
1228jlong 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
1254ScopedJavaLocalRef<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.
1310jint 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
1337jint 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
1352jint 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.
1369jlong 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
1389ScopedJavaLocalRef<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.
1444jint 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
1462jint 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
1478jboolean ChromeBrowserProvider::BookmarkNodeExists(
1479    JNIEnv* env,
1480    jobject obj,
1481    jlong id) {
1482  BookmarkNodeExistsTask task(bookmark_model_);
1483  return task.Run(id);
1484}
1485
1486jlong 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
1499ScopedJavaLocalRef<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
1508void ChromeBrowserProvider::RemoveAllBookmarks(JNIEnv* env, jobject obj) {
1509  RemoveAllBookmarksTask task(bookmark_model_);
1510  task.Run();
1511}
1512
1513ScopedJavaLocalRef<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
1522ScopedJavaLocalRef<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
1531jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch(
1532    JNIEnv* env,
1533    jobject obj,
1534    jlong id) {
1535  IsInMobileBookmarksBranchTask task(bookmark_model_);
1536  return task.Run(id);
1537}
1538
1539ScopedJavaLocalRef<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
1558ScopedJavaLocalRef<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
1579void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning(
1580    BookmarkModel* model) {
1581  handling_extensive_changes_ = true;
1582}
1583
1584void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded(
1585    BookmarkModel* model) {
1586  handling_extensive_changes_ = false;
1587  BookmarkModelChanged();
1588}
1589
1590void 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
1602void 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}
1622