blob: b1cc32211705af4a3c80315f1a52de1b66a298c2 [file] [log] [blame]
// Copyright 2013 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_shim.h"
#include <tuple>
#include <utility>
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
using bookmarks::BookmarkNode;
using content::BrowserThread;
namespace {
// PartnerModelKeeper is used as a singleton to store an immutable hierarchy
// of partner bookmarks. The hierarchy is retrieved from the partner bookmarks
// provider and doesn't depend on the user profile.
// The retrieved hierarchy persists
// PartnerBookmarksShim is responsible to applying and storing the user changes
// (deletions/renames) in the user profile, thus keeping the hierarchy intact.
struct PartnerModelKeeper {
std::unique_ptr<BookmarkNode> partner_bookmarks_root;
bool loaded;
PartnerModelKeeper()
: loaded(false) {}
};
base::LazyInstance<PartnerModelKeeper>::DestructorAtExit
g_partner_model_keeper = LAZY_INSTANCE_INITIALIZER;
const void* const kPartnerBookmarksShimUserDataKey =
&kPartnerBookmarksShimUserDataKey;
// Dictionary keys for entries in the kPartnerBookmarksMapping pref.
const char kMappingUrl[] = "url";
const char kMappingProviderTitle[] = "provider_title";
const char kMappingTitle[] = "mapped_title";
bool g_disable_partner_bookmarks_editing = false;
} // namespace
// static
PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
content::BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PartnerBookmarksShim* data =
static_cast<PartnerBookmarksShim*>(
browser_context->GetUserData(kPartnerBookmarksShimUserDataKey));
if (data)
return data;
data = new PartnerBookmarksShim(
Profile::FromBrowserContext(browser_context)->GetPrefs());
browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, data);
data->ReloadNodeMapping();
return data;
}
// static
void PartnerBookmarksShim::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterListPref(prefs::kPartnerBookmarkMappings);
}
// static
void PartnerBookmarksShim::DisablePartnerBookmarksEditing() {
g_disable_partner_bookmarks_editing = true;
}
bool PartnerBookmarksShim::IsLoaded() const {
return g_partner_model_keeper.Get().loaded;
}
bool PartnerBookmarksShim::HasPartnerBookmarks() const {
DCHECK(IsLoaded());
return g_partner_model_keeper.Get().partner_bookmarks_root.get() != NULL;
}
bool PartnerBookmarksShim::IsReachable(const BookmarkNode* node) const {
DCHECK(IsPartnerBookmark(node));
if (!HasPartnerBookmarks())
return false;
if (!g_disable_partner_bookmarks_editing) {
for (const BookmarkNode* i = node; i != NULL; i = i->parent()) {
const NodeRenamingMapKey key(i->url(), i->GetTitle());
NodeRenamingMap::const_iterator remap = node_rename_remove_map_.find(key);
if (remap != node_rename_remove_map_.end() && remap->second.empty())
return false;
}
}
return true;
}
bool PartnerBookmarksShim::IsEditable(const BookmarkNode* node) const {
DCHECK(IsPartnerBookmark(node));
if (!HasPartnerBookmarks())
return false;
if (g_disable_partner_bookmarks_editing)
return false;
return true;
}
void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
DCHECK(IsEditable(node));
RenameBookmark(node, base::string16());
}
void PartnerBookmarksShim::RenameBookmark(const BookmarkNode* node,
const base::string16& title) {
DCHECK(IsEditable(node));
const NodeRenamingMapKey key(node->url(), node->GetTitle());
node_rename_remove_map_[key] = title;
SaveNodeMapping();
for (PartnerBookmarksShim::Observer& observer : observers_)
observer.PartnerShimChanged(this);
}
void PartnerBookmarksShim::AddObserver(
PartnerBookmarksShim::Observer* observer) {
observers_.AddObserver(observer);
}
void PartnerBookmarksShim::RemoveObserver(
PartnerBookmarksShim::Observer* observer) {
observers_.RemoveObserver(observer);
}
const BookmarkNode* PartnerBookmarksShim::GetNodeByID(int64_t id) const {
DCHECK(IsLoaded());
if (!HasPartnerBookmarks())
return NULL;
return GetNodeByID(GetPartnerBookmarksRoot(), id);
}
base::string16 PartnerBookmarksShim::GetTitle(const BookmarkNode* node) const {
DCHECK(node);
DCHECK(IsPartnerBookmark(node));
if (!g_disable_partner_bookmarks_editing) {
const NodeRenamingMapKey key(node->url(), node->GetTitle());
NodeRenamingMap::const_iterator i = node_rename_remove_map_.find(key);
if (i != node_rename_remove_map_.end())
return i->second;
}
return node->GetTitle();
}
bool PartnerBookmarksShim::IsPartnerBookmark(const BookmarkNode* node) const {
DCHECK(IsLoaded());
if (!HasPartnerBookmarks())
return false;
const BookmarkNode* parent = node;
while (parent) {
if (parent == GetPartnerBookmarksRoot())
return true;
parent = parent->parent();
}
return false;
}
const BookmarkNode* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
return g_partner_model_keeper.Get().partner_bookmarks_root.get();
}
void PartnerBookmarksShim::SetPartnerBookmarksRoot(
std::unique_ptr<bookmarks::BookmarkNode> root_node) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
g_partner_model_keeper.Get().partner_bookmarks_root = std::move(root_node);
g_partner_model_keeper.Get().loaded = true;
for (PartnerBookmarksShim::Observer& observer : observers_)
observer.PartnerShimLoaded(this);
}
PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
const GURL& url, const base::string16& provider_title)
: url_(url), provider_title_(provider_title) {}
PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}
bool operator<(const PartnerBookmarksShim::NodeRenamingMapKey& a,
const PartnerBookmarksShim::NodeRenamingMapKey& b) {
return std::tie(a.url_, a.provider_title_) <
std::tie(b.url_, b.provider_title_);
}
// static
void PartnerBookmarksShim::ClearInBrowserContextForTesting(
content::BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, 0);
}
// static
void PartnerBookmarksShim::ClearPartnerModelForTesting() {
g_partner_model_keeper.Get().loaded = false;
g_partner_model_keeper.Get().partner_bookmarks_root.reset(0);
}
// static
void PartnerBookmarksShim::EnablePartnerBookmarksEditing() {
g_disable_partner_bookmarks_editing = false;
}
PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
: prefs_(prefs),
observers_(base::ObserverList<
PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
}
PartnerBookmarksShim::~PartnerBookmarksShim() {
for (PartnerBookmarksShim::Observer& observer : observers_)
observer.ShimBeingDeleted(this);
}
const BookmarkNode* PartnerBookmarksShim::GetNodeByID(
const BookmarkNode* parent,
int64_t id) const {
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;
}
void PartnerBookmarksShim::ReloadNodeMapping() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
node_rename_remove_map_.clear();
if (!prefs_)
return;
const base::ListValue* list =
prefs_->GetList(prefs::kPartnerBookmarkMappings);
if (!list)
return;
for (base::ListValue::const_iterator it = list->begin();
it != list->end(); ++it) {
const base::DictionaryValue* dict = NULL;
if (!it->GetAsDictionary(&dict)) {
NOTREACHED();
continue;
}
std::string url;
base::string16 provider_title;
base::string16 mapped_title;
if (!dict->GetString(kMappingUrl, &url) ||
!dict->GetString(kMappingProviderTitle, &provider_title) ||
!dict->GetString(kMappingTitle, &mapped_title)) {
NOTREACHED();
continue;
}
const NodeRenamingMapKey key(GURL(url), provider_title);
node_rename_remove_map_[key] = mapped_title;
}
}
void PartnerBookmarksShim::SaveNodeMapping() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!prefs_)
return;
base::ListValue list;
for (NodeRenamingMap::const_iterator i = node_rename_remove_map_.begin();
i != node_rename_remove_map_.end();
++i) {
auto dict = base::MakeUnique<base::DictionaryValue>();
dict->SetString(kMappingUrl, i->first.url().spec());
dict->SetString(kMappingProviderTitle, i->first.provider_title());
dict->SetString(kMappingTitle, i->second);
list.Append(std::move(dict));
}
prefs_->Set(prefs::kPartnerBookmarkMappings, list);
}