Home | History | Annotate | Download | only in bookmarks
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h"
      6 
      7 #include "base/lazy_instance.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/values.h"
     10 #include "chrome/browser/bookmarks/bookmark_model.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/common/pref_names.h"
     13 #include "components/user_prefs/pref_registry_syncable.h"
     14 #include "content/public/browser/browser_thread.h"
     15 
     16 using content::BrowserThread;
     17 
     18 namespace {
     19 
     20 // PartnerModelKeeper is used as a singleton to store an immutable hierarchy
     21 // of partner bookmarks.  The hierarchy is retrieved from the partner bookmarks
     22 // provider and doesn't depend on the user profile.
     23 // The retrieved hierarchy persists
     24 // PartnerBookmarksShim is responsible to applying and storing the user changes
     25 // (deletions/renames) in the user profile, thus keeping the hierarchy intact.
     26 struct PartnerModelKeeper {
     27   scoped_ptr<BookmarkNode> partner_bookmarks_root;
     28   bool loaded;
     29 
     30   PartnerModelKeeper()
     31     : loaded(false) {}
     32 };
     33 
     34 base::LazyInstance<PartnerModelKeeper> g_partner_model_keeper =
     35     LAZY_INSTANCE_INITIALIZER;
     36 
     37 const void* kPartnerBookmarksShimUserDataKey =
     38     &kPartnerBookmarksShimUserDataKey;
     39 
     40 // Dictionary keys for entries in the kPartnerBookmarksMapping pref.
     41 static const char kMappingUrl[] = "url";
     42 static const char kMappingProviderTitle[] = "provider_title";
     43 static const char kMappingTitle[] = "mapped_title";
     44 
     45 }  // namespace
     46 
     47 // static
     48 PartnerBookmarksShim* PartnerBookmarksShim::BuildForBrowserContext(
     49     content::BrowserContext* browser_context) {
     50   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     51 
     52   PartnerBookmarksShim* data =
     53       reinterpret_cast<PartnerBookmarksShim*>(
     54           browser_context->GetUserData(kPartnerBookmarksShimUserDataKey));
     55   if (data)
     56     return data;
     57 
     58   data = new PartnerBookmarksShim(
     59       Profile::FromBrowserContext(browser_context)->GetPrefs());
     60   browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, data);
     61   data->ReloadNodeMapping();
     62   return data;
     63 }
     64 
     65 // static
     66 void PartnerBookmarksShim::RegisterProfilePrefs(
     67     user_prefs::PrefRegistrySyncable* registry) {
     68   registry->RegisterListPref(
     69       prefs::kPartnerBookmarkMappings,
     70       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     71 }
     72 
     73 bool PartnerBookmarksShim::IsLoaded() const {
     74   return g_partner_model_keeper.Get().loaded;
     75 }
     76 
     77 bool PartnerBookmarksShim::HasPartnerBookmarks() const {
     78   DCHECK(IsLoaded());
     79   return g_partner_model_keeper.Get().partner_bookmarks_root.get() != NULL;
     80 }
     81 
     82 bool PartnerBookmarksShim::IsReachable(const BookmarkNode* node) const {
     83   DCHECK(IsPartnerBookmark(node));
     84   if (!HasPartnerBookmarks())
     85     return false;
     86   for (const BookmarkNode* i = node; i != NULL; i = i->parent()) {
     87     const NodeRenamingMapKey key(i->url(), i->GetTitle());
     88     NodeRenamingMap::const_iterator remap = node_rename_remove_map_.find(key);
     89     if (remap != node_rename_remove_map_.end() && remap->second.empty())
     90       return false;
     91   }
     92   return true;
     93 }
     94 
     95 void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
     96   RenameBookmark(node, base::string16());
     97 }
     98 
     99 void PartnerBookmarksShim::RenameBookmark(const BookmarkNode* node,
    100                                           const base::string16& title) {
    101   const NodeRenamingMapKey key(node->url(), node->GetTitle());
    102   node_rename_remove_map_[key] = title;
    103   SaveNodeMapping();
    104   FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
    105                     PartnerShimChanged(this));
    106 }
    107 
    108 void PartnerBookmarksShim::AddObserver(
    109     PartnerBookmarksShim::Observer* observer) {
    110   observers_.AddObserver(observer);
    111 }
    112 
    113 void PartnerBookmarksShim::RemoveObserver(
    114     PartnerBookmarksShim::Observer* observer) {
    115   observers_.RemoveObserver(observer);
    116 }
    117 
    118 const BookmarkNode* PartnerBookmarksShim::GetNodeByID(int64 id) const {
    119   DCHECK(IsLoaded());
    120   if (!HasPartnerBookmarks())
    121     return NULL;
    122   return GetNodeByID(GetPartnerBookmarksRoot(), id);
    123 }
    124 
    125 base::string16 PartnerBookmarksShim::GetTitle(const BookmarkNode* node) const {
    126   DCHECK(node);
    127   DCHECK(IsPartnerBookmark(node));
    128 
    129   const NodeRenamingMapKey key(node->url(), node->GetTitle());
    130   NodeRenamingMap::const_iterator i = node_rename_remove_map_.find(key);
    131   if (i != node_rename_remove_map_.end())
    132     return i->second;
    133 
    134   return node->GetTitle();
    135 }
    136 
    137 bool PartnerBookmarksShim::IsPartnerBookmark(const BookmarkNode* node) const {
    138   DCHECK(IsLoaded());
    139   if (!HasPartnerBookmarks())
    140     return false;
    141   const BookmarkNode* parent = node;
    142   while (parent) {
    143     if (parent == GetPartnerBookmarksRoot())
    144       return true;
    145     parent = parent->parent();
    146   }
    147   return false;
    148 }
    149 
    150 const BookmarkNode* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
    151   return g_partner_model_keeper.Get().partner_bookmarks_root.get();
    152 }
    153 
    154 void PartnerBookmarksShim::SetPartnerBookmarksRoot(BookmarkNode* root_node) {
    155   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    156   g_partner_model_keeper.Get().partner_bookmarks_root.reset(root_node);
    157   g_partner_model_keeper.Get().loaded = true;
    158   FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
    159                     PartnerShimLoaded(this));
    160 }
    161 
    162 PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
    163     const GURL& url, const base::string16& provider_title)
    164     : url_(url), provider_title_(provider_title) {}
    165 
    166 PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}
    167 
    168 bool operator<(const PartnerBookmarksShim::NodeRenamingMapKey& a,
    169                const PartnerBookmarksShim::NodeRenamingMapKey& b) {
    170   return (a.url_ < b.url_) ||
    171       (a.url_ == b.url_ && a.provider_title_ < b.provider_title_);
    172 }
    173 
    174 // static
    175 void PartnerBookmarksShim::ClearInBrowserContextForTesting(
    176     content::BrowserContext* browser_context) {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    178   browser_context->SetUserData(kPartnerBookmarksShimUserDataKey, 0);
    179 }
    180 
    181 // static
    182 void PartnerBookmarksShim::ClearPartnerModelForTesting() {
    183   g_partner_model_keeper.Get().loaded = false;
    184   g_partner_model_keeper.Get().partner_bookmarks_root.reset(0);
    185 }
    186 
    187 PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
    188     : prefs_(prefs),
    189       observers_(
    190           ObserverList<PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
    191 }
    192 
    193 PartnerBookmarksShim::~PartnerBookmarksShim() {
    194   FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
    195                     ShimBeingDeleted(this));
    196 }
    197 
    198 const BookmarkNode* PartnerBookmarksShim::GetNodeByID(
    199     const BookmarkNode* parent, int64 id) const {
    200   if (parent->id() == id)
    201     return parent;
    202   for (int i = 0, child_count = parent->child_count(); i < child_count; ++i) {
    203     const BookmarkNode* result = GetNodeByID(parent->GetChild(i), id);
    204     if (result)
    205       return result;
    206   }
    207   return NULL;
    208 }
    209 
    210 void PartnerBookmarksShim::ReloadNodeMapping() {
    211   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    212 
    213   node_rename_remove_map_.clear();
    214   if (!prefs_)
    215     return;
    216 
    217   const base::ListValue* list =
    218       prefs_->GetList(prefs::kPartnerBookmarkMappings);
    219   if (!list)
    220     return;
    221 
    222   for (base::ListValue::const_iterator it = list->begin();
    223        it != list->end(); ++it) {
    224     const base::DictionaryValue* dict = NULL;
    225     if (!*it || !(*it)->GetAsDictionary(&dict)) {
    226       NOTREACHED();
    227       continue;
    228     }
    229 
    230     std::string url;
    231     base::string16 provider_title;
    232     base::string16 mapped_title;
    233     if (!dict->GetString(kMappingUrl, &url) ||
    234         !dict->GetString(kMappingProviderTitle, &provider_title) ||
    235         !dict->GetString(kMappingTitle, &mapped_title)) {
    236       NOTREACHED();
    237       continue;
    238     }
    239 
    240     const NodeRenamingMapKey key(GURL(url), provider_title);
    241     node_rename_remove_map_[key] = mapped_title;
    242   }
    243 }
    244 
    245 void PartnerBookmarksShim::SaveNodeMapping() {
    246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    247   if (!prefs_)
    248     return;
    249 
    250   base::ListValue list;
    251   for (NodeRenamingMap::const_iterator i = node_rename_remove_map_.begin();
    252        i != node_rename_remove_map_.end();
    253        ++i) {
    254     base::DictionaryValue* dict = new base::DictionaryValue();
    255     dict->SetString(kMappingUrl, i->first.url().spec());
    256     dict->SetString(kMappingProviderTitle, i->first.provider_title());
    257     dict->SetString(kMappingTitle, i->second);
    258     list.Append(dict);
    259   }
    260   prefs_->Set(prefs::kPartnerBookmarkMappings, list);
    261 }
    262