// 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/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 "content/public/browser/browser_context.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 partner_bookmarks_root; bool loaded; PartnerModelKeeper() : loaded(false) {} }; base::LazyInstance 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"; static bool g_disable_partner_bookmarks_editing = false; } // namespace // static PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext( content::BrowserContext* browser_context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PartnerBookmarksShim* data = reinterpret_cast( 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); } // 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_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)); 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(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); } // static void PartnerBookmarksShim::EnablePartnerBookmarksEditing() { g_disable_partner_bookmarks_editing = false; } PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs) : prefs_(prefs), observers_( ObserverList::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); }