1 // Copyright 2014 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 "components/policy/core/browser/managed_bookmarks_tracker.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "components/bookmarks/browser/bookmark_model.h"
16 #include "components/bookmarks/browser/bookmark_node.h"
17 #include "components/bookmarks/common/bookmark_pref_names.h"
18 #include "grit/components_strings.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "url/gurl.h"
21
22 namespace policy {
23
24 const char ManagedBookmarksTracker::kName[] = "name";
25 const char ManagedBookmarksTracker::kUrl[] = "url";
26 const char ManagedBookmarksTracker::kChildren[] = "children";
27
ManagedBookmarksTracker(BookmarkModel * model,PrefService * prefs,const GetManagementDomainCallback & callback)28 ManagedBookmarksTracker::ManagedBookmarksTracker(
29 BookmarkModel* model,
30 PrefService* prefs,
31 const GetManagementDomainCallback& callback)
32 : model_(model),
33 managed_node_(NULL),
34 prefs_(prefs),
35 get_management_domain_callback_(callback) {
36 }
37
~ManagedBookmarksTracker()38 ManagedBookmarksTracker::~ManagedBookmarksTracker() {}
39
40 scoped_ptr<base::ListValue>
GetInitialManagedBookmarks()41 ManagedBookmarksTracker::GetInitialManagedBookmarks() {
42 const base::ListValue* list = prefs_->GetList(prefs::kManagedBookmarks);
43 return make_scoped_ptr(list->DeepCopy());
44 }
45
46 // static
LoadInitial(BookmarkNode * folder,const base::ListValue * list,int64 next_node_id)47 int64 ManagedBookmarksTracker::LoadInitial(BookmarkNode* folder,
48 const base::ListValue* list,
49 int64 next_node_id) {
50 for (size_t i = 0; i < list->GetSize(); ++i) {
51 // Extract the data for the next bookmark from the |list|.
52 base::string16 title;
53 GURL url;
54 const base::ListValue* children = NULL;
55 if (!LoadBookmark(list, i, &title, &url, &children))
56 continue;
57
58 BookmarkNode* child = new BookmarkNode(next_node_id++, url);
59 child->SetTitle(title);
60 folder->Add(child, folder->child_count());
61 if (children) {
62 child->set_type(BookmarkNode::FOLDER);
63 child->set_date_folder_modified(base::Time::Now());
64 next_node_id = LoadInitial(child, children, next_node_id);
65 } else {
66 child->set_type(BookmarkNode::URL);
67 child->set_date_added(base::Time::Now());
68 }
69 }
70
71 return next_node_id;
72 }
73
Init(BookmarkPermanentNode * managed_node)74 void ManagedBookmarksTracker::Init(BookmarkPermanentNode* managed_node) {
75 managed_node_ = managed_node;
76 registrar_.Init(prefs_);
77 registrar_.Add(prefs::kManagedBookmarks,
78 base::Bind(&ManagedBookmarksTracker::ReloadManagedBookmarks,
79 base::Unretained(this)));
80 // Reload now just in case something changed since the initial load started.
81 ReloadManagedBookmarks();
82 }
83
ReloadManagedBookmarks()84 void ManagedBookmarksTracker::ReloadManagedBookmarks() {
85 // Update the managed bookmarks folder title, in case the user just signed
86 // into or out of a managed account.
87 base::string16 title;
88 std::string domain = get_management_domain_callback_.Run();
89 if (domain.empty()) {
90 title =
91 l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_MANAGED_FOLDER_DEFAULT_NAME);
92 } else {
93 title = l10n_util::GetStringFUTF16(
94 IDS_BOOKMARK_BAR_MANAGED_FOLDER_DOMAIN_NAME, base::UTF8ToUTF16(domain));
95 }
96 model_->SetTitle(managed_node_, title);
97
98 // Recursively update all the managed bookmarks and folders.
99 const base::ListValue* list = prefs_->GetList(prefs::kManagedBookmarks);
100 UpdateBookmarks(managed_node_, list);
101
102 // The managed bookmarks folder isn't visible when that policy isn't present.
103 managed_node_->set_visible(!managed_node_->empty());
104 }
105
UpdateBookmarks(const BookmarkNode * folder,const base::ListValue * list)106 void ManagedBookmarksTracker::UpdateBookmarks(const BookmarkNode* folder,
107 const base::ListValue* list) {
108 int folder_index = 0;
109 for (size_t i = 0; i < list->GetSize(); ++i) {
110 // Extract the data for the next bookmark from the |list|.
111 base::string16 title;
112 GURL url;
113 const base::ListValue* children = NULL;
114 if (!LoadBookmark(list, i, &title, &url, &children)) {
115 // Skip this bookmark from |list| but don't advance |folder_index|.
116 continue;
117 }
118
119 // Look for a bookmark at |folder_index| or ahead that matches the current
120 // bookmark from the pref.
121 const BookmarkNode* existing = NULL;
122 for (int k = folder_index; k < folder->child_count(); ++k) {
123 const BookmarkNode* node = folder->GetChild(k);
124 if (node->GetTitle() == title &&
125 ((children && node->is_folder()) ||
126 (!children && node->url() == url))) {
127 existing = node;
128 break;
129 }
130 }
131
132 if (existing) {
133 // Reuse the existing node. The Move() is a nop if |existing| is already
134 // at |folder_index|.
135 model_->Move(existing, folder, folder_index);
136 if (children)
137 UpdateBookmarks(existing, children);
138 } else {
139 // Create a new node for this bookmark now.
140 if (children) {
141 const BookmarkNode* sub =
142 model_->AddFolder(folder, folder_index, title);
143 UpdateBookmarks(sub, children);
144 } else {
145 model_->AddURL(folder, folder_index, title, url);
146 }
147 }
148
149 // The |folder_index| index of |folder| has been updated, so advance it.
150 ++folder_index;
151 }
152
153 // Remove any extra children of |folder| that haven't been reused.
154 while (folder->child_count() != folder_index)
155 model_->Remove(folder, folder_index);
156 }
157
158 // static
LoadBookmark(const base::ListValue * list,size_t index,base::string16 * title,GURL * url,const base::ListValue ** children)159 bool ManagedBookmarksTracker::LoadBookmark(const base::ListValue* list,
160 size_t index,
161 base::string16* title,
162 GURL* url,
163 const base::ListValue** children) {
164 std::string spec;
165 *url = GURL();
166 *children = NULL;
167 const base::DictionaryValue* dict = NULL;
168 if (!list->GetDictionary(index, &dict) ||
169 !dict->GetString(kName, title) ||
170 (!dict->GetString(kUrl, &spec) &&
171 !dict->GetList(kChildren, children))) {
172 // Should never happen after policy validation.
173 NOTREACHED();
174 return false;
175 }
176 if (!*children)
177 *url = GURL(spec);
178 return true;
179 }
180
181 } // namespace policy
182