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