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.
5 #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h"
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"
17 using bookmarks::BookmarkNode
;
18 using content::BrowserThread
;
22 // PartnerModelKeeper is used as a singleton to store an immutable hierarchy
23 // of partner bookmarks. The hierarchy is retrieved from the partner bookmarks
24 // provider and doesn't depend on the user profile.
25 // The retrieved hierarchy persists
26 // PartnerBookmarksShim is responsible to applying and storing the user changes
27 // (deletions/renames) in the user profile, thus keeping the hierarchy intact.
28 struct PartnerModelKeeper
{
29 scoped_ptr
<BookmarkNode
> partner_bookmarks_root
;
36 base::LazyInstance
<PartnerModelKeeper
> g_partner_model_keeper
=
37 LAZY_INSTANCE_INITIALIZER
;
39 const void* kPartnerBookmarksShimUserDataKey
=
40 &kPartnerBookmarksShimUserDataKey
;
42 // Dictionary keys for entries in the kPartnerBookmarksMapping pref.
43 static const char kMappingUrl
[] = "url";
44 static const char kMappingProviderTitle
[] = "provider_title";
45 static const char kMappingTitle
[] = "mapped_title";
47 static bool g_disable_partner_bookmarks_editing
= false;
52 PartnerBookmarksShim
* PartnerBookmarksShim::BuildForBrowserContext(
53 content::BrowserContext
* browser_context
) {
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
56 PartnerBookmarksShim
* data
=
57 static_cast<PartnerBookmarksShim
*>(
58 browser_context
->GetUserData(kPartnerBookmarksShimUserDataKey
));
62 data
= new PartnerBookmarksShim(
63 Profile::FromBrowserContext(browser_context
)->GetPrefs());
64 browser_context
->SetUserData(kPartnerBookmarksShimUserDataKey
, data
);
65 data
->ReloadNodeMapping();
70 void PartnerBookmarksShim::RegisterProfilePrefs(
71 user_prefs::PrefRegistrySyncable
* registry
) {
72 registry
->RegisterListPref(
73 prefs::kPartnerBookmarkMappings
,
74 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
78 void PartnerBookmarksShim::DisablePartnerBookmarksEditing() {
79 g_disable_partner_bookmarks_editing
= true;
82 bool PartnerBookmarksShim::IsLoaded() const {
83 return g_partner_model_keeper
.Get().loaded
;
86 bool PartnerBookmarksShim::HasPartnerBookmarks() const {
88 return g_partner_model_keeper
.Get().partner_bookmarks_root
.get() != NULL
;
91 bool PartnerBookmarksShim::IsReachable(const BookmarkNode
* node
) const {
92 DCHECK(IsPartnerBookmark(node
));
93 if (!HasPartnerBookmarks())
95 if (!g_disable_partner_bookmarks_editing
) {
96 for (const BookmarkNode
* i
= node
; i
!= NULL
; i
= i
->parent()) {
97 const NodeRenamingMapKey
key(i
->url(), i
->GetTitle());
98 NodeRenamingMap::const_iterator remap
= node_rename_remove_map_
.find(key
);
99 if (remap
!= node_rename_remove_map_
.end() && remap
->second
.empty())
106 bool PartnerBookmarksShim::IsEditable(const BookmarkNode
* node
) const {
107 DCHECK(IsPartnerBookmark(node
));
108 if (!HasPartnerBookmarks())
110 if (g_disable_partner_bookmarks_editing
)
115 void PartnerBookmarksShim::RemoveBookmark(const BookmarkNode
* node
) {
116 DCHECK(IsEditable(node
));
117 RenameBookmark(node
, base::string16());
120 void PartnerBookmarksShim::RenameBookmark(const BookmarkNode
* node
,
121 const base::string16
& title
) {
122 DCHECK(IsEditable(node
));
123 const NodeRenamingMapKey
key(node
->url(), node
->GetTitle());
124 node_rename_remove_map_
[key
] = title
;
126 FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer
, observers_
,
127 PartnerShimChanged(this));
130 void PartnerBookmarksShim::AddObserver(
131 PartnerBookmarksShim::Observer
* observer
) {
132 observers_
.AddObserver(observer
);
135 void PartnerBookmarksShim::RemoveObserver(
136 PartnerBookmarksShim::Observer
* observer
) {
137 observers_
.RemoveObserver(observer
);
140 const BookmarkNode
* PartnerBookmarksShim::GetNodeByID(int64 id
) const {
142 if (!HasPartnerBookmarks())
144 return GetNodeByID(GetPartnerBookmarksRoot(), id
);
147 base::string16
PartnerBookmarksShim::GetTitle(const BookmarkNode
* node
) const {
149 DCHECK(IsPartnerBookmark(node
));
151 if (!g_disable_partner_bookmarks_editing
) {
152 const NodeRenamingMapKey
key(node
->url(), node
->GetTitle());
153 NodeRenamingMap::const_iterator i
= node_rename_remove_map_
.find(key
);
154 if (i
!= node_rename_remove_map_
.end())
158 return node
->GetTitle();
161 bool PartnerBookmarksShim::IsPartnerBookmark(const BookmarkNode
* node
) const {
163 if (!HasPartnerBookmarks())
165 const BookmarkNode
* parent
= node
;
167 if (parent
== GetPartnerBookmarksRoot())
169 parent
= parent
->parent();
174 const BookmarkNode
* PartnerBookmarksShim::GetPartnerBookmarksRoot() const {
175 return g_partner_model_keeper
.Get().partner_bookmarks_root
.get();
178 void PartnerBookmarksShim::SetPartnerBookmarksRoot(BookmarkNode
* root_node
) {
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
180 g_partner_model_keeper
.Get().partner_bookmarks_root
.reset(root_node
);
181 g_partner_model_keeper
.Get().loaded
= true;
182 FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer
, observers_
,
183 PartnerShimLoaded(this));
186 PartnerBookmarksShim::NodeRenamingMapKey::NodeRenamingMapKey(
187 const GURL
& url
, const base::string16
& provider_title
)
188 : url_(url
), provider_title_(provider_title
) {}
190 PartnerBookmarksShim::NodeRenamingMapKey::~NodeRenamingMapKey() {}
192 bool operator<(const PartnerBookmarksShim::NodeRenamingMapKey
& a
,
193 const PartnerBookmarksShim::NodeRenamingMapKey
& b
) {
194 return (a
.url_
< b
.url_
) ||
195 (a
.url_
== b
.url_
&& a
.provider_title_
< b
.provider_title_
);
199 void PartnerBookmarksShim::ClearInBrowserContextForTesting(
200 content::BrowserContext
* browser_context
) {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
202 browser_context
->SetUserData(kPartnerBookmarksShimUserDataKey
, 0);
206 void PartnerBookmarksShim::ClearPartnerModelForTesting() {
207 g_partner_model_keeper
.Get().loaded
= false;
208 g_partner_model_keeper
.Get().partner_bookmarks_root
.reset(0);
212 void PartnerBookmarksShim::EnablePartnerBookmarksEditing() {
213 g_disable_partner_bookmarks_editing
= false;
216 PartnerBookmarksShim::PartnerBookmarksShim(PrefService
* prefs
)
219 ObserverList
<PartnerBookmarksShim::Observer
>::NOTIFY_EXISTING_ONLY
) {
222 PartnerBookmarksShim::~PartnerBookmarksShim() {
223 FOR_EACH_OBSERVER(PartnerBookmarksShim::Observer
, observers_
,
224 ShimBeingDeleted(this));
227 const BookmarkNode
* PartnerBookmarksShim::GetNodeByID(
228 const BookmarkNode
* parent
, int64 id
) const {
229 if (parent
->id() == id
)
231 for (int i
= 0, child_count
= parent
->child_count(); i
< child_count
; ++i
) {
232 const BookmarkNode
* result
= GetNodeByID(parent
->GetChild(i
), id
);
239 void PartnerBookmarksShim::ReloadNodeMapping() {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
242 node_rename_remove_map_
.clear();
246 const base::ListValue
* list
=
247 prefs_
->GetList(prefs::kPartnerBookmarkMappings
);
251 for (base::ListValue::const_iterator it
= list
->begin();
252 it
!= list
->end(); ++it
) {
253 const base::DictionaryValue
* dict
= NULL
;
254 if (!*it
|| !(*it
)->GetAsDictionary(&dict
)) {
260 base::string16 provider_title
;
261 base::string16 mapped_title
;
262 if (!dict
->GetString(kMappingUrl
, &url
) ||
263 !dict
->GetString(kMappingProviderTitle
, &provider_title
) ||
264 !dict
->GetString(kMappingTitle
, &mapped_title
)) {
269 const NodeRenamingMapKey
key(GURL(url
), provider_title
);
270 node_rename_remove_map_
[key
] = mapped_title
;
274 void PartnerBookmarksShim::SaveNodeMapping() {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
279 base::ListValue list
;
280 for (NodeRenamingMap::const_iterator i
= node_rename_remove_map_
.begin();
281 i
!= node_rename_remove_map_
.end();
283 base::DictionaryValue
* dict
= new base::DictionaryValue();
284 dict
->SetString(kMappingUrl
, i
->first
.url().spec());
285 dict
->SetString(kMappingProviderTitle
, i
->first
.provider_title());
286 dict
->SetString(kMappingTitle
, i
->second
);
289 prefs_
->Set(prefs::kPartnerBookmarkMappings
, list
);