blob: 208027adce08516b464e3832e76049eaddff79a0 [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 "base/lazy_instance.h"
#include "base/prefs/pref_service.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"
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 {
scoped_ptr<BookmarkNode> partner_bookmarks_root;
bool loaded;
PartnerModelKeeper()
: loaded(false) {}
};
base::LazyInstance<PartnerModelKeeper> g_partner_model_keeper =
LAZY_INSTANCE_INITIALIZER;
const void* kPartnerBookmarksShimUserDataKey =
&kPartnerBookmarksShimUserDataKey;
// Dictionary keys for entries in the kPartnerBookmarksMapping pref.
static const char kMappingUrl[] = "url";
static const char kMappingProviderTitle[] = "provider_title";
static const char kMappingTitle[] = "mapped_title";
} // namespace
// static
PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
content::BrowserContext* browser_context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
PartnerBookmarksShim* data =
reinterpret_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,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}
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;
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;
}
void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
RenameBookmark(node, base::string16());
}
void PartnerBookmarksShim::RenameBookmark(const BookmarkNode* node,
const base::string16& title) {
const NodeRenamingMapKey key(node->url(), node->GetTitle());
node_rename_remove_map_[key] = title;
SaveNodeMapping();
FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
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 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));
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(BookmarkNode* root_node) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
g_partner_model_keeper.Get().partner_bookmarks_root.reset(root_node);
g_partner_model_keeper.Get().loaded = true;
FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
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 (a.url_ < b.url_) ||
(a.url_ == b.url_ && a.provider_title_ < b.provider_title_);
}
// static
void PartnerBookmarksShim::ClearInBrowserContextForTesting(
content::BrowserContext* browser_context) {
DCHECK(BrowserThread::CurrentlyOn(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);
}
PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
: prefs_(prefs),
observers_(
ObserverList<PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
}
PartnerBookmarksShim::~PartnerBookmarksShim() {
FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
ShimBeingDeleted(this));
}
const BookmarkNode* PartnerBookmarksShim::GetNodeByID(
const BookmarkNode* parent, int64 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(BrowserThread::CurrentlyOn(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 || !(*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(BrowserThread::CurrentlyOn(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) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetString(kMappingUrl, i->first.url().spec());
dict->SetString(kMappingProviderTitle, i->first.provider_title());
dict->SetString(kMappingTitle, i->second);
list.Append(dict);
}
prefs_->Set(prefs::kPartnerBookmarkMappings, list);
}