Index: chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc |
diff --git a/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc b/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dab968276526f735aaebf6989b0afa97cf64065e |
--- /dev/null |
+++ b/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc |
@@ -0,0 +1,389 @@ |
+// Copyright (c) 2012 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/ui/webui/ntp/android/bookmarks_handler.h" |
+ |
+#include "base/logging.h" |
+#include "base/memory/ref_counted_memory.h" |
+#include "base/string_number_conversions.h" |
+#include "base/string_util.h" |
+#include "chrome/browser/android/tab_android.h" |
+#include "chrome/browser/bookmarks/bookmark_model.h" |
+#include "chrome/browser/bookmarks/bookmark_model_factory.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/profiles/profile_manager.h" |
+#include "chrome/browser/ui/webui/chrome_url_data_manager.h" |
+#include "chrome/browser/ui/webui/favicon_source.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/web_contents.h" |
+#include "third_party/skia/include/core/SkBitmap.h" |
+#include "ui/gfx/codec/png_codec.h" |
+#include "ui/gfx/color_analysis.h" |
+ |
+using base::Int64ToString; |
+using content::BrowserThread; |
+ |
+namespace { |
+ |
+static const char* kParentIdParam = "parent_id"; |
+static const char* kNodeIdParam = "node_id"; |
+ |
+// Parses a bookmark ID passed back from the NTP. The IDs differ from the |
+// normal int64 bookmark ID because we prepend a "p" if the ID represents a |
+// partner bookmark. |
+bool ParseNtpBookmarkId(const ListValue* args, |
+ int64* out_id, |
+ bool* out_is_partner) { |
+ std::string string_id; |
+ if (!args->GetString(0, &string_id)) |
+ return false; |
+ |
+ if (string_id.empty()) |
+ return false; |
+ |
+ if (StartsWithASCII(string_id, "p", true)) { |
+ *out_is_partner = true; |
+ return base::StringToInt64(string_id.substr(1), out_id); |
+ } |
+ |
+ *out_is_partner = false; |
+ return base::StringToInt64(string_id, out_id); |
+} |
+ |
+std::string BookmarkTypeAsString(BookmarkNode::Type type) { |
+ switch (type) { |
+ case BookmarkNode::URL: |
+ return "URL"; |
+ case BookmarkNode::FOLDER: |
+ return "FOLDER"; |
+ case BookmarkNode::BOOKMARK_BAR: |
+ return "BOOKMARK_BAR"; |
+ case BookmarkNode::OTHER_NODE: |
+ return "OTHER_NODE"; |
+ case BookmarkNode::MOBILE: |
+ return "MOBILE"; |
+ default: |
+ return "UNKNOWN"; |
+ } |
+} |
+ |
+SkColor GetDominantColorForFavicon(scoped_refptr<base::RefCountedMemory> png) { |
+ color_utils::GridSampler sampler; |
+ // 100 here is the darkness_limit which represents the minimum sum of the RGB |
+ // components that is acceptable as a color choice. This can be from 0 to 765. |
+ // 665 here is the brightness_limit represents the maximum sum of the RGB |
+ // components that is acceptable as a color choice. This can be from 0 to 765. |
+ return color_utils::CalculateKMeanColorOfPNG(png, 100, 665, sampler); |
+} |
+ |
+} // namespace |
+ |
+BookmarksHandler::BookmarksHandler() |
+ : bookmark_model_(NULL), |
+ partner_bookmarks_shim_(NULL), |
+ bookmark_data_requested_(false), |
+ extensive_changes_(false) { |
+} |
+ |
+BookmarksHandler::~BookmarksHandler() { |
+ if (bookmark_model_) |
+ bookmark_model_->RemoveObserver(this); |
+ |
+ if (partner_bookmarks_shim_) |
+ partner_bookmarks_shim_->RemoveObserver(this); |
+} |
+ |
+void BookmarksHandler::RegisterMessages() { |
+ // Listen for the bookmark change. We need the both bookmark and folder |
+ // change, the NotificationService is not sufficient. |
+ Profile* profile = Profile::FromBrowserContext( |
+ web_ui()->GetWebContents()->GetBrowserContext()); |
+ |
+ ChromeURLDataManager::AddDataSource(profile, |
+ new FaviconSource(profile, FaviconSource::ANY)); |
+ |
+ bookmark_model_ = BookmarkModelFactory::GetForProfile(profile); |
+ if (bookmark_model_) { |
+ bookmark_model_->AddObserver(this); |
+ // Since a sync or import could have started before this class is |
+ // initialized, we need to make sure that our initial state is |
+ // up to date. |
+ extensive_changes_ = bookmark_model_->IsDoingExtensiveChanges(); |
+ } |
+ |
+ // Create the partner Bookmarks shim as early as possible (but don't attach). |
+ if (!partner_bookmarks_shim_) { |
+ partner_bookmarks_shim_ = PartnerBookmarksShim::GetInstance(); |
+ partner_bookmarks_shim_->AddObserver(this); |
+ } |
+ |
+ // Register ourselves as the handler for the bookmark javascript callbacks. |
+ web_ui()->RegisterMessageCallback("getBookmarks", |
+ base::Bind(&BookmarksHandler::HandleGetBookmarks, |
+ base::Unretained(this))); |
+ web_ui()->RegisterMessageCallback("deleteBookmark", |
+ base::Bind(&BookmarksHandler::HandleDeleteBookmark, |
+ base::Unretained(this))); |
+ web_ui()->RegisterMessageCallback("createHomeScreenBookmarkShortcut", |
+ base::Bind(&BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut, |
+ base::Unretained(this))); |
+} |
+ |
+void BookmarksHandler::HandleGetBookmarks(const ListValue* args) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ bookmark_data_requested_ = true; |
+ Profile* profile = Profile::FromBrowserContext( |
+ web_ui()->GetWebContents()->GetBrowserContext()); |
+ if (!BookmarkModelFactory::GetForProfile(profile)->IsLoaded()) |
+ return; // is handled in Loaded(). |
+ |
+ // Attach the Partner Bookmarks shim under the Mobile Bookmarks. |
+ // Cannot do this earlier because mobile_node is not yet set. |
+ DCHECK(partner_bookmarks_shim_ != NULL); |
+ if (bookmark_model_) { |
+ partner_bookmarks_shim_->AttachTo( |
+ bookmark_model_, bookmark_model_->mobile_node()); |
+ } |
+ if (!partner_bookmarks_shim_->IsLoaded()) |
+ return; // is handled with a PartnerShimLoaded() callback |
+ |
+ int64 id; |
+ bool is_partner; |
+ if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) |
+ QueryBookmarkFolder(id, is_partner); |
+ else |
+ QueryInitialBookmarks(); |
+} |
+ |
+void BookmarksHandler::HandleDeleteBookmark(const ListValue* args) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ int64 id; |
+ bool is_partner; |
+ if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) { |
+ DCHECK(!is_partner); |
+ const BookmarkNode* node = bookmark_model_->GetNodeByID(id); |
+ if (node && node->parent()) { |
+ const BookmarkNode* parent_node = node->parent(); |
+ bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node)); |
+ } |
+ } |
+} |
+ |
+std::string BookmarksHandler::GetBookmarkIdForNtp(const BookmarkNode* node) { |
+ return (partner_bookmarks_shim_->IsPartnerBookmark(node) ? "p" : "") + |
+ Int64ToString(node->id()); |
+} |
+ |
+void BookmarksHandler::SetParentInBookmarksResult(const BookmarkNode* parent, |
+ DictionaryValue* result) { |
+ result->SetString(kParentIdParam, GetBookmarkIdForNtp(parent)); |
+} |
+ |
+void BookmarksHandler::PopulateBookmark(const BookmarkNode* node, |
+ ListValue* result) { |
+ if (!result) |
+ return; |
+ |
+ DictionaryValue* filler_value = new DictionaryValue(); |
+ filler_value->SetString("title", node->GetTitle()); |
+ // Mark reserved system nodes and partner bookmarks as uneditable |
+ // (i.e. the bookmark bar along with the "Other Bookmarks" folder). |
+ filler_value->SetBoolean("editable", |
+ partner_bookmarks_shim_->IsBookmarkEditable(node)); |
+ if (node->is_url()) { |
+ filler_value->SetBoolean("folder", false); |
+ filler_value->SetString("url", node->url().spec()); |
+ } else { |
+ filler_value->SetBoolean("folder", true); |
+ } |
+ filler_value->SetString("id", GetBookmarkIdForNtp(node)); |
+ filler_value->SetString("type", BookmarkTypeAsString(node->type())); |
+ result->Append(filler_value); |
+} |
+ |
+void BookmarksHandler::PopulateBookmarksInFolder( |
+ const BookmarkNode* folder, |
+ DictionaryValue* result) { |
+ ListValue* bookmarks = new ListValue(); |
+ |
+ for (int i = 0; i < folder->child_count(); i++) { |
+ const BookmarkNode* bookmark= folder->GetChild(i); |
+ PopulateBookmark(bookmark, bookmarks); |
+ } |
+ |
+ // Make sure we iterate over the partner's attach point |
+ DCHECK(partner_bookmarks_shim_ != NULL); |
+ if (partner_bookmarks_shim_->HasPartnerBookmarks() && |
+ folder == partner_bookmarks_shim_->get_attach_point()) { |
+ PopulateBookmark( |
+ partner_bookmarks_shim_->GetPartnerBookmarksRoot(), bookmarks); |
+ } |
+ |
+ ListValue* folder_hierarchy = new ListValue(); |
+ const BookmarkNode* parent = partner_bookmarks_shim_->GetParentOf(folder); |
+ |
+ while (parent != NULL) { |
+ DictionaryValue* hierarchy_entry = new DictionaryValue(); |
+ if (partner_bookmarks_shim_->IsRootNode(parent)) |
+ hierarchy_entry->SetBoolean("root", true); |
+ |
+ hierarchy_entry->SetString("title", parent->GetTitle()); |
+ hierarchy_entry->SetString("id", GetBookmarkIdForNtp(parent)); |
+ folder_hierarchy->Append(hierarchy_entry); |
+ parent = partner_bookmarks_shim_->GetParentOf(parent); |
+ } |
+ |
+ result->SetString("title", folder->GetTitle()); |
+ result->SetString("id", GetBookmarkIdForNtp(folder)); |
+ result->SetBoolean("root", partner_bookmarks_shim_->IsRootNode(folder)); |
+ result->Set("bookmarks", bookmarks); |
+ result->Set("hierarchy", folder_hierarchy); |
+} |
+ |
+void BookmarksHandler::QueryBookmarkFolder(const int64& folder_id, |
+ bool is_partner_bookmark) { |
+ DCHECK(partner_bookmarks_shim_ != NULL); |
+ const BookmarkNode* bookmarks = |
+ partner_bookmarks_shim_->GetNodeByID(folder_id, is_partner_bookmark); |
+ if (bookmarks) { |
+ DictionaryValue result; |
+ PopulateBookmarksInFolder(bookmarks, &result); |
+ SendResult(result); |
+ } else { |
+ // If we receive an ID that no longer maps to a bookmark folder, just |
+ // return the initial bookmark folder. |
+ QueryInitialBookmarks(); |
+ } |
+} |
+ |
+void BookmarksHandler::QueryInitialBookmarks() { |
+ DictionaryValue result; |
+ DCHECK(partner_bookmarks_shim_ != NULL); |
+ PopulateBookmarksInFolder( |
+ // We have to go to the partner Root if it exists |
+ partner_bookmarks_shim_->HasPartnerBookmarks() ? |
+ partner_bookmarks_shim_->GetPartnerBookmarksRoot() : |
+ bookmark_model_->mobile_node(), |
+ &result); |
+ SendResult(result); |
+} |
+ |
+void BookmarksHandler::SendResult(const DictionaryValue& result) { |
+ web_ui()->CallJavascriptFunction("ntp.bookmarks", result); |
+} |
+ |
+void BookmarksHandler::Loaded(BookmarkModel* model, bool ids_reassigned) { |
+ BookmarkModelChanged(); |
+} |
+ |
+void BookmarksHandler::PartnerShimLoaded(PartnerBookmarksShim* shim) { |
+ BookmarkModelChanged(); |
+} |
+ |
+void BookmarksHandler::ShimBeingDeleted(PartnerBookmarksShim* shim) { |
+ partner_bookmarks_shim_ = NULL; |
+} |
+ |
+void BookmarksHandler::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) { |
+ extensive_changes_ = true; |
+} |
+ |
+void BookmarksHandler::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { |
+ extensive_changes_ = false; |
+ BookmarkModelChanged(); |
+} |
+ |
+void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model, |
+ const BookmarkNode* parent, |
+ int old_index, |
+ const BookmarkNode* node) { |
+ DictionaryValue result; |
+ SetParentInBookmarksResult(parent, &result); |
+ result.SetString(kNodeIdParam, Int64ToString(node->id())); |
+ NotifyModelChanged(result); |
+} |
+ |
+void BookmarksHandler::BookmarkNodeAdded(BookmarkModel* model, |
+ const BookmarkNode* parent, |
+ int index) { |
+ DictionaryValue result; |
+ SetParentInBookmarksResult(parent, &result); |
+ NotifyModelChanged(result); |
+} |
+ |
+void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model, |
+ const BookmarkNode* node) { |
+ DCHECK(partner_bookmarks_shim_); |
+ DCHECK(!partner_bookmarks_shim_->IsPartnerBookmark(node)); |
+ DictionaryValue result; |
+ SetParentInBookmarksResult(node->parent(), &result); |
+ result.SetString(kNodeIdParam, Int64ToString(node->id())); |
+ NotifyModelChanged(result); |
+} |
+ |
+void BookmarksHandler::BookmarkModelChanged() { |
+ if (bookmark_data_requested_ && !extensive_changes_) |
+ web_ui()->CallJavascriptFunction("ntp.bookmarkChanged"); |
+} |
+ |
+void BookmarksHandler::NotifyModelChanged(const DictionaryValue& status) { |
+ if (bookmark_data_requested_ && !extensive_changes_) |
+ web_ui()->CallJavascriptFunction("ntp.bookmarkChanged", status); |
+} |
+ |
+void BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut( |
+ const ListValue* args) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ Profile* profile = Profile::FromBrowserContext( |
+ web_ui()->GetWebContents()->GetBrowserContext()); |
+ if (!profile) |
+ return; |
+ |
+ int64 id; |
+ bool is_partner; |
+ if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) { |
+ DCHECK(partner_bookmarks_shim_ != NULL); |
+ const BookmarkNode* node = |
+ partner_bookmarks_shim_->GetNodeByID(id, is_partner); |
+ if (!node) |
+ return; |
+ |
+ FaviconService* favicon_service = profile->GetFaviconService( |
+ Profile::EXPLICIT_ACCESS); |
+ FaviconService::Handle handle = favicon_service->GetFaviconForURL( |
+ node->url(), |
+ history::FAVICON | history::TOUCH_ICON, |
+ &cancelable_consumer_, |
+ base::Bind(&BookmarksHandler::OnShortcutFaviconDataAvailable, |
+ base::Unretained(this))); |
+ cancelable_consumer_.SetClientData(favicon_service, handle, node); |
+ } |
+} |
+ |
+void BookmarksHandler::OnShortcutFaviconDataAvailable( |
+ FaviconService::Handle handle, |
+ history::FaviconData favicon) { |
+ SkColor color = SK_ColorWHITE; |
+ SkBitmap favicon_bitmap; |
+ if (favicon.is_valid()) { |
+ color = GetDominantColorForFavicon(favicon.image_data); |
+ gfx::PNGCodec::Decode(favicon.image_data->front(), |
+ favicon.image_data->size(), |
+ &favicon_bitmap); |
+ } |
+ |
+ Profile* profile = Profile::FromBrowserContext( |
+ web_ui()->GetWebContents()->GetBrowserContext()); |
+ const BookmarkNode* node = cancelable_consumer_.GetClientData( |
+ profile->GetFaviconService(Profile::EXPLICIT_ACCESS), handle); |
+ |
+ TabAndroid* tab = TabAndroid::FromWebContents( |
+ web_ui()->GetWebContents()); |
+ if (tab) { |
+ tab->AddShortcutToBookmark(node->url(), node->GetTitle(), |
+ favicon_bitmap, SkColorGetR(color), |
+ SkColorGetG(color), SkColorGetB(color)); |
+ } |
+} |