Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / android / provider / chrome_browser_provider.cc
blob9e9e03817b4d19715f9997d983f0a92dab285766
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.
5 #include "chrome/browser/android/provider/chrome_browser_provider.h"
7 #include <cmath>
8 #include <list>
9 #include <utility>
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/cancelable_task_tracker.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/android/provider/blocking_ui_thread_async_request.h"
20 #include "chrome/browser/android/provider/bookmark_model_observer_task.h"
21 #include "chrome/browser/android/provider/run_on_ui_thread_blocking.h"
22 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
23 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
24 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/favicon/favicon_service.h"
28 #include "chrome/browser/favicon/favicon_service_factory.h"
29 #include "chrome/browser/history/android/sqlite_cursor.h"
30 #include "chrome/browser/history/history_service_factory.h"
31 #include "chrome/browser/history/top_sites.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/profiles/profile_manager.h"
34 #include "chrome/browser/search_engines/template_url_service_factory.h"
35 #include "components/bookmarks/browser/bookmark_model.h"
36 #include "components/bookmarks/browser/bookmark_utils.h"
37 #include "components/history/core/android/android_history_types.h"
38 #include "components/search_engines/template_url.h"
39 #include "components/search_engines/template_url_service.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/browser/notification_service.h"
42 #include "jni/ChromeBrowserProvider_jni.h"
43 #include "sql/statement.h"
44 #include "ui/base/layout.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/gfx/favicon_size.h"
48 using base::android::AttachCurrentThread;
49 using base::android::CheckException;
50 using base::android::ClearException;
51 using base::android::ConvertJavaStringToUTF16;
52 using base::android::ConvertJavaStringToUTF8;
53 using base::android::ConvertUTF8ToJavaString;
54 using base::android::ConvertUTF16ToJavaString;
55 using base::android::GetClass;
56 using base::android::MethodID;
57 using base::android::JavaRef;
58 using base::android::ScopedJavaGlobalRef;
59 using base::android::ScopedJavaLocalRef;
60 using content::BrowserThread;
62 // After refactoring the following class hierarchy has been created in order
63 // to avoid repeating code again for the same basic kind of tasks, to enforce
64 // the correct thread usage and to prevent known race conditions and deadlocks.
66 // - RunOnUIThreadBlocking: auxiliary class to run methods in the UI thread
67 // blocking the current one until finished. Because of the provider threading
68 // expectations this cannot be used from the UI thread.
70 // - BookmarkModelTask: base class for all tasks that operate in any way with
71 // the bookmark model. This class ensures that the model is loaded and
72 // prevents possible deadlocks. Derived classes should make use of
73 // RunOnUIThreadBlocking to perform any manipulation of the bookmark model in
74 // the UI thread. The Run method of these tasks cannot be invoked directly
75 // from the UI thread, but RunOnUIThread can be safely used from the UI
76 // thread code of other BookmarkModelTasks.
78 // - AsyncServiceRequest: base class for any asynchronous requests made to a
79 // Chromium service that require to block the current thread until completed.
80 // Derived classes should make use of RunAsyncRequestOnUIThreadBlocking to
81 // post their requests in the UI thread and return the results synchronously.
82 // All derived classes MUST ALWAYS call RequestCompleted when receiving the
83 // request response. These tasks cannot be invoked from the UI thread.
85 // - FaviconServiceTask: base class for asynchronous requests that make use of
86 // Chromium's favicon service. See AsyncServiceRequest for more details.
88 // - HistoryProviderTask: base class for asynchronous requests that make use of
89 // AndroidHistoryProviderService. See AsyncServiceRequest for mode details.
91 // - SearchTermTask: base class for asynchronous requests that involve the
92 // search term API. Works in the same way as HistoryProviderTask.
94 namespace {
96 const char kDefaultUrlScheme[] = "http://";
97 const int64 kInvalidContentProviderId = 0;
98 const int64 kInvalidBookmarkId = -1;
100 // ------------- Java-related utility methods ------------- //
102 // Convert a BookmarkNode, |node|, to the java representation of a bookmark node
103 // stored in |*jnode|. Parent node information is optional.
104 void ConvertBookmarkNode(
105 const BookmarkNode* node,
106 const JavaRef<jobject>& parent_node,
107 ScopedJavaGlobalRef<jobject>* jnode) {
108 DCHECK(jnode);
109 if (!node)
110 return;
112 JNIEnv* env = AttachCurrentThread();
113 ScopedJavaLocalRef<jstring> url;
114 if (node->is_url())
115 url.Reset(ConvertUTF8ToJavaString(env, node->url().spec()));
116 ScopedJavaLocalRef<jstring> title(
117 ConvertUTF16ToJavaString(env, node->GetTitle()));
119 jnode->Reset(
120 Java_BookmarkNode_create(
121 env, node->id(), (jint) node->type(), title.obj(), url.obj(),
122 parent_node.obj()));
125 jlong ConvertJLongObjectToPrimitive(JNIEnv* env, jobject long_obj) {
126 ScopedJavaLocalRef<jclass> jlong_clazz = GetClass(env, "java/lang/Long");
127 jmethodID long_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
128 env, jlong_clazz.obj(), "longValue", "()J");
129 return env->CallLongMethod(long_obj, long_value, NULL);
132 jboolean ConvertJBooleanObjectToPrimitive(JNIEnv* env, jobject boolean_object) {
133 ScopedJavaLocalRef<jclass> jboolean_clazz =
134 GetClass(env, "java/lang/Boolean");
135 jmethodID boolean_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
136 env, jboolean_clazz.obj(), "booleanValue", "()Z");
137 return env->CallBooleanMethod(boolean_object, boolean_value, NULL);
140 base::Time ConvertJlongToTime(jlong value) {
141 return base::Time::UnixEpoch() +
142 base::TimeDelta::FromMilliseconds((int64)value);
145 jint ConvertJIntegerToJint(JNIEnv* env, jobject integer_obj) {
146 ScopedJavaLocalRef<jclass> jinteger_clazz =
147 GetClass(env, "java/lang/Integer");
148 jmethodID int_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
149 env, jinteger_clazz.obj(), "intValue", "()I");
150 return env->CallIntMethod(integer_obj, int_value, NULL);
153 std::vector<base::string16> ConvertJStringArrayToString16Array(
154 JNIEnv* env,
155 jobjectArray array) {
156 std::vector<base::string16> results;
157 if (array) {
158 jsize len = env->GetArrayLength(array);
159 for (int i = 0; i < len; i++) {
160 results.push_back(ConvertJavaStringToUTF16(env,
161 static_cast<jstring>(env->GetObjectArrayElement(array, i))));
164 return results;
167 // ------------- Utility methods used by tasks ------------- //
169 // Parse the given url and return a GURL, appending the default scheme
170 // if one is not present.
171 GURL ParseAndMaybeAppendScheme(const base::string16& url,
172 const char* default_scheme) {
173 GURL gurl(url);
174 if (!gurl.is_valid() && !gurl.has_scheme()) {
175 base::string16 refined_url(base::ASCIIToUTF16(default_scheme));
176 refined_url.append(url);
177 gurl = GURL(refined_url);
179 return gurl;
182 const BookmarkNode* GetChildFolderByTitle(const BookmarkNode* parent,
183 const base::string16& title) {
184 for (int i = 0; i < parent->child_count(); ++i) {
185 if (parent->GetChild(i)->is_folder() &&
186 parent->GetChild(i)->GetTitle() == title) {
187 return parent->GetChild(i);
190 return NULL;
193 // ------------- Synchronous task classes ------------- //
195 // Utility task to add a bookmark.
196 class AddBookmarkTask : public BookmarkModelTask {
197 public:
198 explicit AddBookmarkTask(BookmarkModel* model) : BookmarkModelTask(model) {}
200 int64 Run(const base::string16& title,
201 const base::string16& url,
202 const bool is_folder,
203 const int64 parent_id) {
204 int64 result = kInvalidBookmarkId;
205 RunOnUIThreadBlocking::Run(
206 base::Bind(&AddBookmarkTask::RunOnUIThread,
207 model(), title, url, is_folder, parent_id, &result));
208 return result;
211 static void RunOnUIThread(BookmarkModel* model,
212 const base::string16& title,
213 const base::string16& url,
214 const bool is_folder,
215 const int64 parent_id,
216 int64* result) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218 DCHECK(result);
219 GURL gurl = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme);
221 // Check if the bookmark already exists.
222 const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(gurl);
223 if (!node) {
224 const BookmarkNode* parent_node = NULL;
225 if (parent_id >= 0)
226 parent_node = bookmarks::GetBookmarkNodeByID(model, parent_id);
227 if (!parent_node)
228 parent_node = model->bookmark_bar_node();
230 if (is_folder)
231 node = model->AddFolder(parent_node, parent_node->child_count(), title);
232 else
233 node = model->AddURL(parent_node, 0, title, gurl);
236 *result = node ? node ->id() : kInvalidBookmarkId;
239 private:
240 DISALLOW_COPY_AND_ASSIGN(AddBookmarkTask);
243 // Utility method to remove a bookmark.
244 class RemoveBookmarkTask : public BookmarkModelObserverTask {
245 public:
246 explicit RemoveBookmarkTask(BookmarkModel* model)
247 : BookmarkModelObserverTask(model),
248 deleted_(0),
249 id_to_delete_(kInvalidBookmarkId) {}
250 virtual ~RemoveBookmarkTask() {}
252 int Run(const int64 id) {
253 id_to_delete_ = id;
254 RunOnUIThreadBlocking::Run(
255 base::Bind(&RemoveBookmarkTask::RunOnUIThread, model(), id));
256 return deleted_;
259 static void RunOnUIThread(BookmarkModel* model, const int64 id) {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
262 if (node && node->parent()) {
263 const BookmarkNode* parent_node = node->parent();
264 model->Remove(parent_node, parent_node->GetIndexOf(node));
268 // Verify that the bookmark was actually removed. Called synchronously.
269 virtual void BookmarkNodeRemoved(
270 BookmarkModel* bookmark_model,
271 const BookmarkNode* parent,
272 int old_index,
273 const BookmarkNode* node,
274 const std::set<GURL>& removed_urls) override {
275 if (bookmark_model == model() && node->id() == id_to_delete_)
276 ++deleted_;
279 private:
280 int deleted_;
281 int64 id_to_delete_;
283 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarkTask);
286 // Utility method to remove all bookmarks that the user can edit.
287 class RemoveAllUserBookmarksTask : public BookmarkModelObserverTask {
288 public:
289 explicit RemoveAllUserBookmarksTask(BookmarkModel* model)
290 : BookmarkModelObserverTask(model) {}
292 virtual ~RemoveAllUserBookmarksTask() {}
294 void Run() {
295 RunOnUIThreadBlocking::Run(
296 base::Bind(&RemoveAllUserBookmarksTask::RunOnUIThread, model()));
299 static void RunOnUIThread(BookmarkModel* model) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 model->RemoveAllUserBookmarks();
304 private:
305 DISALLOW_COPY_AND_ASSIGN(RemoveAllUserBookmarksTask);
308 // Utility method to update a bookmark.
309 class UpdateBookmarkTask : public BookmarkModelObserverTask {
310 public:
311 explicit UpdateBookmarkTask(BookmarkModel* model)
312 : BookmarkModelObserverTask(model),
313 updated_(0),
314 id_to_update_(kInvalidBookmarkId){}
315 virtual ~UpdateBookmarkTask() {}
317 int Run(const int64 id,
318 const base::string16& title,
319 const base::string16& url,
320 const int64 parent_id) {
321 id_to_update_ = id;
322 RunOnUIThreadBlocking::Run(
323 base::Bind(&UpdateBookmarkTask::RunOnUIThread,
324 model(), id, title, url, parent_id));
325 return updated_;
328 static void RunOnUIThread(BookmarkModel* model,
329 const int64 id,
330 const base::string16& title,
331 const base::string16& url,
332 const int64 parent_id) {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
334 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
335 if (node) {
336 if (node->GetTitle() != title)
337 model->SetTitle(node, title);
339 if (node->type() == BookmarkNode::URL) {
340 GURL bookmark_url = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme);
341 if (bookmark_url != node->url())
342 model->SetURL(node, bookmark_url);
345 if (parent_id >= 0 &&
346 (!node->parent() || parent_id != node->parent()->id())) {
347 const BookmarkNode* new_parent =
348 bookmarks::GetBookmarkNodeByID(model, parent_id);
350 if (new_parent)
351 model->Move(node, new_parent, 0);
356 // Verify that the bookmark was actually updated. Called synchronously.
357 virtual void BookmarkNodeChanged(BookmarkModel* bookmark_model,
358 const BookmarkNode* node) override {
359 if (bookmark_model == model() && node->id() == id_to_update_)
360 ++updated_;
363 private:
364 int updated_;
365 int64 id_to_update_;
367 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarkTask);
370 // Checks if a node exists in the bookmark model.
371 class BookmarkNodeExistsTask : public BookmarkModelTask {
372 public:
373 explicit BookmarkNodeExistsTask(BookmarkModel* model)
374 : BookmarkModelTask(model) {
377 bool Run(const int64 id) {
378 bool result = false;
379 RunOnUIThreadBlocking::Run(
380 base::Bind(&BookmarkNodeExistsTask::RunOnUIThread,
381 model(), id, &result));
382 return result;
385 static void RunOnUIThread(BookmarkModel* model,
386 const int64 id,
387 bool* result) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 DCHECK(result);
390 *result = bookmarks::GetBookmarkNodeByID(model, id) != NULL;
393 private:
394 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeExistsTask);
397 // Checks if a node belongs to the Mobile Bookmarks hierarchy branch.
398 class IsInMobileBookmarksBranchTask : public BookmarkModelTask {
399 public:
400 explicit IsInMobileBookmarksBranchTask(BookmarkModel* model)
401 : BookmarkModelTask(model) {}
403 bool Run(const int64 id) {
404 bool result = false;
405 RunOnUIThreadBlocking::Run(
406 base::Bind(&IsInMobileBookmarksBranchTask::RunOnUIThread,
407 model(), id, &result));
408 return result;
411 static void RunOnUIThread(BookmarkModel* model,
412 const int64 id,
413 bool *result) {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 DCHECK(result);
416 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
417 const BookmarkNode* mobile_node = model->mobile_node();
418 while (node && node != mobile_node)
419 node = node->parent();
421 *result = node == mobile_node;
424 private:
425 DISALLOW_COPY_AND_ASSIGN(IsInMobileBookmarksBranchTask);
428 // Creates folder or retrieves its id if already exists.
429 // An invalid parent id is assumed to represent the Mobile Bookmarks folder.
430 // Can only be used to create folders inside the Mobile Bookmarks branch.
431 class CreateBookmarksFolderOnceTask : public BookmarkModelTask {
432 public:
433 explicit CreateBookmarksFolderOnceTask(BookmarkModel* model)
434 : BookmarkModelTask(model) {}
436 int64 Run(const base::string16& title, const int64 parent_id) {
437 int64 result = kInvalidBookmarkId;
438 RunOnUIThreadBlocking::Run(
439 base::Bind(&CreateBookmarksFolderOnceTask::RunOnUIThread,
440 model(), title, parent_id, &result));
441 return result;
444 static void RunOnUIThread(BookmarkModel* model,
445 const base::string16& title,
446 const int64 parent_id,
447 int64* result) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 DCHECK(result);
451 // Invalid ids are assumed to refer to the Mobile Bookmarks folder.
452 const BookmarkNode* parent =
453 parent_id >= 0 ? bookmarks::GetBookmarkNodeByID(model, parent_id)
454 : model->mobile_node();
455 DCHECK(parent);
457 bool in_mobile_bookmarks;
458 IsInMobileBookmarksBranchTask::RunOnUIThread(model, parent->id(),
459 &in_mobile_bookmarks);
460 if (!in_mobile_bookmarks) {
461 // The parent folder must be inside the Mobile Bookmarks folder.
462 *result = kInvalidBookmarkId;
463 return;
466 const BookmarkNode* node = GetChildFolderByTitle(parent, title);
467 if (node) {
468 *result = node->id();
469 return;
472 AddBookmarkTask::RunOnUIThread(model, title, base::string16(), true,
473 parent->id(), result);
476 private:
477 DISALLOW_COPY_AND_ASSIGN(CreateBookmarksFolderOnceTask);
480 // Creates a Java BookmarkNode object for a node given its id.
481 class GetEditableBookmarkFoldersTask : public BookmarkModelTask {
482 public:
483 GetEditableBookmarkFoldersTask(ChromeBookmarkClient* client,
484 BookmarkModel* model)
485 : BookmarkModelTask(model), client_(client) {}
487 void Run(ScopedJavaGlobalRef<jobject>* jroot) {
488 RunOnUIThreadBlocking::Run(
489 base::Bind(&GetEditableBookmarkFoldersTask::RunOnUIThread,
490 client_, model(), jroot));
493 static void RunOnUIThread(ChromeBookmarkClient* client,
494 BookmarkModel* model,
495 ScopedJavaGlobalRef<jobject>* jroot) {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
497 const BookmarkNode* root = model->root_node();
498 if (!root || !root->is_folder())
499 return;
501 // The iterative approach is not possible because ScopedGlobalJavaRefs
502 // cannot be copy-constructed, and therefore not used in STL containers.
503 ConvertFolderSubtree(client, AttachCurrentThread(), root,
504 ScopedJavaLocalRef<jobject>(), jroot);
507 private:
508 static void ConvertFolderSubtree(ChromeBookmarkClient* client,
509 JNIEnv* env,
510 const BookmarkNode* node,
511 const JavaRef<jobject>& parent_folder,
512 ScopedJavaGlobalRef<jobject>* jfolder) {
513 DCHECK(node);
514 DCHECK(node->is_folder());
515 DCHECK(jfolder);
517 // Global refs should be used here for thread-safety reasons as this task
518 // might be invoked from a thread other than UI. All refs are scoped.
519 ConvertBookmarkNode(node, parent_folder, jfolder);
521 for (int i = 0; i < node->child_count(); ++i) {
522 const BookmarkNode* child = node->GetChild(i);
523 if (child->is_folder() && client->CanBeEditedByUser(child)) {
524 ScopedJavaGlobalRef<jobject> jchild;
525 ConvertFolderSubtree(client, env, child, *jfolder, &jchild);
527 Java_BookmarkNode_addChild(env, jfolder->obj(), jchild.obj());
528 if (ClearException(env)) {
529 LOG(WARNING) << "Java exception while adding child node.";
530 return;
536 ChromeBookmarkClient* client_;
538 DISALLOW_COPY_AND_ASSIGN(GetEditableBookmarkFoldersTask);
541 // Creates a Java BookmarkNode object for a node given its id.
542 class GetBookmarkNodeTask : public BookmarkModelTask {
543 public:
544 explicit GetBookmarkNodeTask(BookmarkModel* model)
545 : BookmarkModelTask(model) {
548 void Run(const int64 id,
549 bool get_parent,
550 bool get_children,
551 ScopedJavaGlobalRef<jobject>* jnode) {
552 return RunOnUIThreadBlocking::Run(
553 base::Bind(&GetBookmarkNodeTask::RunOnUIThread,
554 model(), id, get_parent, get_children, jnode));
557 static void RunOnUIThread(BookmarkModel* model,
558 const int64 id,
559 bool get_parent,
560 bool get_children,
561 ScopedJavaGlobalRef<jobject>* jnode) {
562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
564 if (!node || !jnode)
565 return;
567 ScopedJavaGlobalRef<jobject> jparent;
568 if (get_parent) {
569 ConvertBookmarkNode(node->parent(), ScopedJavaLocalRef<jobject>(),
570 &jparent);
573 ConvertBookmarkNode(node, jparent, jnode);
575 JNIEnv* env = AttachCurrentThread();
576 if (!jparent.is_null()) {
577 Java_BookmarkNode_addChild(env, jparent.obj(), jnode->obj());
578 if (ClearException(env)) {
579 LOG(WARNING) << "Java exception while adding child node.";
580 return;
584 if (get_children) {
585 for (int i = 0; i < node->child_count(); ++i) {
586 ScopedJavaGlobalRef<jobject> jchild;
587 ConvertBookmarkNode(node->GetChild(i), *jnode, &jchild);
588 Java_BookmarkNode_addChild(env, jnode->obj(), jchild.obj());
589 if (ClearException(env)) {
590 LOG(WARNING) << "Java exception while adding child node.";
591 return;
597 private:
598 DISALLOW_COPY_AND_ASSIGN(GetBookmarkNodeTask);
601 // Gets the Mobile Bookmarks node. Using this task ensures the correct
602 // initialization of the bookmark model.
603 class GetMobileBookmarksNodeTask : public BookmarkModelTask {
604 public:
605 explicit GetMobileBookmarksNodeTask(BookmarkModel* model)
606 : BookmarkModelTask(model) {}
608 const BookmarkNode* Run() {
609 const BookmarkNode* result = NULL;
610 RunOnUIThreadBlocking::Run(
611 base::Bind(&GetMobileBookmarksNodeTask::RunOnUIThread,
612 model(), &result));
613 return result;
616 static void RunOnUIThread(BookmarkModel* model, const BookmarkNode** result) {
617 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
618 DCHECK(result);
619 *result = model->mobile_node();
622 private:
623 DISALLOW_COPY_AND_ASSIGN(GetMobileBookmarksNodeTask);
626 // ------------- Aynchronous requests classes ------------- //
628 // Base class for asynchronous blocking requests to Chromium services.
629 // Service: type of the service to use (e.g. HistoryService, FaviconService).
630 template <typename Service>
631 class AsyncServiceRequest : protected BlockingUIThreadAsyncRequest {
632 public:
633 AsyncServiceRequest(Service* service,
634 base::CancelableTaskTracker* cancelable_tracker)
635 : service_(service), cancelable_tracker_(cancelable_tracker) {}
637 Service* service() const { return service_; }
639 base::CancelableTaskTracker* cancelable_tracker() const {
640 return cancelable_tracker_;
643 private:
644 Service* service_;
645 base::CancelableTaskTracker* cancelable_tracker_;
647 DISALLOW_COPY_AND_ASSIGN(AsyncServiceRequest);
650 // Base class for all asynchronous blocking tasks that use the favicon service.
651 class FaviconServiceTask : public AsyncServiceRequest<FaviconService> {
652 public:
653 FaviconServiceTask(FaviconService* service,
654 base::CancelableTaskTracker* cancelable_tracker,
655 Profile* profile)
656 : AsyncServiceRequest<FaviconService>(service, cancelable_tracker),
657 profile_(profile) {}
659 Profile* profile() const { return profile_; }
661 private:
662 Profile* profile_;
664 DISALLOW_COPY_AND_ASSIGN(FaviconServiceTask);
667 // Retrieves the favicon or touch icon for a URL from the FaviconService.
668 class BookmarkIconFetchTask : public FaviconServiceTask {
669 public:
670 BookmarkIconFetchTask(FaviconService* favicon_service,
671 base::CancelableTaskTracker* cancelable_tracker,
672 Profile* profile)
673 : FaviconServiceTask(favicon_service, cancelable_tracker, profile) {}
675 favicon_base::FaviconRawBitmapResult Run(const GURL& url) {
676 float max_scale = ui::GetScaleForScaleFactor(
677 ResourceBundle::GetSharedInstance().GetMaxScaleFactor());
678 int desired_size_in_pixel = std::ceil(gfx::kFaviconSize * max_scale);
679 RunAsyncRequestOnUIThreadBlocking(
680 base::Bind(&FaviconService::GetRawFaviconForPageURL,
681 base::Unretained(service()),
682 url,
683 favicon_base::FAVICON | favicon_base::TOUCH_ICON,
684 desired_size_in_pixel,
685 base::Bind(&BookmarkIconFetchTask::OnFaviconRetrieved,
686 base::Unretained(this)),
687 cancelable_tracker()));
688 return result_;
691 private:
692 void OnFaviconRetrieved(
693 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
694 result_ = bitmap_result;
695 RequestCompleted();
698 favicon_base::FaviconRawBitmapResult result_;
700 DISALLOW_COPY_AND_ASSIGN(BookmarkIconFetchTask);
703 // Base class for all asynchronous blocking tasks that use the Android history
704 // provider service.
705 class HistoryProviderTask
706 : public AsyncServiceRequest<AndroidHistoryProviderService> {
707 public:
708 HistoryProviderTask(AndroidHistoryProviderService* service,
709 base::CancelableTaskTracker* cancelable_tracker)
710 : AsyncServiceRequest<AndroidHistoryProviderService>(service,
711 cancelable_tracker) {
714 private:
715 DISALLOW_COPY_AND_ASSIGN(HistoryProviderTask);
718 // Adds a bookmark from the API.
719 class AddBookmarkFromAPITask : public HistoryProviderTask {
720 public:
721 AddBookmarkFromAPITask(AndroidHistoryProviderService* service,
722 base::CancelableTaskTracker* cancelable_tracker)
723 : HistoryProviderTask(service, cancelable_tracker) {}
725 history::URLID Run(const history::HistoryAndBookmarkRow& row) {
726 RunAsyncRequestOnUIThreadBlocking(
727 base::Bind(&AndroidHistoryProviderService::InsertHistoryAndBookmark,
728 base::Unretained(service()),
729 row,
730 base::Bind(&AddBookmarkFromAPITask::OnBookmarkInserted,
731 base::Unretained(this)),
732 cancelable_tracker()));
733 return result_;
736 private:
737 void OnBookmarkInserted(history::URLID id) {
738 // Note that here 0 means an invalid id.
739 // This is because it represents a SQLite database row id.
740 result_ = id;
741 RequestCompleted();
744 history::URLID result_;
746 DISALLOW_COPY_AND_ASSIGN(AddBookmarkFromAPITask);
749 // Queries bookmarks from the API.
750 class QueryBookmarksFromAPITask : public HistoryProviderTask {
751 public:
752 QueryBookmarksFromAPITask(AndroidHistoryProviderService* service,
753 base::CancelableTaskTracker* cancelable_tracker)
754 : HistoryProviderTask(service, cancelable_tracker), result_(NULL) {}
756 history::AndroidStatement* Run(
757 const std::vector<history::HistoryAndBookmarkRow::ColumnID>& projections,
758 const std::string& selection,
759 const std::vector<base::string16>& selection_args,
760 const std::string& sort_order) {
761 RunAsyncRequestOnUIThreadBlocking(
762 base::Bind(&AndroidHistoryProviderService::QueryHistoryAndBookmarks,
763 base::Unretained(service()),
764 projections,
765 selection,
766 selection_args,
767 sort_order,
768 base::Bind(&QueryBookmarksFromAPITask::OnBookmarksQueried,
769 base::Unretained(this)),
770 cancelable_tracker()));
771 return result_;
774 private:
775 void OnBookmarksQueried(history::AndroidStatement* statement) {
776 result_ = statement;
777 RequestCompleted();
780 history::AndroidStatement* result_;
782 DISALLOW_COPY_AND_ASSIGN(QueryBookmarksFromAPITask);
785 // Updates bookmarks from the API.
786 class UpdateBookmarksFromAPITask : public HistoryProviderTask {
787 public:
788 UpdateBookmarksFromAPITask(AndroidHistoryProviderService* service,
789 base::CancelableTaskTracker* cancelable_tracker)
790 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
792 int Run(const history::HistoryAndBookmarkRow& row,
793 const std::string& selection,
794 const std::vector<base::string16>& selection_args) {
795 RunAsyncRequestOnUIThreadBlocking(
796 base::Bind(&AndroidHistoryProviderService::UpdateHistoryAndBookmarks,
797 base::Unretained(service()),
798 row,
799 selection,
800 selection_args,
801 base::Bind(&UpdateBookmarksFromAPITask::OnBookmarksUpdated,
802 base::Unretained(this)),
803 cancelable_tracker()));
804 return result_;
807 private:
808 void OnBookmarksUpdated(int updated_row_count) {
809 result_ = updated_row_count;
810 RequestCompleted();
813 int result_;
815 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarksFromAPITask);
818 // Removes bookmarks from the API.
819 class RemoveBookmarksFromAPITask : public HistoryProviderTask {
820 public:
821 RemoveBookmarksFromAPITask(AndroidHistoryProviderService* service,
822 base::CancelableTaskTracker* cancelable_tracker)
823 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
825 int Run(const std::string& selection,
826 const std::vector<base::string16>& selection_args) {
827 RunAsyncRequestOnUIThreadBlocking(
828 base::Bind(&AndroidHistoryProviderService::DeleteHistoryAndBookmarks,
829 base::Unretained(service()),
830 selection,
831 selection_args,
832 base::Bind(&RemoveBookmarksFromAPITask::OnBookmarksRemoved,
833 base::Unretained(this)),
834 cancelable_tracker()));
835 return result_;
838 private:
839 void OnBookmarksRemoved(int removed_row_count) {
840 result_ = removed_row_count;
841 RequestCompleted();
844 int result_;
846 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarksFromAPITask);
849 // Removes history from the API.
850 class RemoveHistoryFromAPITask : public HistoryProviderTask {
851 public:
852 RemoveHistoryFromAPITask(AndroidHistoryProviderService* service,
853 base::CancelableTaskTracker* cancelable_tracker)
854 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
856 int Run(const std::string& selection,
857 const std::vector<base::string16>& selection_args) {
858 RunAsyncRequestOnUIThreadBlocking(
859 base::Bind(&AndroidHistoryProviderService::DeleteHistory,
860 base::Unretained(service()),
861 selection,
862 selection_args,
863 base::Bind(&RemoveHistoryFromAPITask::OnHistoryRemoved,
864 base::Unretained(this)),
865 cancelable_tracker()));
866 return result_;
869 private:
870 void OnHistoryRemoved(int removed_row_count) {
871 result_ = removed_row_count;
872 RequestCompleted();
875 int result_;
877 DISALLOW_COPY_AND_ASSIGN(RemoveHistoryFromAPITask);
880 // This class provides the common method for the SearchTermAPIHelper.
881 class SearchTermTask : public HistoryProviderTask {
882 protected:
883 SearchTermTask(AndroidHistoryProviderService* service,
884 base::CancelableTaskTracker* cancelable_tracker,
885 Profile* profile)
886 : HistoryProviderTask(service, cancelable_tracker), profile_(profile) {}
888 // Fill SearchRow's keyword_id and url fields according the given
889 // search_term. Return true if succeeded.
890 void BuildSearchRow(history::SearchRow* row) {
891 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
893 TemplateURLService* template_service =
894 TemplateURLServiceFactory::GetForProfile(profile_);
895 template_service->Load();
897 const TemplateURL* search_engine =
898 template_service->GetDefaultSearchProvider();
899 if (search_engine) {
900 const TemplateURLRef* search_url = &search_engine->url_ref();
901 TemplateURLRef::SearchTermsArgs search_terms_args(row->search_term());
902 search_terms_args.append_extra_query_params = true;
903 std::string url = search_url->ReplaceSearchTerms(
904 search_terms_args, template_service->search_terms_data());
905 if (!url.empty()) {
906 row->set_url(GURL(url));
907 row->set_keyword_id(search_engine->id());
912 private:
913 Profile* profile_;
915 DISALLOW_COPY_AND_ASSIGN(SearchTermTask);
918 // Adds a search term from the API.
919 class AddSearchTermFromAPITask : public SearchTermTask {
920 public:
921 AddSearchTermFromAPITask(AndroidHistoryProviderService* service,
922 base::CancelableTaskTracker* cancelable_tracker,
923 Profile* profile)
924 : SearchTermTask(service, cancelable_tracker, profile) {}
926 history::URLID Run(const history::SearchRow& row) {
927 RunAsyncRequestOnUIThreadBlocking(
928 base::Bind(&AddSearchTermFromAPITask::MakeRequestOnUIThread,
929 base::Unretained(this), row));
930 return result_;
933 private:
934 void MakeRequestOnUIThread(const history::SearchRow& row) {
935 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
936 history::SearchRow internal_row = row;
937 BuildSearchRow(&internal_row);
938 service()->InsertSearchTerm(
939 internal_row,
940 base::Bind(&AddSearchTermFromAPITask::OnSearchTermInserted,
941 base::Unretained(this)),
942 cancelable_tracker());
945 void OnSearchTermInserted(history::URLID id) {
946 // Note that here 0 means an invalid id.
947 // This is because it represents a SQLite database row id.
948 result_ = id;
949 RequestCompleted();
952 history::URLID result_;
954 DISALLOW_COPY_AND_ASSIGN(AddSearchTermFromAPITask);
957 // Queries search terms from the API.
958 class QuerySearchTermsFromAPITask : public SearchTermTask {
959 public:
960 QuerySearchTermsFromAPITask(AndroidHistoryProviderService* service,
961 base::CancelableTaskTracker* cancelable_tracker,
962 Profile* profile)
963 : SearchTermTask(service, cancelable_tracker, profile), result_(NULL) {}
965 history::AndroidStatement* Run(
966 const std::vector<history::SearchRow::ColumnID>& projections,
967 const std::string& selection,
968 const std::vector<base::string16>& selection_args,
969 const std::string& sort_order) {
970 RunAsyncRequestOnUIThreadBlocking(base::Bind(
971 &AndroidHistoryProviderService::QuerySearchTerms,
972 base::Unretained(service()),
973 projections,
974 selection,
975 selection_args,
976 sort_order,
977 base::Bind(&QuerySearchTermsFromAPITask::OnSearchTermsQueried,
978 base::Unretained(this)),
979 cancelable_tracker()));
980 return result_;
983 private:
984 // Callback to return the result.
985 void OnSearchTermsQueried(history::AndroidStatement* statement) {
986 result_ = statement;
987 RequestCompleted();
990 history::AndroidStatement* result_;
992 DISALLOW_COPY_AND_ASSIGN(QuerySearchTermsFromAPITask);
995 // Updates search terms from the API.
996 class UpdateSearchTermsFromAPITask : public SearchTermTask {
997 public:
998 UpdateSearchTermsFromAPITask(AndroidHistoryProviderService* service,
999 base::CancelableTaskTracker* cancelable_tracker,
1000 Profile* profile)
1001 : SearchTermTask(service, cancelable_tracker, profile), result_(0) {}
1003 int Run(const history::SearchRow& row,
1004 const std::string& selection,
1005 const std::vector<base::string16>& selection_args) {
1006 RunAsyncRequestOnUIThreadBlocking(
1007 base::Bind(&UpdateSearchTermsFromAPITask::MakeRequestOnUIThread,
1008 base::Unretained(this), row, selection, selection_args));
1009 return result_;
1012 private:
1013 void MakeRequestOnUIThread(
1014 const history::SearchRow& row,
1015 const std::string& selection,
1016 const std::vector<base::string16>& selection_args) {
1017 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1018 history::SearchRow internal_row = row;
1019 BuildSearchRow(&internal_row);
1020 service()->UpdateSearchTerms(
1021 internal_row,
1022 selection,
1023 selection_args,
1024 base::Bind(&UpdateSearchTermsFromAPITask::OnSearchTermsUpdated,
1025 base::Unretained(this)),
1026 cancelable_tracker());
1029 void OnSearchTermsUpdated(int updated_row_count) {
1030 result_ = updated_row_count;
1031 RequestCompleted();
1034 int result_;
1036 DISALLOW_COPY_AND_ASSIGN(UpdateSearchTermsFromAPITask);
1039 // Removes search terms from the API.
1040 class RemoveSearchTermsFromAPITask : public SearchTermTask {
1041 public:
1042 RemoveSearchTermsFromAPITask(AndroidHistoryProviderService* service,
1043 base::CancelableTaskTracker* cancelable_tracker,
1044 Profile* profile)
1045 : SearchTermTask(service, cancelable_tracker, profile), result_() {}
1047 int Run(const std::string& selection,
1048 const std::vector<base::string16>& selection_args) {
1049 RunAsyncRequestOnUIThreadBlocking(base::Bind(
1050 &AndroidHistoryProviderService::DeleteSearchTerms,
1051 base::Unretained(service()),
1052 selection,
1053 selection_args,
1054 base::Bind(&RemoveSearchTermsFromAPITask::OnSearchTermsDeleted,
1055 base::Unretained(this)),
1056 cancelable_tracker()));
1057 return result_;
1060 private:
1061 void OnSearchTermsDeleted(int deleted_row_count) {
1062 result_ = deleted_row_count;
1063 RequestCompleted();
1066 int result_;
1068 DISALLOW_COPY_AND_ASSIGN(RemoveSearchTermsFromAPITask);
1071 // ------------- Other utility methods (may use tasks) ------------- //
1073 // Fills the bookmark |row| with the given java objects.
1074 void FillBookmarkRow(JNIEnv* env,
1075 jobject obj,
1076 jstring url,
1077 jobject created,
1078 jobject isBookmark,
1079 jobject date,
1080 jbyteArray favicon,
1081 jstring title,
1082 jobject visits,
1083 jlong parent_id,
1084 history::HistoryAndBookmarkRow* row,
1085 BookmarkModel* model) {
1086 // Needed because of the internal bookmark model task invocation.
1087 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
1089 if (url) {
1090 base::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(base::UTF16ToUTF8(raw_url));
1100 if (created)
1101 row->set_created(ConvertJlongToTime(
1102 ConvertJLongObjectToPrimitive(env, created)));
1104 if (isBookmark)
1105 row->set_is_bookmark(ConvertJBooleanObjectToPrimitive(env, isBookmark));
1107 if (date)
1108 row->set_last_visit_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive(
1109 env, date)));
1111 if (favicon) {
1112 std::vector<uint8> bytes;
1113 base::android::JavaByteArrayToByteVector(env, favicon, &bytes);
1114 row->set_favicon(base::RefCountedBytes::TakeVector(&bytes));
1117 if (title)
1118 row->set_title(ConvertJavaStringToUTF16(env, title));
1120 if (visits)
1121 row->set_visit_count(ConvertJIntegerToJint(env, visits));
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);
1129 // Fills the bookmark |row| with the given java objects if it is not null.
1130 void FillSearchRow(JNIEnv* env,
1131 jobject obj,
1132 jstring search_term,
1133 jobject date,
1134 history::SearchRow* row) {
1135 if (search_term)
1136 row->set_search_term(ConvertJavaStringToUTF16(env, search_term));
1138 if (date)
1139 row->set_search_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive(
1140 env, date)));
1143 } // namespace
1145 // ------------- Native initialization and destruction ------------- //
1147 static jlong Init(JNIEnv* env, jobject obj) {
1148 ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj);
1149 return reinterpret_cast<intptr_t>(provider);
1152 bool ChromeBrowserProvider::RegisterChromeBrowserProvider(JNIEnv* env) {
1153 return RegisterNativesImpl(env);
1156 ChromeBrowserProvider::ChromeBrowserProvider(JNIEnv* env, jobject obj)
1157 : weak_java_provider_(env, obj),
1158 history_service_observer_(this),
1159 handling_extensive_changes_(false) {
1160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1161 profile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
1162 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
1163 top_sites_ = profile_->GetTopSites();
1164 service_.reset(new AndroidHistoryProviderService(profile_));
1165 favicon_service_.reset(FaviconServiceFactory::GetForProfile(profile_,
1166 Profile::EXPLICIT_ACCESS));
1168 // Registers the notifications we are interested.
1169 bookmark_model_->AddObserver(this);
1170 history_service_observer_.Add(
1171 HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS));
1172 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED,
1173 content::NotificationService::AllSources());
1174 notification_registrar_.Add(this,
1175 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
1176 content::NotificationService::AllSources());
1177 TemplateURLService* template_service =
1178 TemplateURLServiceFactory::GetForProfile(profile_);
1179 if (!template_service->loaded())
1180 template_service->Load();
1183 ChromeBrowserProvider::~ChromeBrowserProvider() {
1184 bookmark_model_->RemoveObserver(this);
1187 void ChromeBrowserProvider::Destroy(JNIEnv*, jobject) {
1188 delete this;
1191 // ------------- Provider public APIs ------------- //
1193 jlong ChromeBrowserProvider::AddBookmark(JNIEnv* env,
1194 jobject,
1195 jstring jurl,
1196 jstring jtitle,
1197 jboolean is_folder,
1198 jlong parent_id) {
1199 base::string16 url;
1200 if (jurl)
1201 url = ConvertJavaStringToUTF16(env, jurl);
1202 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1204 AddBookmarkTask task(bookmark_model_);
1205 return task.Run(title, url, is_folder, parent_id);
1208 jint ChromeBrowserProvider::RemoveBookmark(JNIEnv*, jobject, jlong id) {
1209 RemoveBookmarkTask task(bookmark_model_);
1210 return task.Run(id);
1213 jint ChromeBrowserProvider::UpdateBookmark(JNIEnv* env,
1214 jobject,
1215 jlong id,
1216 jstring jurl,
1217 jstring jtitle,
1218 jlong parent_id) {
1219 base::string16 url;
1220 if (jurl)
1221 url = ConvertJavaStringToUTF16(env, jurl);
1222 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1224 UpdateBookmarkTask task(bookmark_model_);
1225 return task.Run(id, title, url, parent_id);
1228 // Add the bookmark with the given column values.
1229 jlong ChromeBrowserProvider::AddBookmarkFromAPI(JNIEnv* env,
1230 jobject obj,
1231 jstring url,
1232 jobject created,
1233 jobject isBookmark,
1234 jobject date,
1235 jbyteArray favicon,
1236 jstring title,
1237 jobject visits,
1238 jlong parent_id) {
1239 DCHECK(url);
1241 history::HistoryAndBookmarkRow row;
1242 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title,
1243 visits, parent_id, &row, bookmark_model_);
1245 // URL must be valid.
1246 if (row.url().is_empty()) {
1247 LOG(ERROR) << "Not a valid URL " << row.raw_url();
1248 return kInvalidContentProviderId;
1251 AddBookmarkFromAPITask task(service_.get(), &cancelable_task_tracker_);
1252 return task.Run(row);
1255 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QueryBookmarkFromAPI(
1256 JNIEnv* env,
1257 jobject obj,
1258 jobjectArray projection,
1259 jstring selections,
1260 jobjectArray selection_args,
1261 jstring sort_order) {
1262 // Converts the projection to array of ColumnID and column name.
1263 // Used to store the projection column ID according their sequence.
1264 std::vector<history::HistoryAndBookmarkRow::ColumnID> query_columns;
1265 // Used to store the projection column names according their sequence.
1266 std::vector<std::string> columns_name;
1267 if (projection) {
1268 jsize len = env->GetArrayLength(projection);
1269 for (int i = 0; i < len; i++) {
1270 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>(
1271 env->GetObjectArrayElement(projection, i)));
1272 history::HistoryAndBookmarkRow::ColumnID id =
1273 history::HistoryAndBookmarkRow::GetColumnID(name);
1274 if (id == history::HistoryAndBookmarkRow::COLUMN_END) {
1275 // Ignore the unknown column; As Android platform will send us
1276 // the non public column.
1277 continue;
1279 query_columns.push_back(id);
1280 columns_name.push_back(name);
1284 std::vector<base::string16> where_args =
1285 ConvertJStringArrayToString16Array(env, selection_args);
1287 std::string where_clause;
1288 if (selections) {
1289 where_clause = ConvertJavaStringToUTF8(env, selections);
1292 std::string sort_clause;
1293 if (sort_order) {
1294 sort_clause = ConvertJavaStringToUTF8(env, sort_order);
1297 QueryBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1298 history::AndroidStatement* statement = task.Run(
1299 query_columns, where_clause, where_args, sort_clause);
1300 if (!statement)
1301 return ScopedJavaLocalRef<jobject>();
1303 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor
1304 // Java object.
1305 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement,
1306 service_.get(), favicon_service_.get());
1309 // Updates the bookmarks with the given column values. The value is not given if
1310 // it is NULL.
1311 jint ChromeBrowserProvider::UpdateBookmarkFromAPI(JNIEnv* env,
1312 jobject obj,
1313 jstring url,
1314 jobject created,
1315 jobject isBookmark,
1316 jobject date,
1317 jbyteArray favicon,
1318 jstring title,
1319 jobject visits,
1320 jlong parent_id,
1321 jstring selections,
1322 jobjectArray selection_args) {
1323 history::HistoryAndBookmarkRow row;
1324 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title,
1325 visits, parent_id, &row, bookmark_model_);
1327 std::vector<base::string16> where_args =
1328 ConvertJStringArrayToString16Array(env, selection_args);
1330 std::string where_clause;
1331 if (selections)
1332 where_clause = ConvertJavaStringToUTF8(env, selections);
1334 UpdateBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1335 return task.Run(row, where_clause, where_args);
1338 jint ChromeBrowserProvider::RemoveBookmarkFromAPI(JNIEnv* env,
1339 jobject obj,
1340 jstring selections,
1341 jobjectArray selection_args) {
1342 std::vector<base::string16> where_args =
1343 ConvertJStringArrayToString16Array(env, selection_args);
1345 std::string where_clause;
1346 if (selections)
1347 where_clause = ConvertJavaStringToUTF8(env, selections);
1349 RemoveBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1350 return task.Run(where_clause, where_args);
1353 jint ChromeBrowserProvider::RemoveHistoryFromAPI(JNIEnv* env,
1354 jobject obj,
1355 jstring selections,
1356 jobjectArray selection_args) {
1357 std::vector<base::string16> where_args =
1358 ConvertJStringArrayToString16Array(env, selection_args);
1360 std::string where_clause;
1361 if (selections)
1362 where_clause = ConvertJavaStringToUTF8(env, selections);
1364 RemoveHistoryFromAPITask task(service_.get(), &cancelable_task_tracker_);
1365 return task.Run(where_clause, where_args);
1368 // Add the search term with the given column values. The value is not given if
1369 // it is NULL.
1370 jlong ChromeBrowserProvider::AddSearchTermFromAPI(JNIEnv* env,
1371 jobject obj,
1372 jstring search_term,
1373 jobject date) {
1374 DCHECK(search_term);
1376 history::SearchRow row;
1377 FillSearchRow(env, obj, search_term, date, &row);
1379 // URL must be valid.
1380 if (row.search_term().empty()) {
1381 LOG(ERROR) << "Search term is empty.";
1382 return kInvalidContentProviderId;
1385 AddSearchTermFromAPITask task(service_.get(),
1386 &cancelable_task_tracker_,
1387 profile_);
1388 return task.Run(row);
1391 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QuerySearchTermFromAPI(
1392 JNIEnv* env,
1393 jobject obj,
1394 jobjectArray projection,
1395 jstring selections,
1396 jobjectArray selection_args,
1397 jstring sort_order) {
1398 // Converts the projection to array of ColumnID and column name.
1399 // Used to store the projection column ID according their sequence.
1400 std::vector<history::SearchRow::ColumnID> query_columns;
1401 // Used to store the projection column names according their sequence.
1402 std::vector<std::string> columns_name;
1403 if (projection) {
1404 jsize len = env->GetArrayLength(projection);
1405 for (int i = 0; i < len; i++) {
1406 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>(
1407 env->GetObjectArrayElement(projection, i)));
1408 history::SearchRow::ColumnID id =
1409 history::SearchRow::GetColumnID(name);
1410 if (id == history::SearchRow::COLUMN_END) {
1411 LOG(ERROR) << "Can not find " << name;
1412 return ScopedJavaLocalRef<jobject>();
1414 query_columns.push_back(id);
1415 columns_name.push_back(name);
1419 std::vector<base::string16> where_args =
1420 ConvertJStringArrayToString16Array(env, selection_args);
1422 std::string where_clause;
1423 if (selections) {
1424 where_clause = ConvertJavaStringToUTF8(env, selections);
1427 std::string sort_clause;
1428 if (sort_order) {
1429 sort_clause = ConvertJavaStringToUTF8(env, sort_order);
1432 QuerySearchTermsFromAPITask task(service_.get(),
1433 &cancelable_task_tracker_,
1434 profile_);
1435 history::AndroidStatement* statement = task.Run(
1436 query_columns, where_clause, where_args, sort_clause);
1437 if (!statement)
1438 return ScopedJavaLocalRef<jobject>();
1439 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor
1440 // Java object.
1441 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement,
1442 service_.get(), favicon_service_.get());
1445 // Updates the search terms with the given column values. The value is not
1446 // given if it is NULL.
1447 jint ChromeBrowserProvider::UpdateSearchTermFromAPI(
1448 JNIEnv* env, jobject obj, jstring search_term, jobject date,
1449 jstring selections, jobjectArray selection_args) {
1450 history::SearchRow row;
1451 FillSearchRow(env, obj, search_term, date, &row);
1453 std::vector<base::string16> where_args = ConvertJStringArrayToString16Array(
1454 env, selection_args);
1456 std::string where_clause;
1457 if (selections)
1458 where_clause = ConvertJavaStringToUTF8(env, selections);
1460 UpdateSearchTermsFromAPITask task(service_.get(),
1461 &cancelable_task_tracker_,
1462 profile_);
1463 return task.Run(row, where_clause, where_args);
1466 jint ChromeBrowserProvider::RemoveSearchTermFromAPI(
1467 JNIEnv* env, jobject obj, jstring selections, jobjectArray selection_args) {
1468 std::vector<base::string16> where_args =
1469 ConvertJStringArrayToString16Array(env, selection_args);
1471 std::string where_clause;
1472 if (selections)
1473 where_clause = ConvertJavaStringToUTF8(env, selections);
1475 RemoveSearchTermsFromAPITask task(service_.get(),
1476 &cancelable_task_tracker_,
1477 profile_);
1478 return task.Run(where_clause, where_args);
1481 // ------------- Provider custom APIs ------------- //
1483 jboolean ChromeBrowserProvider::BookmarkNodeExists(
1484 JNIEnv* env,
1485 jobject obj,
1486 jlong id) {
1487 BookmarkNodeExistsTask task(bookmark_model_);
1488 return task.Run(id);
1491 jlong ChromeBrowserProvider::CreateBookmarksFolderOnce(
1492 JNIEnv* env,
1493 jobject obj,
1494 jstring jtitle,
1495 jlong parent_id) {
1496 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1497 if (title.empty())
1498 return kInvalidBookmarkId;
1500 CreateBookmarksFolderOnceTask task(bookmark_model_);
1501 return task.Run(title, parent_id);
1504 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetEditableBookmarkFolders(
1505 JNIEnv* env,
1506 jobject obj) {
1507 ScopedJavaGlobalRef<jobject> jroot;
1508 ChromeBookmarkClient* client =
1509 ChromeBookmarkClientFactory::GetForProfile(profile_);
1510 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
1511 GetEditableBookmarkFoldersTask task(client, model);
1512 task.Run(&jroot);
1513 return ScopedJavaLocalRef<jobject>(jroot);
1516 void ChromeBrowserProvider::RemoveAllUserBookmarks(JNIEnv* env, jobject obj) {
1517 RemoveAllUserBookmarksTask task(bookmark_model_);
1518 task.Run();
1521 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetBookmarkNode(
1522 JNIEnv* env, jobject obj, jlong id, jboolean get_parent,
1523 jboolean get_children) {
1524 ScopedJavaGlobalRef<jobject> jnode;
1525 GetBookmarkNodeTask task(bookmark_model_);
1526 task.Run(id, get_parent, get_children, &jnode);
1527 return ScopedJavaLocalRef<jobject>(jnode);
1530 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetMobileBookmarksFolder(
1531 JNIEnv* env,
1532 jobject obj) {
1533 ScopedJavaGlobalRef<jobject> jnode;
1534 GetMobileBookmarksNodeTask task(bookmark_model_);
1535 ConvertBookmarkNode(task.Run(), ScopedJavaLocalRef<jobject>(), &jnode);
1536 return ScopedJavaLocalRef<jobject>(jnode);
1539 jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch(
1540 JNIEnv* env,
1541 jobject obj,
1542 jlong id) {
1543 IsInMobileBookmarksBranchTask task(bookmark_model_);
1544 return task.Run(id);
1547 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetFaviconOrTouchIcon(
1548 JNIEnv* env, jobject obj, jstring jurl) {
1549 if (!jurl)
1550 return ScopedJavaLocalRef<jbyteArray>();
1552 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1553 BookmarkIconFetchTask favicon_task(
1554 favicon_service_.get(), &cancelable_task_tracker_, profile_);
1555 favicon_base::FaviconRawBitmapResult bitmap_result = favicon_task.Run(url);
1557 if (!bitmap_result.is_valid() || !bitmap_result.bitmap_data.get())
1558 return ScopedJavaLocalRef<jbyteArray>();
1560 return base::android::ToJavaByteArray(env, bitmap_result.bitmap_data->front(),
1561 bitmap_result.bitmap_data->size());
1564 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail(
1565 JNIEnv* env, jobject obj, jstring jurl) {
1566 if (!jurl)
1567 return ScopedJavaLocalRef<jbyteArray>();
1568 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1570 // GetPageThumbnail is synchronous and can be called from any thread.
1571 scoped_refptr<base::RefCountedMemory> thumbnail;
1572 if (top_sites_)
1573 top_sites_->GetPageThumbnail(url, false, &thumbnail);
1575 if (!thumbnail.get() || !thumbnail->front()) {
1576 return ScopedJavaLocalRef<jbyteArray>();
1579 return base::android::ToJavaByteArray(env, thumbnail->front(),
1580 thumbnail->size());
1583 // ------------- Observer-related methods ------------- //
1585 void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning(
1586 BookmarkModel* model) {
1587 handling_extensive_changes_ = true;
1590 void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded(
1591 BookmarkModel* model) {
1592 handling_extensive_changes_ = false;
1593 BookmarkModelChanged();
1596 void ChromeBrowserProvider::BookmarkModelChanged() {
1597 if (handling_extensive_changes_)
1598 return;
1600 JNIEnv* env = AttachCurrentThread();
1601 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1602 if (obj.is_null())
1603 return;
1605 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj());
1608 void ChromeBrowserProvider::OnHistoryChanged() {
1609 JNIEnv* env = AttachCurrentThread();
1610 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1611 if (obj.is_null())
1612 return;
1613 Java_ChromeBrowserProvider_onHistoryChanged(env, obj.obj());
1616 void ChromeBrowserProvider::OnURLVisited(HistoryService* history_service,
1617 ui::PageTransition transition,
1618 const history::URLRow& row,
1619 const history::RedirectList& redirects,
1620 base::Time visit_time) {
1621 OnHistoryChanged();
1624 void ChromeBrowserProvider::Observe(
1625 int type,
1626 const content::NotificationSource& source,
1627 const content::NotificationDetails& details) {
1628 if (type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
1629 OnHistoryChanged();
1630 } else if (type ==
1631 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED) {
1632 JNIEnv* env = AttachCurrentThread();
1633 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1634 if (obj.is_null())
1635 return;
1636 Java_ChromeBrowserProvider_onSearchTermChanged(env, obj.obj());