1 // Copyright 2015 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/supervised_user/supervised_user_bookmarks_handler.h"
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/values.h"
10 #include "components/url_formatter/url_fixer.h"
14 // Keys of relevant managed user settings.
15 const char kKeyLink
[] = "SupervisedBookmarkLink";
16 const char kKeyFolder
[] = "SupervisedBookmarkFolder";
18 // Keys for elements of the bookmarks json tree.
19 const char kId
[] = "id";
20 const char kName
[] = "name";
21 const char kUrl
[] = "url";
22 const char kChildren
[] = "children";
24 bool ExtractId(const std::string
& key
, int* id
) {
25 // |key| can be either "<ID>:<Value>" (for links and for "parentID:name"
26 // pairs) or just "<ID>" (for folders).
27 std::string id_str
= key
.substr(0, key
.find_first_of(':'));
28 if (!base::StringToInt(id_str
, id
)) {
29 LOG(WARNING
) << "Failed to parse id from " << key
;
32 LOG_IF(WARNING
, *id
< 0) << "IDs should be >= 0, but got "
33 << *id
<< " from " << key
;
37 bool ExtractValue(const std::string
& key
, std::string
* value
) {
38 // |key| must be "<ID>:<Value>".
39 size_t pos
= key
.find_first_of(':');
40 if (pos
== std::string::npos
) {
41 LOG(WARNING
) << "Failed to parse value from " << key
;
44 *value
= key
.substr(pos
+ 1);
48 bool ExtractIdAndValue(const std::string
& key
, int* id
, std::string
* value
) {
49 return ExtractId(key
, id
) && ExtractValue(key
, value
);
52 // Recursively searches the tree from |root| for a folder with the given |id|.
53 // Returns the list of children of that folder if found, otherwise returns null.
54 base::ListValue
* FindFolder(base::ListValue
* root
, int id
) {
55 if (id
== 0) // We're looking for the root folder. Assume this is it.
58 for (size_t i
= 0; i
< root
->GetSize(); ++i
) {
59 base::DictionaryValue
* item
= nullptr;
60 root
->GetDictionary(i
, &item
);
61 DCHECK_NE(item
, (base::DictionaryValue
*)nullptr);
63 base::ListValue
* children
;
64 if (!item
->GetList(kChildren
, &children
))
65 continue; // Skip bookmarks. Only interested in folders.
69 if (item
->GetInteger(kId
, &node_id
) && node_id
== id
)
73 base::ListValue
* result
= FindFolder(children
, id
);
82 SupervisedUserBookmarksHandler::Folder::Folder(
83 int id
, const std::string
& name
, int parent_id
)
84 : id(id
), name(name
), parent_id(parent_id
) {
87 SupervisedUserBookmarksHandler::Link::Link(
88 const std::string
& url
, const std::string
& name
, int parent_id
)
89 : url(url
), name(name
), parent_id(parent_id
) {
92 SupervisedUserBookmarksHandler::SupervisedUserBookmarksHandler() {
95 SupervisedUserBookmarksHandler::~SupervisedUserBookmarksHandler() {
98 scoped_ptr
<base::ListValue
> SupervisedUserBookmarksHandler::BuildBookmarksTree(
99 const base::DictionaryValue
& settings
) {
100 SupervisedUserBookmarksHandler handler
;
101 handler
.ParseSettings(settings
);
102 return handler
.BuildTree();
105 void SupervisedUserBookmarksHandler::ParseSettings(
106 const base::DictionaryValue
& settings
) {
107 const base::DictionaryValue
* folders
;
108 if (settings
.GetDictionary(kKeyFolder
, &folders
))
109 ParseFolders(*folders
);
111 const base::DictionaryValue
* links
;
112 if (settings
.GetDictionary(kKeyLink
, &links
))
116 void SupervisedUserBookmarksHandler::ParseFolders(
117 const base::DictionaryValue
& folders
) {
118 for (base::DictionaryValue::Iterator
it(folders
); !it
.IsAtEnd();
121 if (!ExtractId(it
.key(), &id
))
124 it
.value().GetAsString(&value
);
127 if (!ExtractIdAndValue(value
, &parent_id
, &name
))
129 folders_
.push_back(Folder(id
, name
, parent_id
));
133 void SupervisedUserBookmarksHandler::ParseLinks(
134 const base::DictionaryValue
& links
) {
135 for (base::DictionaryValue::Iterator
it(links
); !it
.IsAtEnd(); it
.Advance()) {
137 if (!ExtractValue(it
.key(), &url
))
140 it
.value().GetAsString(&value
);
143 if (!ExtractIdAndValue(value
, &parent_id
, &name
))
145 links_
.push_back(Link(url
, name
, parent_id
));
149 scoped_ptr
<base::ListValue
> SupervisedUserBookmarksHandler::BuildTree() {
150 root_
.reset(new base::ListValue
);
156 void SupervisedUserBookmarksHandler::AddFoldersToTree() {
157 // Go over all folders and try inserting them into the correct position in the
158 // tree. This requires the respective parent folder to be there already. Since
159 // the parent might appear later in |folders_|, we might need multiple rounds
160 // until all folders can be added successfully.
161 // To avoid an infinite loop in the case of a non-existing parent, we take
162 // care to stop when no folders could be added in a round.
163 std::vector
<Folder
> folders
= folders_
;
164 std::vector
<Folder
> folders_failed
;
165 while (!folders
.empty() && folders
.size() != folders_failed
.size()) {
166 folders_failed
.clear();
167 for (const auto& folder
: folders
) {
168 scoped_ptr
<base::DictionaryValue
> node(new base::DictionaryValue
);
169 node
->SetIntegerWithoutPathExpansion(kId
, folder
.id
);
170 node
->SetStringWithoutPathExpansion(kName
, folder
.name
);
171 node
->SetWithoutPathExpansion(kChildren
, new base::ListValue
);
172 if (!AddNodeToTree(folder
.parent_id
, node
.Pass()))
173 folders_failed
.push_back(folder
);
175 folders
.swap(folders_failed
);
177 if (!folders_failed
.empty()) {
178 LOG(WARNING
) << "SupervisedUserBookmarksHandler::AddFoldersToTree"
179 << " failed adding the following folders (id,name,parent):";
180 for (const Folder
& folder
: folders_failed
) {
181 LOG(WARNING
) << folder
.id
<< ", " << folder
.name
<< ", "
187 void SupervisedUserBookmarksHandler::AddLinksToTree() {
188 for (const auto& link
: links_
) {
189 scoped_ptr
<base::DictionaryValue
> node(new base::DictionaryValue
);
190 GURL url
= url_formatter::FixupURL(link
.url
, std::string());
191 if (!url
.is_valid()) {
192 LOG(WARNING
) << "Got invalid URL: " << link
.url
;
195 node
->SetStringWithoutPathExpansion(kUrl
, url
.spec());
196 node
->SetStringWithoutPathExpansion(kName
, link
.name
);
197 if (!AddNodeToTree(link
.parent_id
, node
.Pass())) {
198 LOG(WARNING
) << "SupervisedUserBookmarksHandler::AddLinksToTree"
199 << " failed to add link (url,name,parent): "
200 << link
.url
<< ", " << link
.name
<< ", " << link
.parent_id
;
205 bool SupervisedUserBookmarksHandler::AddNodeToTree(
207 scoped_ptr
<base::DictionaryValue
> node
) {
208 base::ListValue
* parent
= FindFolder(root_
.get(), parent_id
);
211 parent
->Append(node
.release());