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