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"
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/cancelable_task_tracker.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/android/provider/blocking_ui_thread_async_request.h"
20 #include "chrome/browser/android/provider/bookmark_model_observer_task.h"
21 #include "chrome/browser/android/provider/run_on_ui_thread_blocking.h"
22 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
23 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
24 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/favicon/favicon_service.h"
28 #include "chrome/browser/favicon/favicon_service_factory.h"
29 #include "chrome/browser/history/android/sqlite_cursor.h"
30 #include "chrome/browser/history/top_sites.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/profiles/profile_manager.h"
33 #include "chrome/browser/search_engines/template_url_service_factory.h"
34 #include "components/bookmarks/browser/bookmark_model.h"
35 #include "components/bookmarks/browser/bookmark_utils.h"
36 #include "components/history/core/android/android_history_types.h"
37 #include "components/search_engines/template_url.h"
38 #include "components/search_engines/template_url_service.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "jni/ChromeBrowserProvider_jni.h"
42 #include "sql/statement.h"
43 #include "ui/base/layout.h"
44 #include "ui/base/resource/resource_bundle.h"
45 #include "ui/gfx/favicon_size.h"
47 using base::android::AttachCurrentThread;
48 using base::android::CheckException;
49 using base::android::ClearException;
50 using base::android::ConvertJavaStringToUTF16;
51 using base::android::ConvertJavaStringToUTF8;
52 using base::android::ConvertUTF8ToJavaString;
53 using base::android::ConvertUTF16ToJavaString;
54 using base::android::GetClass;
55 using base::android::MethodID;
56 using base::android::JavaRef;
57 using base::android::ScopedJavaGlobalRef;
58 using base::android::ScopedJavaLocalRef;
59 using content::BrowserThread;
61 // After refactoring the following class hierarchy has been created in order
62 // to avoid repeating code again for the same basic kind of tasks, to enforce
63 // the correct thread usage and to prevent known race conditions and deadlocks.
65 // - RunOnUIThreadBlocking: auxiliary class to run methods in the UI thread
66 // blocking the current one until finished. Because of the provider threading
67 // expectations this cannot be used from the UI thread.
69 // - BookmarkModelTask: base class for all tasks that operate in any way with
70 // the bookmark model. This class ensures that the model is loaded and
71 // prevents possible deadlocks. Derived classes should make use of
72 // RunOnUIThreadBlocking to perform any manipulation of the bookmark model in
73 // the UI thread. The Run method of these tasks cannot be invoked directly
74 // from the UI thread, but RunOnUIThread can be safely used from the UI
75 // thread code of other BookmarkModelTasks.
77 // - AsyncServiceRequest: base class for any asynchronous requests made to a
78 // Chromium service that require to block the current thread until completed.
79 // Derived classes should make use of RunAsyncRequestOnUIThreadBlocking to
80 // post their requests in the UI thread and return the results synchronously.
81 // All derived classes MUST ALWAYS call RequestCompleted when receiving the
82 // request response. These tasks cannot be invoked from the UI thread.
84 // - FaviconServiceTask: base class for asynchronous requests that make use of
85 // Chromium's favicon service. See AsyncServiceRequest for more details.
87 // - HistoryProviderTask: base class for asynchronous requests that make use of
88 // AndroidHistoryProviderService. See AsyncServiceRequest for mode details.
90 // - SearchTermTask: base class for asynchronous requests that involve the
91 // search term API. Works in the same way as HistoryProviderTask.
95 const char kDefaultUrlScheme[] = "http://";
96 const int64 kInvalidContentProviderId = 0;
97 const int64 kInvalidBookmarkId = -1;
99 // ------------- Java-related utility methods ------------- //
101 // Convert a BookmarkNode, |node|, to the java representation of a bookmark node
102 // stored in |*jnode|. Parent node information is optional.
103 void ConvertBookmarkNode(
104 const BookmarkNode* node,
105 const JavaRef<jobject>& parent_node,
106 ScopedJavaGlobalRef<jobject>* jnode) {
111 JNIEnv* env = AttachCurrentThread();
112 ScopedJavaLocalRef<jstring> url;
114 url.Reset(ConvertUTF8ToJavaString(env, node->url().spec()));
115 ScopedJavaLocalRef<jstring> title(
116 ConvertUTF16ToJavaString(env, node->GetTitle()));
119 Java_BookmarkNode_create(
120 env, node->id(), (jint) node->type(), title.obj(), url.obj(),
124 jlong ConvertJLongObjectToPrimitive(JNIEnv* env, jobject long_obj) {
125 ScopedJavaLocalRef<jclass> jlong_clazz = GetClass(env, "java/lang/Long");
126 jmethodID long_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
127 env, jlong_clazz.obj(), "longValue", "()J");
128 return env->CallLongMethod(long_obj, long_value, NULL);
131 jboolean ConvertJBooleanObjectToPrimitive(JNIEnv* env, jobject boolean_object) {
132 ScopedJavaLocalRef<jclass> jboolean_clazz =
133 GetClass(env, "java/lang/Boolean");
134 jmethodID boolean_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
135 env, jboolean_clazz.obj(), "booleanValue", "()Z");
136 return env->CallBooleanMethod(boolean_object, boolean_value, NULL);
139 base::Time ConvertJlongToTime(jlong value) {
140 return base::Time::UnixEpoch() +
141 base::TimeDelta::FromMilliseconds((int64)value);
144 jint ConvertJIntegerToJint(JNIEnv* env, jobject integer_obj) {
145 ScopedJavaLocalRef<jclass> jinteger_clazz =
146 GetClass(env, "java/lang/Integer");
147 jmethodID int_value = MethodID::Get<MethodID::TYPE_INSTANCE>(
148 env, jinteger_clazz.obj(), "intValue", "()I");
149 return env->CallIntMethod(integer_obj, int_value, NULL);
152 std::vector<base::string16> ConvertJStringArrayToString16Array(
154 jobjectArray array) {
155 std::vector<base::string16> results;
157 jsize len = env->GetArrayLength(array);
158 for (int i = 0; i < len; i++) {
159 results.push_back(ConvertJavaStringToUTF16(env,
160 static_cast<jstring>(env->GetObjectArrayElement(array, i))));
166 // ------------- Utility methods used by tasks ------------- //
168 // Parse the given url and return a GURL, appending the default scheme
169 // if one is not present.
170 GURL ParseAndMaybeAppendScheme(const base::string16& url,
171 const char* default_scheme) {
173 if (!gurl.is_valid() && !gurl.has_scheme()) {
174 base::string16 refined_url(base::ASCIIToUTF16(default_scheme));
175 refined_url.append(url);
176 gurl = GURL(refined_url);
181 const BookmarkNode* GetChildFolderByTitle(const BookmarkNode* parent,
182 const base::string16& title) {
183 for (int i = 0; i < parent->child_count(); ++i) {
184 if (parent->GetChild(i)->is_folder() &&
185 parent->GetChild(i)->GetTitle() == title) {
186 return parent->GetChild(i);
192 // ------------- Synchronous task classes ------------- //
194 // Utility task to add a bookmark.
195 class AddBookmarkTask : public BookmarkModelTask {
197 explicit AddBookmarkTask(BookmarkModel* model) : BookmarkModelTask(model) {}
199 int64 Run(const base::string16& title,
200 const base::string16& url,
201 const bool is_folder,
202 const int64 parent_id) {
203 int64 result = kInvalidBookmarkId;
204 RunOnUIThreadBlocking::Run(
205 base::Bind(&AddBookmarkTask::RunOnUIThread,
206 model(), title, url, is_folder, parent_id, &result));
210 static void RunOnUIThread(BookmarkModel* model,
211 const base::string16& title,
212 const base::string16& url,
213 const bool is_folder,
214 const int64 parent_id,
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218 GURL gurl = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme);
220 // Check if the bookmark already exists.
221 const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(gurl);
223 const BookmarkNode* parent_node = NULL;
225 parent_node = bookmarks::GetBookmarkNodeByID(model, parent_id);
227 parent_node = model->bookmark_bar_node();
230 node = model->AddFolder(parent_node, parent_node->child_count(), title);
232 node = model->AddURL(parent_node, 0, title, gurl);
235 *result = node ? node ->id() : kInvalidBookmarkId;
239 DISALLOW_COPY_AND_ASSIGN(AddBookmarkTask);
242 // Utility method to remove a bookmark.
243 class RemoveBookmarkTask : public BookmarkModelObserverTask {
245 explicit RemoveBookmarkTask(BookmarkModel* model)
246 : BookmarkModelObserverTask(model),
248 id_to_delete_(kInvalidBookmarkId) {}
249 virtual ~RemoveBookmarkTask() {}
251 int Run(const int64 id) {
253 RunOnUIThreadBlocking::Run(
254 base::Bind(&RemoveBookmarkTask::RunOnUIThread, model(), id));
258 static void RunOnUIThread(BookmarkModel* model, const int64 id) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
261 if (node && node->parent()) {
262 const BookmarkNode* parent_node = node->parent();
263 model->Remove(parent_node, parent_node->GetIndexOf(node));
267 // Verify that the bookmark was actually removed. Called synchronously.
268 virtual void BookmarkNodeRemoved(
269 BookmarkModel* bookmark_model,
270 const BookmarkNode* parent,
272 const BookmarkNode* node,
273 const std::set<GURL>& removed_urls) OVERRIDE {
274 if (bookmark_model == model() && node->id() == id_to_delete_)
282 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarkTask);
285 // Utility method to remove all bookmarks that the user can edit.
286 class RemoveAllUserBookmarksTask : public BookmarkModelObserverTask {
288 explicit RemoveAllUserBookmarksTask(BookmarkModel* model)
289 : BookmarkModelObserverTask(model) {}
291 virtual ~RemoveAllUserBookmarksTask() {}
294 RunOnUIThreadBlocking::Run(
295 base::Bind(&RemoveAllUserBookmarksTask::RunOnUIThread, model()));
298 static void RunOnUIThread(BookmarkModel* model) {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
300 model->RemoveAllUserBookmarks();
304 DISALLOW_COPY_AND_ASSIGN(RemoveAllUserBookmarksTask);
307 // Utility method to update a bookmark.
308 class UpdateBookmarkTask : public BookmarkModelObserverTask {
310 explicit UpdateBookmarkTask(BookmarkModel* model)
311 : BookmarkModelObserverTask(model),
313 id_to_update_(kInvalidBookmarkId){}
314 virtual ~UpdateBookmarkTask() {}
316 int Run(const int64 id,
317 const base::string16& title,
318 const base::string16& url,
319 const int64 parent_id) {
321 RunOnUIThreadBlocking::Run(
322 base::Bind(&UpdateBookmarkTask::RunOnUIThread,
323 model(), id, title, url, parent_id));
327 static void RunOnUIThread(BookmarkModel* model,
329 const base::string16& title,
330 const base::string16& url,
331 const int64 parent_id) {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
335 if (node->GetTitle() != title)
336 model->SetTitle(node, title);
338 if (node->type() == BookmarkNode::URL) {
339 GURL bookmark_url = ParseAndMaybeAppendScheme(url, kDefaultUrlScheme);
340 if (bookmark_url != node->url())
341 model->SetURL(node, bookmark_url);
344 if (parent_id >= 0 &&
345 (!node->parent() || parent_id != node->parent()->id())) {
346 const BookmarkNode* new_parent =
347 bookmarks::GetBookmarkNodeByID(model, parent_id);
350 model->Move(node, new_parent, 0);
355 // Verify that the bookmark was actually updated. Called synchronously.
356 virtual void BookmarkNodeChanged(BookmarkModel* bookmark_model,
357 const BookmarkNode* node) OVERRIDE {
358 if (bookmark_model == model() && node->id() == id_to_update_)
366 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarkTask);
369 // Checks if a node exists in the bookmark model.
370 class BookmarkNodeExistsTask : public BookmarkModelTask {
372 explicit BookmarkNodeExistsTask(BookmarkModel* model)
373 : BookmarkModelTask(model) {
376 bool Run(const int64 id) {
378 RunOnUIThreadBlocking::Run(
379 base::Bind(&BookmarkNodeExistsTask::RunOnUIThread,
380 model(), id, &result));
384 static void RunOnUIThread(BookmarkModel* model,
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 *result = bookmarks::GetBookmarkNodeByID(model, id) != NULL;
393 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeExistsTask);
396 // Checks if a node belongs to the Mobile Bookmarks hierarchy branch.
397 class IsInMobileBookmarksBranchTask : public BookmarkModelTask {
399 explicit IsInMobileBookmarksBranchTask(BookmarkModel* model)
400 : BookmarkModelTask(model) {}
402 bool Run(const int64 id) {
404 RunOnUIThreadBlocking::Run(
405 base::Bind(&IsInMobileBookmarksBranchTask::RunOnUIThread,
406 model(), id, &result));
410 static void RunOnUIThread(BookmarkModel* model,
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
416 const BookmarkNode* mobile_node = model->mobile_node();
417 while (node && node != mobile_node)
418 node = node->parent();
420 *result = node == mobile_node;
424 DISALLOW_COPY_AND_ASSIGN(IsInMobileBookmarksBranchTask);
427 // Creates folder or retrieves its id if already exists.
428 // An invalid parent id is assumed to represent the Mobile Bookmarks folder.
429 // Can only be used to create folders inside the Mobile Bookmarks branch.
430 class CreateBookmarksFolderOnceTask : public BookmarkModelTask {
432 explicit CreateBookmarksFolderOnceTask(BookmarkModel* model)
433 : BookmarkModelTask(model) {}
435 int64 Run(const base::string16& title, const int64 parent_id) {
436 int64 result = kInvalidBookmarkId;
437 RunOnUIThreadBlocking::Run(
438 base::Bind(&CreateBookmarksFolderOnceTask::RunOnUIThread,
439 model(), title, parent_id, &result));
443 static void RunOnUIThread(BookmarkModel* model,
444 const base::string16& title,
445 const int64 parent_id,
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 // Invalid ids are assumed to refer to the Mobile Bookmarks folder.
451 const BookmarkNode* parent =
452 parent_id >= 0 ? bookmarks::GetBookmarkNodeByID(model, parent_id)
453 : model->mobile_node();
456 bool in_mobile_bookmarks;
457 IsInMobileBookmarksBranchTask::RunOnUIThread(model, parent->id(),
458 &in_mobile_bookmarks);
459 if (!in_mobile_bookmarks) {
460 // The parent folder must be inside the Mobile Bookmarks folder.
461 *result = kInvalidBookmarkId;
465 const BookmarkNode* node = GetChildFolderByTitle(parent, title);
467 *result = node->id();
471 AddBookmarkTask::RunOnUIThread(model, title, base::string16(), true,
472 parent->id(), result);
476 DISALLOW_COPY_AND_ASSIGN(CreateBookmarksFolderOnceTask);
479 // Creates a Java BookmarkNode object for a node given its id.
480 class GetEditableBookmarkFoldersTask : public BookmarkModelTask {
482 GetEditableBookmarkFoldersTask(ChromeBookmarkClient* client,
483 BookmarkModel* model)
484 : BookmarkModelTask(model), client_(client) {}
486 void Run(ScopedJavaGlobalRef<jobject>* jroot) {
487 RunOnUIThreadBlocking::Run(
488 base::Bind(&GetEditableBookmarkFoldersTask::RunOnUIThread,
489 client_, model(), jroot));
492 static void RunOnUIThread(ChromeBookmarkClient* client,
493 BookmarkModel* model,
494 ScopedJavaGlobalRef<jobject>* jroot) {
495 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
496 const BookmarkNode* root = model->root_node();
497 if (!root || !root->is_folder())
500 // The iterative approach is not possible because ScopedGlobalJavaRefs
501 // cannot be copy-constructed, and therefore not used in STL containers.
502 ConvertFolderSubtree(client, AttachCurrentThread(), root,
503 ScopedJavaLocalRef<jobject>(), jroot);
507 static void ConvertFolderSubtree(ChromeBookmarkClient* client,
509 const BookmarkNode* node,
510 const JavaRef<jobject>& parent_folder,
511 ScopedJavaGlobalRef<jobject>* jfolder) {
513 DCHECK(node->is_folder());
516 // Global refs should be used here for thread-safety reasons as this task
517 // might be invoked from a thread other than UI. All refs are scoped.
518 ConvertBookmarkNode(node, parent_folder, jfolder);
520 for (int i = 0; i < node->child_count(); ++i) {
521 const BookmarkNode* child = node->GetChild(i);
522 if (child->is_folder() && client->CanBeEditedByUser(child)) {
523 ScopedJavaGlobalRef<jobject> jchild;
524 ConvertFolderSubtree(client, env, child, *jfolder, &jchild);
526 Java_BookmarkNode_addChild(env, jfolder->obj(), jchild.obj());
527 if (ClearException(env)) {
528 LOG(WARNING) << "Java exception while adding child node.";
535 ChromeBookmarkClient* client_;
537 DISALLOW_COPY_AND_ASSIGN(GetEditableBookmarkFoldersTask);
540 // Creates a Java BookmarkNode object for a node given its id.
541 class GetBookmarkNodeTask : public BookmarkModelTask {
543 explicit GetBookmarkNodeTask(BookmarkModel* model)
544 : BookmarkModelTask(model) {
547 void Run(const int64 id,
550 ScopedJavaGlobalRef<jobject>* jnode) {
551 return RunOnUIThreadBlocking::Run(
552 base::Bind(&GetBookmarkNodeTask::RunOnUIThread,
553 model(), id, get_parent, get_children, jnode));
556 static void RunOnUIThread(BookmarkModel* model,
560 ScopedJavaGlobalRef<jobject>* jnode) {
561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
562 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, id);
566 ScopedJavaGlobalRef<jobject> jparent;
568 ConvertBookmarkNode(node->parent(), ScopedJavaLocalRef<jobject>(),
572 ConvertBookmarkNode(node, jparent, jnode);
574 JNIEnv* env = AttachCurrentThread();
575 if (!jparent.is_null()) {
576 Java_BookmarkNode_addChild(env, jparent.obj(), jnode->obj());
577 if (ClearException(env)) {
578 LOG(WARNING) << "Java exception while adding child node.";
584 for (int i = 0; i < node->child_count(); ++i) {
585 ScopedJavaGlobalRef<jobject> jchild;
586 ConvertBookmarkNode(node->GetChild(i), *jnode, &jchild);
587 Java_BookmarkNode_addChild(env, jnode->obj(), jchild.obj());
588 if (ClearException(env)) {
589 LOG(WARNING) << "Java exception while adding child node.";
597 DISALLOW_COPY_AND_ASSIGN(GetBookmarkNodeTask);
600 // Gets the Mobile Bookmarks node. Using this task ensures the correct
601 // initialization of the bookmark model.
602 class GetMobileBookmarksNodeTask : public BookmarkModelTask {
604 explicit GetMobileBookmarksNodeTask(BookmarkModel* model)
605 : BookmarkModelTask(model) {}
607 const BookmarkNode* Run() {
608 const BookmarkNode* result = NULL;
609 RunOnUIThreadBlocking::Run(
610 base::Bind(&GetMobileBookmarksNodeTask::RunOnUIThread,
615 static void RunOnUIThread(BookmarkModel* model, const BookmarkNode** result) {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
618 *result = model->mobile_node();
622 DISALLOW_COPY_AND_ASSIGN(GetMobileBookmarksNodeTask);
625 // ------------- Aynchronous requests classes ------------- //
627 // Base class for asynchronous blocking requests to Chromium services.
628 // Service: type of the service to use (e.g. HistoryService, FaviconService).
629 template <typename Service>
630 class AsyncServiceRequest : protected BlockingUIThreadAsyncRequest {
632 AsyncServiceRequest(Service* service,
633 base::CancelableTaskTracker* cancelable_tracker)
634 : service_(service), cancelable_tracker_(cancelable_tracker) {}
636 Service* service() const { return service_; }
638 base::CancelableTaskTracker* cancelable_tracker() const {
639 return cancelable_tracker_;
644 base::CancelableTaskTracker* cancelable_tracker_;
646 DISALLOW_COPY_AND_ASSIGN(AsyncServiceRequest);
649 // Base class for all asynchronous blocking tasks that use the favicon service.
650 class FaviconServiceTask : public AsyncServiceRequest<FaviconService> {
652 FaviconServiceTask(FaviconService* service,
653 base::CancelableTaskTracker* cancelable_tracker,
655 : AsyncServiceRequest<FaviconService>(service, cancelable_tracker),
658 Profile* profile() const { return profile_; }
663 DISALLOW_COPY_AND_ASSIGN(FaviconServiceTask);
666 // Retrieves the favicon or touch icon for a URL from the FaviconService.
667 class BookmarkIconFetchTask : public FaviconServiceTask {
669 BookmarkIconFetchTask(FaviconService* favicon_service,
670 base::CancelableTaskTracker* cancelable_tracker,
672 : FaviconServiceTask(favicon_service, cancelable_tracker, profile) {}
674 favicon_base::FaviconRawBitmapResult Run(const GURL& url) {
675 float max_scale = ui::GetScaleForScaleFactor(
676 ResourceBundle::GetSharedInstance().GetMaxScaleFactor());
677 int desired_size_in_pixel = std::ceil(gfx::kFaviconSize * max_scale);
678 RunAsyncRequestOnUIThreadBlocking(
679 base::Bind(&FaviconService::GetRawFaviconForPageURL,
680 base::Unretained(service()),
682 favicon_base::FAVICON | favicon_base::TOUCH_ICON,
683 desired_size_in_pixel,
684 base::Bind(&BookmarkIconFetchTask::OnFaviconRetrieved,
685 base::Unretained(this)),
686 cancelable_tracker()));
691 void OnFaviconRetrieved(
692 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
693 result_ = bitmap_result;
697 favicon_base::FaviconRawBitmapResult result_;
699 DISALLOW_COPY_AND_ASSIGN(BookmarkIconFetchTask);
702 // Base class for all asynchronous blocking tasks that use the Android history
704 class HistoryProviderTask
705 : public AsyncServiceRequest<AndroidHistoryProviderService> {
707 HistoryProviderTask(AndroidHistoryProviderService* service,
708 base::CancelableTaskTracker* cancelable_tracker)
709 : AsyncServiceRequest<AndroidHistoryProviderService>(service,
710 cancelable_tracker) {
714 DISALLOW_COPY_AND_ASSIGN(HistoryProviderTask);
717 // Adds a bookmark from the API.
718 class AddBookmarkFromAPITask : public HistoryProviderTask {
720 AddBookmarkFromAPITask(AndroidHistoryProviderService* service,
721 base::CancelableTaskTracker* cancelable_tracker)
722 : HistoryProviderTask(service, cancelable_tracker) {}
724 history::URLID Run(const history::HistoryAndBookmarkRow& row) {
725 RunAsyncRequestOnUIThreadBlocking(
726 base::Bind(&AndroidHistoryProviderService::InsertHistoryAndBookmark,
727 base::Unretained(service()),
729 base::Bind(&AddBookmarkFromAPITask::OnBookmarkInserted,
730 base::Unretained(this)),
731 cancelable_tracker()));
736 void OnBookmarkInserted(history::URLID id) {
737 // Note that here 0 means an invalid id.
738 // This is because it represents a SQLite database row id.
743 history::URLID result_;
745 DISALLOW_COPY_AND_ASSIGN(AddBookmarkFromAPITask);
748 // Queries bookmarks from the API.
749 class QueryBookmarksFromAPITask : public HistoryProviderTask {
751 QueryBookmarksFromAPITask(AndroidHistoryProviderService* service,
752 base::CancelableTaskTracker* cancelable_tracker)
753 : HistoryProviderTask(service, cancelable_tracker), result_(NULL) {}
755 history::AndroidStatement* Run(
756 const std::vector<history::HistoryAndBookmarkRow::ColumnID>& projections,
757 const std::string& selection,
758 const std::vector<base::string16>& selection_args,
759 const std::string& sort_order) {
760 RunAsyncRequestOnUIThreadBlocking(
761 base::Bind(&AndroidHistoryProviderService::QueryHistoryAndBookmarks,
762 base::Unretained(service()),
767 base::Bind(&QueryBookmarksFromAPITask::OnBookmarksQueried,
768 base::Unretained(this)),
769 cancelable_tracker()));
774 void OnBookmarksQueried(history::AndroidStatement* statement) {
779 history::AndroidStatement* result_;
781 DISALLOW_COPY_AND_ASSIGN(QueryBookmarksFromAPITask);
784 // Updates bookmarks from the API.
785 class UpdateBookmarksFromAPITask : public HistoryProviderTask {
787 UpdateBookmarksFromAPITask(AndroidHistoryProviderService* service,
788 base::CancelableTaskTracker* cancelable_tracker)
789 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
791 int Run(const history::HistoryAndBookmarkRow& row,
792 const std::string& selection,
793 const std::vector<base::string16>& selection_args) {
794 RunAsyncRequestOnUIThreadBlocking(
795 base::Bind(&AndroidHistoryProviderService::UpdateHistoryAndBookmarks,
796 base::Unretained(service()),
800 base::Bind(&UpdateBookmarksFromAPITask::OnBookmarksUpdated,
801 base::Unretained(this)),
802 cancelable_tracker()));
807 void OnBookmarksUpdated(int updated_row_count) {
808 result_ = updated_row_count;
814 DISALLOW_COPY_AND_ASSIGN(UpdateBookmarksFromAPITask);
817 // Removes bookmarks from the API.
818 class RemoveBookmarksFromAPITask : public HistoryProviderTask {
820 RemoveBookmarksFromAPITask(AndroidHistoryProviderService* service,
821 base::CancelableTaskTracker* cancelable_tracker)
822 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
824 int Run(const std::string& selection,
825 const std::vector<base::string16>& selection_args) {
826 RunAsyncRequestOnUIThreadBlocking(
827 base::Bind(&AndroidHistoryProviderService::DeleteHistoryAndBookmarks,
828 base::Unretained(service()),
831 base::Bind(&RemoveBookmarksFromAPITask::OnBookmarksRemoved,
832 base::Unretained(this)),
833 cancelable_tracker()));
838 void OnBookmarksRemoved(int removed_row_count) {
839 result_ = removed_row_count;
845 DISALLOW_COPY_AND_ASSIGN(RemoveBookmarksFromAPITask);
848 // Removes history from the API.
849 class RemoveHistoryFromAPITask : public HistoryProviderTask {
851 RemoveHistoryFromAPITask(AndroidHistoryProviderService* service,
852 base::CancelableTaskTracker* cancelable_tracker)
853 : HistoryProviderTask(service, cancelable_tracker), result_(0) {}
855 int Run(const std::string& selection,
856 const std::vector<base::string16>& selection_args) {
857 RunAsyncRequestOnUIThreadBlocking(
858 base::Bind(&AndroidHistoryProviderService::DeleteHistory,
859 base::Unretained(service()),
862 base::Bind(&RemoveHistoryFromAPITask::OnHistoryRemoved,
863 base::Unretained(this)),
864 cancelable_tracker()));
869 void OnHistoryRemoved(int removed_row_count) {
870 result_ = removed_row_count;
876 DISALLOW_COPY_AND_ASSIGN(RemoveHistoryFromAPITask);
879 // This class provides the common method for the SearchTermAPIHelper.
880 class SearchTermTask : public HistoryProviderTask {
882 SearchTermTask(AndroidHistoryProviderService* service,
883 base::CancelableTaskTracker* cancelable_tracker,
885 : HistoryProviderTask(service, cancelable_tracker), profile_(profile) {}
887 // Fill SearchRow's keyword_id and url fields according the given
888 // search_term. Return true if succeeded.
889 void BuildSearchRow(history::SearchRow* row) {
890 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
892 TemplateURLService* template_service =
893 TemplateURLServiceFactory::GetForProfile(profile_);
894 template_service->Load();
896 const TemplateURL* search_engine =
897 template_service->GetDefaultSearchProvider();
899 const TemplateURLRef* search_url = &search_engine->url_ref();
900 TemplateURLRef::SearchTermsArgs search_terms_args(row->search_term());
901 search_terms_args.append_extra_query_params = true;
902 std::string url = search_url->ReplaceSearchTerms(
903 search_terms_args, template_service->search_terms_data());
905 row->set_url(GURL(url));
906 row->set_keyword_id(search_engine->id());
914 DISALLOW_COPY_AND_ASSIGN(SearchTermTask);
917 // Adds a search term from the API.
918 class AddSearchTermFromAPITask : public SearchTermTask {
920 AddSearchTermFromAPITask(AndroidHistoryProviderService* service,
921 base::CancelableTaskTracker* cancelable_tracker,
923 : SearchTermTask(service, cancelable_tracker, profile) {}
925 history::URLID Run(const history::SearchRow& row) {
926 RunAsyncRequestOnUIThreadBlocking(
927 base::Bind(&AddSearchTermFromAPITask::MakeRequestOnUIThread,
928 base::Unretained(this), row));
933 void MakeRequestOnUIThread(const history::SearchRow& row) {
934 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
935 history::SearchRow internal_row = row;
936 BuildSearchRow(&internal_row);
937 service()->InsertSearchTerm(
939 base::Bind(&AddSearchTermFromAPITask::OnSearchTermInserted,
940 base::Unretained(this)),
941 cancelable_tracker());
944 void OnSearchTermInserted(history::URLID id) {
945 // Note that here 0 means an invalid id.
946 // This is because it represents a SQLite database row id.
951 history::URLID result_;
953 DISALLOW_COPY_AND_ASSIGN(AddSearchTermFromAPITask);
956 // Queries search terms from the API.
957 class QuerySearchTermsFromAPITask : public SearchTermTask {
959 QuerySearchTermsFromAPITask(AndroidHistoryProviderService* service,
960 base::CancelableTaskTracker* cancelable_tracker,
962 : SearchTermTask(service, cancelable_tracker, profile), result_(NULL) {}
964 history::AndroidStatement* Run(
965 const std::vector<history::SearchRow::ColumnID>& projections,
966 const std::string& selection,
967 const std::vector<base::string16>& selection_args,
968 const std::string& sort_order) {
969 RunAsyncRequestOnUIThreadBlocking(base::Bind(
970 &AndroidHistoryProviderService::QuerySearchTerms,
971 base::Unretained(service()),
976 base::Bind(&QuerySearchTermsFromAPITask::OnSearchTermsQueried,
977 base::Unretained(this)),
978 cancelable_tracker()));
983 // Callback to return the result.
984 void OnSearchTermsQueried(history::AndroidStatement* statement) {
989 history::AndroidStatement* result_;
991 DISALLOW_COPY_AND_ASSIGN(QuerySearchTermsFromAPITask);
994 // Updates search terms from the API.
995 class UpdateSearchTermsFromAPITask : public SearchTermTask {
997 UpdateSearchTermsFromAPITask(AndroidHistoryProviderService* service,
998 base::CancelableTaskTracker* cancelable_tracker,
1000 : SearchTermTask(service, cancelable_tracker, profile), result_(0) {}
1002 int Run(const history::SearchRow& row,
1003 const std::string& selection,
1004 const std::vector<base::string16>& selection_args) {
1005 RunAsyncRequestOnUIThreadBlocking(
1006 base::Bind(&UpdateSearchTermsFromAPITask::MakeRequestOnUIThread,
1007 base::Unretained(this), row, selection, selection_args));
1012 void MakeRequestOnUIThread(
1013 const history::SearchRow& row,
1014 const std::string& selection,
1015 const std::vector<base::string16>& selection_args) {
1016 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1017 history::SearchRow internal_row = row;
1018 BuildSearchRow(&internal_row);
1019 service()->UpdateSearchTerms(
1023 base::Bind(&UpdateSearchTermsFromAPITask::OnSearchTermsUpdated,
1024 base::Unretained(this)),
1025 cancelable_tracker());
1028 void OnSearchTermsUpdated(int updated_row_count) {
1029 result_ = updated_row_count;
1035 DISALLOW_COPY_AND_ASSIGN(UpdateSearchTermsFromAPITask);
1038 // Removes search terms from the API.
1039 class RemoveSearchTermsFromAPITask : public SearchTermTask {
1041 RemoveSearchTermsFromAPITask(AndroidHistoryProviderService* service,
1042 base::CancelableTaskTracker* cancelable_tracker,
1044 : SearchTermTask(service, cancelable_tracker, profile), result_() {}
1046 int Run(const std::string& selection,
1047 const std::vector<base::string16>& selection_args) {
1048 RunAsyncRequestOnUIThreadBlocking(base::Bind(
1049 &AndroidHistoryProviderService::DeleteSearchTerms,
1050 base::Unretained(service()),
1053 base::Bind(&RemoveSearchTermsFromAPITask::OnSearchTermsDeleted,
1054 base::Unretained(this)),
1055 cancelable_tracker()));
1060 void OnSearchTermsDeleted(int deleted_row_count) {
1061 result_ = deleted_row_count;
1067 DISALLOW_COPY_AND_ASSIGN(RemoveSearchTermsFromAPITask);
1070 // ------------- Other utility methods (may use tasks) ------------- //
1072 // Fills the bookmark |row| with the given java objects.
1073 void FillBookmarkRow(JNIEnv* env,
1083 history::HistoryAndBookmarkRow* row,
1084 BookmarkModel* model) {
1085 // Needed because of the internal bookmark model task invocation.
1086 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
1089 base::string16 raw_url = ConvertJavaStringToUTF16(env, url);
1090 // GURL doesn't accept the URL without protocol, but the Android CTS
1091 // allows it. We are trying to prefix with 'http://' to see whether
1092 // GURL thinks it is a valid URL. The original url will be stored in
1093 // history::BookmarkRow.raw_url_.
1094 GURL gurl = ParseAndMaybeAppendScheme(raw_url, kDefaultUrlScheme);
1096 row->set_raw_url(base::UTF16ToUTF8(raw_url));
1100 row->set_created(ConvertJlongToTime(
1101 ConvertJLongObjectToPrimitive(env, created)));
1104 row->set_is_bookmark(ConvertJBooleanObjectToPrimitive(env, isBookmark));
1107 row->set_last_visit_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive(
1111 std::vector<uint8> bytes;
1112 base::android::JavaByteArrayToByteVector(env, favicon, &bytes);
1113 row->set_favicon(base::RefCountedBytes::TakeVector(&bytes));
1117 row->set_title(ConvertJavaStringToUTF16(env, title));
1120 row->set_visit_count(ConvertJIntegerToJint(env, visits));
1122 // Make sure parent_id is always in the mobile_node branch.
1123 IsInMobileBookmarksBranchTask task(model);
1124 if (task.Run(parent_id))
1125 row->set_parent_id(parent_id);
1128 // Fills the bookmark |row| with the given java objects if it is not null.
1129 void FillSearchRow(JNIEnv* env,
1131 jstring search_term,
1133 history::SearchRow* row) {
1135 row->set_search_term(ConvertJavaStringToUTF16(env, search_term));
1138 row->set_search_time(ConvertJlongToTime(ConvertJLongObjectToPrimitive(
1144 // ------------- Native initialization and destruction ------------- //
1146 static jlong Init(JNIEnv* env, jobject obj) {
1147 ChromeBrowserProvider* provider = new ChromeBrowserProvider(env, obj);
1148 return reinterpret_cast<intptr_t>(provider);
1151 bool ChromeBrowserProvider::RegisterChromeBrowserProvider(JNIEnv* env) {
1152 return RegisterNativesImpl(env);
1155 ChromeBrowserProvider::ChromeBrowserProvider(JNIEnv* env, jobject obj)
1156 : weak_java_provider_(env, obj),
1157 handling_extensive_changes_(false) {
1158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1159 profile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
1160 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile_);
1161 top_sites_ = profile_->GetTopSites();
1162 service_.reset(new AndroidHistoryProviderService(profile_));
1163 favicon_service_.reset(FaviconServiceFactory::GetForProfile(profile_,
1164 Profile::EXPLICIT_ACCESS));
1166 // Registers the notifications we are interested.
1167 bookmark_model_->AddObserver(this);
1168 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED,
1169 content::NotificationService::AllSources());
1170 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED,
1171 content::NotificationService::AllSources());
1172 notification_registrar_.Add(this,
1173 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
1174 content::NotificationService::AllSources());
1175 TemplateURLService* template_service =
1176 TemplateURLServiceFactory::GetForProfile(profile_);
1177 if (!template_service->loaded())
1178 template_service->Load();
1181 ChromeBrowserProvider::~ChromeBrowserProvider() {
1182 bookmark_model_->RemoveObserver(this);
1185 void ChromeBrowserProvider::Destroy(JNIEnv*, jobject) {
1189 // ------------- Provider public APIs ------------- //
1191 jlong ChromeBrowserProvider::AddBookmark(JNIEnv* env,
1199 url = ConvertJavaStringToUTF16(env, jurl);
1200 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1202 AddBookmarkTask task(bookmark_model_);
1203 return task.Run(title, url, is_folder, parent_id);
1206 jint ChromeBrowserProvider::RemoveBookmark(JNIEnv*, jobject, jlong id) {
1207 RemoveBookmarkTask task(bookmark_model_);
1208 return task.Run(id);
1211 jint ChromeBrowserProvider::UpdateBookmark(JNIEnv* env,
1219 url = ConvertJavaStringToUTF16(env, jurl);
1220 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1222 UpdateBookmarkTask task(bookmark_model_);
1223 return task.Run(id, title, url, parent_id);
1226 // Add the bookmark with the given column values.
1227 jlong ChromeBrowserProvider::AddBookmarkFromAPI(JNIEnv* env,
1239 history::HistoryAndBookmarkRow row;
1240 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title,
1241 visits, parent_id, &row, bookmark_model_);
1243 // URL must be valid.
1244 if (row.url().is_empty()) {
1245 LOG(ERROR) << "Not a valid URL " << row.raw_url();
1246 return kInvalidContentProviderId;
1249 AddBookmarkFromAPITask task(service_.get(), &cancelable_task_tracker_);
1250 return task.Run(row);
1253 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QueryBookmarkFromAPI(
1256 jobjectArray projection,
1258 jobjectArray selection_args,
1259 jstring sort_order) {
1260 // Converts the projection to array of ColumnID and column name.
1261 // Used to store the projection column ID according their sequence.
1262 std::vector<history::HistoryAndBookmarkRow::ColumnID> query_columns;
1263 // Used to store the projection column names according their sequence.
1264 std::vector<std::string> columns_name;
1266 jsize len = env->GetArrayLength(projection);
1267 for (int i = 0; i < len; i++) {
1268 std::string name = ConvertJavaStringToUTF8(env, static_cast<jstring>(
1269 env->GetObjectArrayElement(projection, i)));
1270 history::HistoryAndBookmarkRow::ColumnID id =
1271 history::HistoryAndBookmarkRow::GetColumnID(name);
1272 if (id == history::HistoryAndBookmarkRow::COLUMN_END) {
1273 // Ignore the unknown column; As Android platform will send us
1274 // the non public column.
1277 query_columns.push_back(id);
1278 columns_name.push_back(name);
1282 std::vector<base::string16> where_args =
1283 ConvertJStringArrayToString16Array(env, selection_args);
1285 std::string where_clause;
1287 where_clause = ConvertJavaStringToUTF8(env, selections);
1290 std::string sort_clause;
1292 sort_clause = ConvertJavaStringToUTF8(env, sort_order);
1295 QueryBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1296 history::AndroidStatement* statement = task.Run(
1297 query_columns, where_clause, where_args, sort_clause);
1299 return ScopedJavaLocalRef<jobject>();
1301 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor
1303 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement,
1304 service_.get(), favicon_service_.get());
1307 // Updates the bookmarks with the given column values. The value is not given if
1309 jint ChromeBrowserProvider::UpdateBookmarkFromAPI(JNIEnv* env,
1320 jobjectArray selection_args) {
1321 history::HistoryAndBookmarkRow row;
1322 FillBookmarkRow(env, obj, url, created, isBookmark, date, favicon, title,
1323 visits, parent_id, &row, bookmark_model_);
1325 std::vector<base::string16> where_args =
1326 ConvertJStringArrayToString16Array(env, selection_args);
1328 std::string where_clause;
1330 where_clause = ConvertJavaStringToUTF8(env, selections);
1332 UpdateBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1333 return task.Run(row, where_clause, where_args);
1336 jint ChromeBrowserProvider::RemoveBookmarkFromAPI(JNIEnv* env,
1339 jobjectArray selection_args) {
1340 std::vector<base::string16> where_args =
1341 ConvertJStringArrayToString16Array(env, selection_args);
1343 std::string where_clause;
1345 where_clause = ConvertJavaStringToUTF8(env, selections);
1347 RemoveBookmarksFromAPITask task(service_.get(), &cancelable_task_tracker_);
1348 return task.Run(where_clause, where_args);
1351 jint ChromeBrowserProvider::RemoveHistoryFromAPI(JNIEnv* env,
1354 jobjectArray selection_args) {
1355 std::vector<base::string16> where_args =
1356 ConvertJStringArrayToString16Array(env, selection_args);
1358 std::string where_clause;
1360 where_clause = ConvertJavaStringToUTF8(env, selections);
1362 RemoveHistoryFromAPITask task(service_.get(), &cancelable_task_tracker_);
1363 return task.Run(where_clause, where_args);
1366 // Add the search term with the given column values. The value is not given if
1368 jlong ChromeBrowserProvider::AddSearchTermFromAPI(JNIEnv* env,
1370 jstring search_term,
1372 DCHECK(search_term);
1374 history::SearchRow row;
1375 FillSearchRow(env, obj, search_term, date, &row);
1377 // URL must be valid.
1378 if (row.search_term().empty()) {
1379 LOG(ERROR) << "Search term is empty.";
1380 return kInvalidContentProviderId;
1383 AddSearchTermFromAPITask task(service_.get(),
1384 &cancelable_task_tracker_,
1386 return task.Run(row);
1389 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::QuerySearchTermFromAPI(
1392 jobjectArray projection,
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;
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>();
1412 query_columns.push_back(id);
1413 columns_name.push_back(name);
1417 std::vector<base::string16> where_args =
1418 ConvertJStringArrayToString16Array(env, selection_args);
1420 std::string where_clause;
1422 where_clause = ConvertJavaStringToUTF8(env, selections);
1425 std::string sort_clause;
1427 sort_clause = ConvertJavaStringToUTF8(env, sort_order);
1430 QuerySearchTermsFromAPITask task(service_.get(),
1431 &cancelable_task_tracker_,
1433 history::AndroidStatement* statement = task.Run(
1434 query_columns, where_clause, where_args, sort_clause);
1436 return ScopedJavaLocalRef<jobject>();
1437 // Creates and returns org.chromium.chrome.browser.database.SQLiteCursor
1439 return SQLiteCursor::NewJavaSqliteCursor(env, columns_name, statement,
1440 service_.get(), favicon_service_.get());
1443 // Updates the search terms with the given column values. The value is not
1444 // given if it is NULL.
1445 jint ChromeBrowserProvider::UpdateSearchTermFromAPI(
1446 JNIEnv* env, jobject obj, jstring search_term, jobject date,
1447 jstring selections, jobjectArray selection_args) {
1448 history::SearchRow row;
1449 FillSearchRow(env, obj, search_term, date, &row);
1451 std::vector<base::string16> where_args = ConvertJStringArrayToString16Array(
1452 env, selection_args);
1454 std::string where_clause;
1456 where_clause = ConvertJavaStringToUTF8(env, selections);
1458 UpdateSearchTermsFromAPITask task(service_.get(),
1459 &cancelable_task_tracker_,
1461 return task.Run(row, where_clause, where_args);
1464 jint ChromeBrowserProvider::RemoveSearchTermFromAPI(
1465 JNIEnv* env, jobject obj, jstring selections, jobjectArray selection_args) {
1466 std::vector<base::string16> where_args =
1467 ConvertJStringArrayToString16Array(env, selection_args);
1469 std::string where_clause;
1471 where_clause = ConvertJavaStringToUTF8(env, selections);
1473 RemoveSearchTermsFromAPITask task(service_.get(),
1474 &cancelable_task_tracker_,
1476 return task.Run(where_clause, where_args);
1479 // ------------- Provider custom APIs ------------- //
1481 jboolean ChromeBrowserProvider::BookmarkNodeExists(
1485 BookmarkNodeExistsTask task(bookmark_model_);
1486 return task.Run(id);
1489 jlong ChromeBrowserProvider::CreateBookmarksFolderOnce(
1494 base::string16 title = ConvertJavaStringToUTF16(env, jtitle);
1496 return kInvalidBookmarkId;
1498 CreateBookmarksFolderOnceTask task(bookmark_model_);
1499 return task.Run(title, parent_id);
1502 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetEditableBookmarkFolders(
1505 ScopedJavaGlobalRef<jobject> jroot;
1506 ChromeBookmarkClient* client =
1507 ChromeBookmarkClientFactory::GetForProfile(profile_);
1508 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
1509 GetEditableBookmarkFoldersTask task(client, model);
1511 return ScopedJavaLocalRef<jobject>(jroot);
1514 void ChromeBrowserProvider::RemoveAllUserBookmarks(JNIEnv* env, jobject obj) {
1515 RemoveAllUserBookmarksTask task(bookmark_model_);
1519 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetBookmarkNode(
1520 JNIEnv* env, jobject obj, jlong id, jboolean get_parent,
1521 jboolean get_children) {
1522 ScopedJavaGlobalRef<jobject> jnode;
1523 GetBookmarkNodeTask task(bookmark_model_);
1524 task.Run(id, get_parent, get_children, &jnode);
1525 return ScopedJavaLocalRef<jobject>(jnode);
1528 ScopedJavaLocalRef<jobject> ChromeBrowserProvider::GetMobileBookmarksFolder(
1531 ScopedJavaGlobalRef<jobject> jnode;
1532 GetMobileBookmarksNodeTask task(bookmark_model_);
1533 ConvertBookmarkNode(task.Run(), ScopedJavaLocalRef<jobject>(), &jnode);
1534 return ScopedJavaLocalRef<jobject>(jnode);
1537 jboolean ChromeBrowserProvider::IsBookmarkInMobileBookmarksBranch(
1541 IsInMobileBookmarksBranchTask task(bookmark_model_);
1542 return task.Run(id);
1545 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetFaviconOrTouchIcon(
1546 JNIEnv* env, jobject obj, jstring jurl) {
1548 return ScopedJavaLocalRef<jbyteArray>();
1550 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1551 BookmarkIconFetchTask favicon_task(
1552 favicon_service_.get(), &cancelable_task_tracker_, profile_);
1553 favicon_base::FaviconRawBitmapResult bitmap_result = favicon_task.Run(url);
1555 if (!bitmap_result.is_valid() || !bitmap_result.bitmap_data.get())
1556 return ScopedJavaLocalRef<jbyteArray>();
1558 return base::android::ToJavaByteArray(env, bitmap_result.bitmap_data->front(),
1559 bitmap_result.bitmap_data->size());
1562 ScopedJavaLocalRef<jbyteArray> ChromeBrowserProvider::GetThumbnail(
1563 JNIEnv* env, jobject obj, jstring jurl) {
1565 return ScopedJavaLocalRef<jbyteArray>();
1566 GURL url = GURL(ConvertJavaStringToUTF16(env, jurl));
1568 // GetPageThumbnail is synchronous and can be called from any thread.
1569 scoped_refptr<base::RefCountedMemory> thumbnail;
1571 top_sites_->GetPageThumbnail(url, false, &thumbnail);
1573 if (!thumbnail.get() || !thumbnail->front()) {
1574 return ScopedJavaLocalRef<jbyteArray>();
1577 return base::android::ToJavaByteArray(env, thumbnail->front(),
1581 // ------------- Observer-related methods ------------- //
1583 void ChromeBrowserProvider::ExtensiveBookmarkChangesBeginning(
1584 BookmarkModel* model) {
1585 handling_extensive_changes_ = true;
1588 void ChromeBrowserProvider::ExtensiveBookmarkChangesEnded(
1589 BookmarkModel* model) {
1590 handling_extensive_changes_ = false;
1591 BookmarkModelChanged();
1594 void ChromeBrowserProvider::BookmarkModelChanged() {
1595 if (handling_extensive_changes_)
1598 JNIEnv* env = AttachCurrentThread();
1599 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1603 Java_ChromeBrowserProvider_onBookmarkChanged(env, obj.obj());
1606 void ChromeBrowserProvider::Observe(
1608 const content::NotificationSource& source,
1609 const content::NotificationDetails& details) {
1610 if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED ||
1611 type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
1612 JNIEnv* env = AttachCurrentThread();
1613 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1616 Java_ChromeBrowserProvider_onHistoryChanged(env, obj.obj());
1618 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED) {
1619 JNIEnv* env = AttachCurrentThread();
1620 ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
1623 Java_ChromeBrowserProvider_onSearchTermChanged(env, obj.obj());