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