This source file includes following definitions.
- RecordBookmarkAction
 
- BookmarkTypeAsString
 
- GetDominantColorForFavicon
 
- extensive_changes_
 
- RegisterMessages
 
- HandleGetBookmarks
 
- HandleDeleteBookmark
 
- HandleEditBookmark
 
- AreModelsLoaded
 
- NotifyModelChanged
 
- GetBookmarkIdForNtp
 
- SetParentInBookmarksResult
 
- PopulateBookmark
 
- PopulateBookmarksInFolder
 
- QueryBookmarkFolder
 
- QueryInitialBookmarks
 
- SendResult
 
- BookmarkModelLoaded
 
- PartnerShimChanged
 
- PartnerShimLoaded
 
- ShimBeingDeleted
 
- OnManagedBookmarksChanged
 
- ExtensiveBookmarkChangesBeginning
 
- ExtensiveBookmarkChangesEnded
 
- BookmarkNodeRemoved
 
- BookmarkAllNodesRemoved
 
- BookmarkNodeAdded
 
- BookmarkNodeChanged
 
- BookmarkModelChanged
 
- HandleCreateHomeScreenBookmarkShortcut
 
- OnShortcutFaviconDataAvailable
 
- GetNodeByID
 
- GetParentOf
 
- GetTitle
 
- IsReachable
 
- IsEditable
 
- IsRoot
 
#include "chrome/browser/ui/webui/ntp/android/bookmarks_handler.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/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/favicon/favicon_service_factory.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/url_data_source.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"
#include "ui/gfx/favicon_size.h"
using base::Int64ToString;
using content::BrowserThread;
namespace {
static const char* kParentIdParam = "parent_id";
static const char* kNodeIdParam = "node_id";
enum PartnerBookmarkAction {
  BOOKMARK_ACTION_DELETE_BOOKMARK_PARTNER = 0,
  BOOKMARK_ACTION_DELETE_ROOT_FOLDER_PARTNER = 1,
  BOOKMARK_ACTION_EDIT_BOOKMARK_PARTNER = 2,
  BOOKMARK_ACTION_EDIT_ROOT_FOLDER_PARTNER = 3,
  BOOKMARK_ACTION_BUCKET_BOUNDARY = 4
};
void RecordBookmarkAction(PartnerBookmarkAction type) {
  UMA_HISTOGRAM_ENUMERATION("NewTabPage.BookmarkActionAndroid", type,
                            BOOKMARK_ACTION_BUCKET_BOUNDARY);
}
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;
  
  
  
  
  return color_utils::CalculateKMeanColorOfPNG(png, 100, 665, &sampler);
}
}  
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);
  if (managed_bookmarks_shim_)
    managed_bookmarks_shim_->RemoveObserver(this);
}
void BookmarksHandler::RegisterMessages() {
  
  
  Profile* profile = Profile::FromBrowserContext(
      web_ui()->GetWebContents()->GetBrowserContext());
  content::URLDataSource::Add(
      profile, new FaviconSource(profile, FaviconSource::ANY));
  bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
  if (bookmark_model_) {
    bookmark_model_->AddObserver(this);
    
    
    
    extensive_changes_ = bookmark_model_->IsDoingExtensiveChanges();
  }
  
  if (!partner_bookmarks_shim_) {
    partner_bookmarks_shim_ = PartnerBookmarksShim::BuildForBrowserContext(
        chrome::GetBrowserContextRedirectedInIncognito(
            web_ui()->GetWebContents()->GetBrowserContext()));
    partner_bookmarks_shim_->AddObserver(this);
  }
  managed_bookmarks_shim_.reset(new ManagedBookmarksShim(profile->GetPrefs()));
  managed_bookmarks_shim_->AddObserver(this);
  
  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("editBookmark",
        base::Bind(&BookmarksHandler::HandleEditBookmark,
                   base::Unretained(this)));
  web_ui()->RegisterMessageCallback("createHomeScreenBookmarkShortcut",
      base::Bind(&BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut,
                 base::Unretained(this)));
}
void BookmarksHandler::HandleGetBookmarks(const base::ListValue* args) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  bookmark_data_requested_ = true;
  if (!AreModelsLoaded())
    return;  
  const BookmarkNode* node = GetNodeByID(args);
  if (node)
    QueryBookmarkFolder(node);
  else
    QueryInitialBookmarks();
}
void BookmarksHandler::HandleDeleteBookmark(const base::ListValue* args) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!AreModelsLoaded())
    return;
  const BookmarkNode* node = GetNodeByID(args);
  if (!node)
    return;
  if (!IsEditable(node)) {
    NOTREACHED();
    return;
  }
  if (partner_bookmarks_shim_->IsPartnerBookmark(node)) {
    if (partner_bookmarks_shim_->GetPartnerBookmarksRoot() == node)
      RecordBookmarkAction(BOOKMARK_ACTION_DELETE_ROOT_FOLDER_PARTNER);
    else
      RecordBookmarkAction(BOOKMARK_ACTION_DELETE_BOOKMARK_PARTNER);
    partner_bookmarks_shim_->RemoveBookmark(node);
    return;
  }
  const BookmarkNode* parent_node = node->parent();
  bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
}
void BookmarksHandler::HandleEditBookmark(const base::ListValue* args) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!AreModelsLoaded())
    return;
  const BookmarkNode* node = GetNodeByID(args);
  if (!node)
    return;
  if (!IsEditable(node)) {
    NOTREACHED();
    return;
  }
  TabAndroid* tab = TabAndroid::FromWebContents(web_ui()->GetWebContents());
  if (tab) {
    if (partner_bookmarks_shim_->IsPartnerBookmark(node)) {
      if (partner_bookmarks_shim_->GetPartnerBookmarksRoot() == node)
        RecordBookmarkAction(BOOKMARK_ACTION_EDIT_ROOT_FOLDER_PARTNER);
      else
        RecordBookmarkAction(BOOKMARK_ACTION_EDIT_BOOKMARK_PARTNER);
    }
    tab->EditBookmark(node->id(),
                      GetTitle(node),
                      node->is_folder(),
                      partner_bookmarks_shim_->IsPartnerBookmark(node));
  }
}
bool BookmarksHandler::AreModelsLoaded() const {
  Profile* profile = Profile::FromBrowserContext(
      web_ui()->GetWebContents()->GetBrowserContext());
  if (!profile)
    return false;
  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
  if (!model || !model->loaded())
    return false;
  return partner_bookmarks_shim_ && partner_bookmarks_shim_->IsLoaded();
}
void BookmarksHandler::NotifyModelChanged(const base::DictionaryValue& status) {
  DCHECK(AreModelsLoaded());
  if (bookmark_data_requested_ && !extensive_changes_)
    web_ui()->CallJavascriptFunction("ntp.bookmarkChanged", status);
}
std::string BookmarksHandler::GetBookmarkIdForNtp(const BookmarkNode* node) {
  DCHECK(AreModelsLoaded());
  std::string prefix;
  if (partner_bookmarks_shim_->IsPartnerBookmark(node))
    prefix = "p";
  else if (managed_bookmarks_shim_->IsManagedBookmark(node))
    prefix = "m";
  return prefix + Int64ToString(node->id());
}
void BookmarksHandler::SetParentInBookmarksResult(
    const BookmarkNode* parent,
    base::DictionaryValue* result) {
  result->SetString(kParentIdParam, GetBookmarkIdForNtp(parent));
}
void BookmarksHandler::PopulateBookmark(const BookmarkNode* node,
                                        base::ListValue* result) {
  if (!result)
    return;
  DCHECK(AreModelsLoaded());
  if (!IsReachable(node))
    return;
  base::DictionaryValue* filler_value = new base::DictionaryValue();
  filler_value->SetString("title", GetTitle(node));
  filler_value->SetBoolean("editable", IsEditable(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,
    base::DictionaryValue* result) {
  DCHECK(AreModelsLoaded());
  if (!IsReachable(folder))
    return;
  base::ListValue* bookmarks = new base::ListValue();
  
  
  if (bookmark_model_ && folder == bookmark_model_->mobile_node() &&
      managed_bookmarks_shim_->HasManagedBookmarks()) {
    PopulateBookmark(managed_bookmarks_shim_->GetManagedBookmarksRoot(),
                     bookmarks);
  }
  for (int i = 0; i < folder->child_count(); i++) {
    const BookmarkNode* bookmark= folder->GetChild(i);
    PopulateBookmark(bookmark, bookmarks);
  }
  
  if (bookmark_model_ && folder == bookmark_model_->mobile_node() &&
      partner_bookmarks_shim_->HasPartnerBookmarks()) {
    PopulateBookmark(partner_bookmarks_shim_->GetPartnerBookmarksRoot(),
                     bookmarks);
  }
  base::ListValue* folder_hierarchy = new base::ListValue();
  const BookmarkNode* parent = GetParentOf(folder);
  while (parent != NULL) {
    base::DictionaryValue* hierarchy_entry = new base::DictionaryValue();
    if (IsRoot(parent))
      hierarchy_entry->SetBoolean("root", true);
    hierarchy_entry->SetString("title", GetTitle(parent));
    hierarchy_entry->SetString("id", GetBookmarkIdForNtp(parent));
    folder_hierarchy->Append(hierarchy_entry);
    parent = GetParentOf(parent);
  }
  result->SetString("title", GetTitle(folder));
  result->SetString("id", GetBookmarkIdForNtp(folder));
  result->SetBoolean("root", IsRoot(folder));
  result->Set("bookmarks", bookmarks);
  result->Set("hierarchy", folder_hierarchy);
}
void BookmarksHandler::QueryBookmarkFolder(const BookmarkNode* node) {
  DCHECK(AreModelsLoaded());
  if (node->is_folder() && IsReachable(node)) {
    base::DictionaryValue result;
    PopulateBookmarksInFolder(node, &result);
    SendResult(result);
  } else {
    
    
    QueryInitialBookmarks();
  }
}
void BookmarksHandler::QueryInitialBookmarks() {
  DCHECK(AreModelsLoaded());
  base::DictionaryValue result;
  PopulateBookmarksInFolder(bookmark_model_->mobile_node(), &result);
  SendResult(result);
}
void BookmarksHandler::SendResult(const base::DictionaryValue& result) {
  web_ui()->CallJavascriptFunction("ntp.bookmarks", result);
}
void BookmarksHandler::BookmarkModelLoaded(BookmarkModel* model,
                                           bool ids_reassigned) {
  if (AreModelsLoaded())
    BookmarkModelChanged();
}
void BookmarksHandler::PartnerShimChanged(PartnerBookmarksShim* shim) {
  if (AreModelsLoaded())
    BookmarkModelChanged();
}
void BookmarksHandler::PartnerShimLoaded(PartnerBookmarksShim* shim) {
  if (AreModelsLoaded())
    BookmarkModelChanged();
}
void BookmarksHandler::ShimBeingDeleted(PartnerBookmarksShim* shim) {
  partner_bookmarks_shim_ = NULL;
}
void BookmarksHandler::OnManagedBookmarksChanged() {
  if (AreModelsLoaded())
    BookmarkModelChanged();
}
void BookmarksHandler::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
  extensive_changes_ = true;
}
void BookmarksHandler::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
  extensive_changes_ = false;
  if (AreModelsLoaded())
    BookmarkModelChanged();
}
void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model,
                                           const BookmarkNode* parent,
                                           int old_index,
                                           const BookmarkNode* node) {
  if (!AreModelsLoaded())
    return;
  base::DictionaryValue result;
  SetParentInBookmarksResult(parent, &result);
  result.SetString(kNodeIdParam, Int64ToString(node->id()));
  NotifyModelChanged(result);
}
void BookmarksHandler::BookmarkAllNodesRemoved(BookmarkModel* model) {
  if (!AreModelsLoaded())
    return;
  if (bookmark_data_requested_ && !extensive_changes_)
    web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
}
void BookmarksHandler::BookmarkNodeAdded(
    BookmarkModel* model, const BookmarkNode* parent, int index) {
  if (!AreModelsLoaded())
    return;
  base::DictionaryValue result;
  SetParentInBookmarksResult(parent, &result);
  NotifyModelChanged(result);
}
void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model,
                                           const BookmarkNode* node) {
  if (!AreModelsLoaded())
    return;
  DCHECK(!partner_bookmarks_shim_->IsPartnerBookmark(node));
  base::DictionaryValue result;
  SetParentInBookmarksResult(node->parent(), &result);
  result.SetString(kNodeIdParam, Int64ToString(node->id()));
  NotifyModelChanged(result);
}
void BookmarksHandler::BookmarkModelChanged() {
  if (!AreModelsLoaded())
    return;
  if (bookmark_data_requested_ && !extensive_changes_)
    web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
}
void BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut(
    const base::ListValue* args) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!AreModelsLoaded())
    return;
  Profile* profile = Profile::FromBrowserContext(
      web_ui()->GetWebContents()->GetBrowserContext());
  if (!profile)
    return;
  const BookmarkNode* node = GetNodeByID(args);
  if (!node)
    return;
  FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
      profile, Profile::EXPLICIT_ACCESS);
  favicon_service->GetRawFaviconForURL(
      FaviconService::FaviconForURLParams(
          node->url(),
          chrome::TOUCH_PRECOMPOSED_ICON | chrome::TOUCH_ICON |
              chrome::FAVICON,
          0),  
      ui::SCALE_FACTOR_100P,  
      base::Bind(&BookmarksHandler::OnShortcutFaviconDataAvailable,
                 base::Unretained(this),
                 node),
      &cancelable_task_tracker_);
}
void BookmarksHandler::OnShortcutFaviconDataAvailable(
    const BookmarkNode* node,
    const chrome::FaviconBitmapResult& bitmap_result) {
  if (!AreModelsLoaded())
    return;
  SkColor color = SK_ColorWHITE;
  SkBitmap favicon_bitmap;
  if (bitmap_result.is_valid()) {
    color = GetDominantColorForFavicon(bitmap_result.bitmap_data);
    gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
                          bitmap_result.bitmap_data->size(),
                          &favicon_bitmap);
  }
  TabAndroid* tab = TabAndroid::FromWebContents(web_ui()->GetWebContents());
  if (tab) {
    tab->AddShortcutToBookmark(node->url(),
                               GetTitle(node),
                               favicon_bitmap, SkColorGetR(color),
                               SkColorGetG(color), SkColorGetB(color));
  }
}
const BookmarkNode* BookmarksHandler::GetNodeByID(
    const base::ListValue* args) const {
  DCHECK(AreModelsLoaded());
  
  
  
  if (!args || args->empty())
    return NULL;
  std::string string_id;
  if (!args->GetString(0, &string_id) || string_id.empty()) {
    NOTREACHED();
    return NULL;
  }
  bool is_partner = string_id[0] == 'p';
  bool is_managed = string_id[0] == 'm';
  if (is_partner || is_managed)
    string_id = string_id.substr(1);
  int64 id = 0;
  if (!base::StringToInt64(string_id, &id)) {
    NOTREACHED();
    return NULL;
  }
  if (is_managed)
    return managed_bookmarks_shim_->GetNodeByID(id);
  if (is_partner)
    return partner_bookmarks_shim_->GetNodeByID(id);
  return bookmark_model_->GetNodeByID(id);
}
const BookmarkNode* BookmarksHandler::GetParentOf(
    const BookmarkNode* node) const {
  DCHECK(AreModelsLoaded());
  if (node == managed_bookmarks_shim_->GetManagedBookmarksRoot() ||
      node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) {
    return bookmark_model_->mobile_node();
  }
  return node->parent();
}
base::string16 BookmarksHandler::GetTitle(const BookmarkNode* node) const {
  DCHECK(AreModelsLoaded());
  if (partner_bookmarks_shim_->IsPartnerBookmark(node))
    return partner_bookmarks_shim_->GetTitle(node);
  return node->GetTitle();
}
bool BookmarksHandler::IsReachable(const BookmarkNode* node) const {
  DCHECK(AreModelsLoaded());
  if (!partner_bookmarks_shim_->IsPartnerBookmark(node))
    return true;
  return partner_bookmarks_shim_->IsReachable(node);
}
bool BookmarksHandler::IsEditable(const BookmarkNode* node) const {
  DCHECK(AreModelsLoaded());
  
  
  
  if (!node ||
      (node->type() != BookmarkNode::FOLDER &&
          node->type() != BookmarkNode::URL)) {
    return false;
  }
  const PrefService* pref = Profile::FromBrowserContext(
      web_ui()->GetWebContents()->GetBrowserContext())->GetPrefs();
  if (!pref->GetBoolean(prefs::kEditBookmarksEnabled))
    return false;
  if (partner_bookmarks_shim_->IsPartnerBookmark(node))
    return true;
  return !managed_bookmarks_shim_->IsManagedBookmark(node);
}
bool BookmarksHandler::IsRoot(const BookmarkNode* node) const {
  DCHECK(AreModelsLoaded());
  return node->is_root() &&
         node != partner_bookmarks_shim_->GetPartnerBookmarksRoot() &&
         node != managed_bookmarks_shim_->GetManagedBookmarksRoot();
}