• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PartnerModelKeeper__anon7fbe3dff0111::PartnerModelKeeper31   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
BuildForBrowserContext(content::BrowserContext * browser_context)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
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)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
DisablePartnerBookmarksEditing()77 void PartnerBookmarksShim::DisablePartnerBookmarksEditing() {
78   g_disable_partner_bookmarks_editing = true;
79 }
80 
IsLoaded() const81 bool PartnerBookmarksShim::IsLoaded() const {
82   return g_partner_model_keeper.Get().loaded;
83 }
84 
HasPartnerBookmarks() const85 bool PartnerBookmarksShim::HasPartnerBookmarks() const {
86   DCHECK(IsLoaded());
87   return g_partner_model_keeper.Get().partner_bookmarks_root.get() != NULL;
88 }
89 
IsReachable(const BookmarkNode * node) const90 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 
IsEditable(const BookmarkNode * node) const105 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 
RemoveBookmark(const BookmarkNode * node)114 void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode* node) {
115   DCHECK(IsEditable(node));
116   RenameBookmark(node, base::string16());
117 }
118 
RenameBookmark(const BookmarkNode * node,const base::string16 & title)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 
AddObserver(PartnerBookmarksShim::Observer * observer)129 void PartnerBookmarksShim::AddObserver(
130     PartnerBookmarksShim::Observer* observer) {
131   observers_.AddObserver(observer);
132 }
133 
RemoveObserver(PartnerBookmarksShim::Observer * observer)134 void PartnerBookmarksShim::RemoveObserver(
135     PartnerBookmarksShim::Observer* observer) {
136   observers_.RemoveObserver(observer);
137 }
138 
GetNodeByID(int64 id) const139 const BookmarkNode* PartnerBookmarksShim::GetNodeByID(int64 id) const {
140   DCHECK(IsLoaded());
141   if (!HasPartnerBookmarks())
142     return NULL;
143   return GetNodeByID(GetPartnerBookmarksRoot(), id);
144 }
145 
GetTitle(const BookmarkNode * node) const146 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 
IsPartnerBookmark(const BookmarkNode * node) const160 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 
GetPartnerBookmarksRoot() const173 const BookmarkNode* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
174   return g_partner_model_keeper.Get().partner_bookmarks_root.get();
175 }
176 
SetPartnerBookmarksRoot(BookmarkNode * root_node)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 
NodeRenamingMapKey(const GURL & url,const base::string16 & provider_title)185 PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
186     const GURL& url, const base::string16& provider_title)
187     : url_(url), provider_title_(provider_title) {}
188 
~NodeRenamingMapKey()189 PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}
190 
operator <(const PartnerBookmarksShim::NodeRenamingMapKey & a,const PartnerBookmarksShim::NodeRenamingMapKey & b)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
ClearInBrowserContextForTesting(content::BrowserContext * browser_context)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
ClearPartnerModelForTesting()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
EnablePartnerBookmarksEditing()211 void PartnerBookmarksShim::EnablePartnerBookmarksEditing() {
212   g_disable_partner_bookmarks_editing = false;
213 }
214 
PartnerBookmarksShim(PrefService * prefs)215 PartnerBookmarksShim::PartnerBookmarksShim(PrefService* prefs)
216     : prefs_(prefs),
217       observers_(
218           ObserverList<PartnerBookmarksShim::Observer>::NOTIFY_EXISTING_ONLY) {
219 }
220 
~PartnerBookmarksShim()221 PartnerBookmarksShim::~PartnerBookmarksShim() {
222   FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer, observers_,
223                     ShimBeingDeleted(this));
224 }
225 
GetNodeByID(const BookmarkNode * parent,int64 id) const226 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 
ReloadNodeMapping()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 
SaveNodeMapping()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