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