// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/android/bookmarks/partner_bookmarks_reader.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/logging.h" #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/favicon/core/favicon_service.h" #include "components/favicon_base/favicon_types.h" #include "content/public/browser/browser_thread.h" #include "jni/PartnerBookmarksReader_jni.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/favicon_size.h" using base::android::AttachCurrentThread; using base::android::CheckException; using base::android::ConvertJavaStringToUTF16; using bookmarks::BookmarkNode; using bookmarks::BookmarkPermanentNode; using content::BrowserThread; namespace { void SetFaviconTask(Profile* profile, const GURL& page_url, const GURL& icon_url, const std::vector& image_data, favicon_base::IconType icon_type) { DCHECK_CURRENTLY_ON(BrowserThread::UI); scoped_refptr bitmap_data( new base::RefCountedBytes(image_data)); gfx::Size pixel_size(gfx::kFaviconSize, gfx::kFaviconSize); favicon::FaviconService* favicon_service = FaviconServiceFactory::GetForProfile( ProfileManager::GetActiveUserProfile(), ServiceAccessType::EXPLICIT_ACCESS); if (!favicon_service) return; favicon_service->MergeFavicon( page_url, page_url, icon_type, bitmap_data, pixel_size); } void SetFaviconCallback(Profile* profile, const GURL& page_url, const GURL& icon_url, const std::vector& image_data, favicon_base::IconType icon_type, base::WaitableEvent* bookmark_added_event) { SetFaviconTask(profile, page_url, icon_url, image_data, icon_type); if (bookmark_added_event) bookmark_added_event->Signal(); } void PrepareAndSetFavicon(JNIEnv* env, jbyte* icon_bytes, int icon_len, BookmarkNode* node, Profile* profile, favicon_base::IconType icon_type) { SkBitmap icon_bitmap; if (!gfx::PNGCodec::Decode( reinterpret_cast(icon_bytes), icon_len, &icon_bitmap)) return; std::vector image_data; if (!gfx::PNGCodec::EncodeBGRASkBitmap(icon_bitmap, false, &image_data)) return; // TODO(aruslan): TODO(tedchoc): Follow up on how to avoid this through js. // Since the favicon URL is used as a key in the history's thumbnail DB, // this gives us a value which does not collide with others. GURL fake_icon_url = node->url(); if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { SetFaviconTask(profile, node->url(), fake_icon_url, image_data, icon_type); } else { base::WaitableEvent event(false, false); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&SetFaviconCallback, profile, node->url(), fake_icon_url, image_data, icon_type, &event)); // TODO(aruslan): http://b/6397072 If possible - avoid using favicon service event.Wait(); } } const BookmarkNode* GetNodeByID(const BookmarkNode* parent, int64_t id) { if (parent->id() == id) return parent; for (int i= 0, child_count = parent->child_count(); i < child_count; ++i) { const BookmarkNode* result = GetNodeByID(parent->GetChild(i), id); if (result) return result; } return NULL; } } // namespace PartnerBookmarksReader::PartnerBookmarksReader( PartnerBookmarksShim* partner_bookmarks_shim, Profile* profile) : partner_bookmarks_shim_(partner_bookmarks_shim), profile_(profile), wip_next_available_id_(0) { } PartnerBookmarksReader::~PartnerBookmarksReader() {} void PartnerBookmarksReader::PartnerBookmarksCreationComplete( JNIEnv*, const JavaParamRef&) { DCHECK_CURRENTLY_ON(BrowserThread::UI); partner_bookmarks_shim_->SetPartnerBookmarksRoot( wip_partner_bookmarks_root_.release()); wip_next_available_id_ = 0; } void PartnerBookmarksReader::Destroy(JNIEnv* env, const JavaParamRef& obj) { delete this; } void PartnerBookmarksReader::Reset(JNIEnv* env, const JavaParamRef& obj) { DCHECK_CURRENTLY_ON(BrowserThread::UI); wip_partner_bookmarks_root_.reset(); wip_next_available_id_ = 0; } jlong PartnerBookmarksReader::AddPartnerBookmark( JNIEnv* env, const JavaParamRef& obj, const JavaParamRef& jurl, const JavaParamRef& jtitle, jboolean is_folder, jlong parent_id, const JavaParamRef& favicon, const JavaParamRef& touchicon) { base::string16 url; base::string16 title; if (jurl) url = ConvertJavaStringToUTF16(env, jurl); if (jtitle) title = ConvertJavaStringToUTF16(env, jtitle); BookmarkNode* node = NULL; if (wip_partner_bookmarks_root_.get()) { node = new BookmarkNode(wip_next_available_id_++, GURL(url)); node->set_type(is_folder ? BookmarkNode::FOLDER : BookmarkNode::URL); node->SetTitle(title); // Handle favicon and touchicon if (profile_ != NULL && (favicon != NULL || touchicon != NULL)) { jbyteArray icon = (touchicon != NULL) ? touchicon : favicon; const favicon_base::IconType icon_type = touchicon ? favicon_base::TOUCH_ICON : favicon_base::FAVICON; const int icon_len = env->GetArrayLength(icon); jbyte* icon_bytes = env->GetByteArrayElements(icon, NULL); if (icon_bytes) PrepareAndSetFavicon(env, icon_bytes, icon_len, node, profile_, icon_type); env->ReleaseByteArrayElements(icon, icon_bytes, JNI_ABORT); } const BookmarkNode* parent = GetNodeByID(wip_partner_bookmarks_root_.get(), parent_id); if (!parent) { LOG(WARNING) << "partner_bookmarks_shim: invalid/unknown parent_id=" << parent_id << ": adding to the root"; parent = wip_partner_bookmarks_root_.get(); } const_cast(parent)->Add(node, parent->child_count()); } else { node = new BookmarkPermanentNode(wip_next_available_id_++); node->SetTitle(title); wip_partner_bookmarks_root_.reset(node); } return node->id(); } // static static void DisablePartnerBookmarksEditing(JNIEnv* env, const JavaParamRef& clazz) { PartnerBookmarksShim::DisablePartnerBookmarksEditing(); } // static bool PartnerBookmarksReader::RegisterPartnerBookmarksReader(JNIEnv* env) { return RegisterNativesImpl(env); } // ---------------------------------------------------------------- static jlong Init(JNIEnv* env, const JavaParamRef& obj) { Profile* profile = ProfileManager::GetActiveUserProfile(); PartnerBookmarksShim* partner_bookmarks_shim = PartnerBookmarksShim::BuildForBrowserContext(profile); PartnerBookmarksReader* reader = new PartnerBookmarksReader( partner_bookmarks_shim, profile); return reinterpret_cast(reader); }