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 "chrome/browser/bookmarks/chrome_bookmark_client.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/values.h"
11 #include "chrome/browser/favicon/favicon_service.h"
12 #include "chrome/browser/favicon/favicon_service_factory.h"
13 #include "chrome/browser/history/history_service.h"
14 #include "chrome/browser/policy/profile_policy_connector.h"
15 #include "chrome/browser/policy/profile_policy_connector_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "components/bookmarks/browser/bookmark_node.h"
19 #include "components/history/core/browser/url_database.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/user_metrics.h"
24 #include "grit/components_strings.h"
25 #include "policy/policy_constants.h"
26 #include "ui/base/l10n/l10n_util.h"
27
28 namespace {
29
RunCallbackWithImage(const favicon_base::FaviconImageCallback & callback,const favicon_base::FaviconRawBitmapResult & bitmap_result)30 void RunCallbackWithImage(
31 const favicon_base::FaviconImageCallback& callback,
32 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
33 favicon_base::FaviconImageResult result;
34 if (bitmap_result.is_valid()) {
35 result.image = gfx::Image::CreateFrom1xPNGBytes(
36 bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size());
37 result.icon_url = bitmap_result.icon_url;
38 callback.Run(result);
39 return;
40 }
41 callback.Run(result);
42 }
43
44 } // namespace
45
ChromeBookmarkClient(Profile * profile)46 ChromeBookmarkClient::ChromeBookmarkClient(Profile* profile)
47 : profile_(profile),
48 history_service_(NULL),
49 model_(NULL),
50 managed_node_(NULL) {
51 }
52
~ChromeBookmarkClient()53 ChromeBookmarkClient::~ChromeBookmarkClient() {
54 }
55
Init(BookmarkModel * model)56 void ChromeBookmarkClient::Init(BookmarkModel* model) {
57 DCHECK(model);
58 DCHECK(!model_);
59 model_ = model;
60 model_->AddObserver(this);
61
62 managed_bookmarks_tracker_.reset(new policy::ManagedBookmarksTracker(
63 model_,
64 profile_->GetPrefs(),
65 base::Bind(&ChromeBookmarkClient::GetManagedBookmarksDomain,
66 base::Unretained(this))));
67 }
68
Shutdown()69 void ChromeBookmarkClient::Shutdown() {
70 favicon_changed_subscription_.reset();
71 if (model_) {
72 model_->RemoveObserver(this);
73 model_ = NULL;
74 }
75 BookmarkClient::Shutdown();
76 }
77
IsDescendantOfManagedNode(const BookmarkNode * node)78 bool ChromeBookmarkClient::IsDescendantOfManagedNode(const BookmarkNode* node) {
79 return node && node->HasAncestor(managed_node_);
80 }
81
HasDescendantsOfManagedNode(const std::vector<const BookmarkNode * > & list)82 bool ChromeBookmarkClient::HasDescendantsOfManagedNode(
83 const std::vector<const BookmarkNode*>& list) {
84 for (size_t i = 0; i < list.size(); ++i) {
85 if (IsDescendantOfManagedNode(list[i]))
86 return true;
87 }
88 return false;
89 }
90
PreferTouchIcon()91 bool ChromeBookmarkClient::PreferTouchIcon() {
92 #if !defined(OS_IOS)
93 return false;
94 #else
95 return true;
96 #endif
97 }
98
99 base::CancelableTaskTracker::TaskId
GetFaviconImageForPageURL(const GURL & page_url,favicon_base::IconType type,const favicon_base::FaviconImageCallback & callback,base::CancelableTaskTracker * tracker)100 ChromeBookmarkClient::GetFaviconImageForPageURL(
101 const GURL& page_url,
102 favicon_base::IconType type,
103 const favicon_base::FaviconImageCallback& callback,
104 base::CancelableTaskTracker* tracker) {
105 FaviconService* favicon_service =
106 FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
107 if (!favicon_service)
108 return base::CancelableTaskTracker::kBadTaskId;
109 if (type == favicon_base::FAVICON) {
110 return favicon_service->GetFaviconImageForPageURL(
111 page_url, callback, tracker);
112 } else {
113 return favicon_service->GetRawFaviconForPageURL(
114 page_url,
115 type,
116 0,
117 base::Bind(&RunCallbackWithImage, callback),
118 tracker);
119 }
120 }
121
SupportsTypedCountForNodes()122 bool ChromeBookmarkClient::SupportsTypedCountForNodes() {
123 return true;
124 }
125
GetTypedCountForNodes(const NodeSet & nodes,NodeTypedCountPairs * node_typed_count_pairs)126 void ChromeBookmarkClient::GetTypedCountForNodes(
127 const NodeSet& nodes,
128 NodeTypedCountPairs* node_typed_count_pairs) {
129 history::URLDatabase* url_db =
130 history_service_ ? history_service_->InMemoryDatabase() : NULL;
131 for (NodeSet::const_iterator i = nodes.begin(); i != nodes.end(); ++i) {
132 int typed_count = 0;
133
134 // If |url_db| is the InMemoryDatabase, it might not cache all URLRows, but
135 // it guarantees to contain those with |typed_count| > 0. Thus, if we cannot
136 // fetch the URLRow, it is safe to assume that its |typed_count| is 0.
137 history::URLRow url;
138 if (url_db && url_db->GetRowForURL((*i)->url(), &url))
139 typed_count = url.typed_count();
140
141 NodeTypedCountPair pair(*i, typed_count);
142 node_typed_count_pairs->push_back(pair);
143 }
144 }
145
IsPermanentNodeVisible(const BookmarkPermanentNode * node)146 bool ChromeBookmarkClient::IsPermanentNodeVisible(
147 const BookmarkPermanentNode* node) {
148 DCHECK(node->type() == BookmarkNode::BOOKMARK_BAR ||
149 node->type() == BookmarkNode::OTHER_NODE ||
150 node->type() == BookmarkNode::MOBILE ||
151 node == managed_node_);
152 if (node == managed_node_)
153 return false;
154 #if !defined(OS_IOS)
155 return node->type() != BookmarkNode::MOBILE;
156 #else
157 return node->type() == BookmarkNode::MOBILE;
158 #endif
159 }
160
RecordAction(const base::UserMetricsAction & action)161 void ChromeBookmarkClient::RecordAction(const base::UserMetricsAction& action) {
162 content::RecordAction(action);
163 }
164
GetLoadExtraNodesCallback()165 bookmarks::LoadExtraCallback ChromeBookmarkClient::GetLoadExtraNodesCallback() {
166 // Create the managed_node now; it will be populated in the LoadExtraNodes
167 // callback.
168 // The ownership of managed_node_ is in limbo until LoadExtraNodes runs,
169 // so we leave it in the care of the closure meanwhile.
170 scoped_ptr<BookmarkPermanentNode> managed(new BookmarkPermanentNode(0));
171 managed_node_ = managed.get();
172
173 return base::Bind(
174 &ChromeBookmarkClient::LoadExtraNodes,
175 base::Passed(&managed),
176 base::Passed(managed_bookmarks_tracker_->GetInitialManagedBookmarks()));
177 }
178
CanSetPermanentNodeTitle(const BookmarkNode * permanent_node)179 bool ChromeBookmarkClient::CanSetPermanentNodeTitle(
180 const BookmarkNode* permanent_node) {
181 // The |managed_node_| can have its title updated if the user signs in or
182 // out.
183 return !IsDescendantOfManagedNode(permanent_node) ||
184 permanent_node == managed_node_;
185 }
186
CanSyncNode(const BookmarkNode * node)187 bool ChromeBookmarkClient::CanSyncNode(const BookmarkNode* node) {
188 return !IsDescendantOfManagedNode(node);
189 }
190
CanBeEditedByUser(const BookmarkNode * node)191 bool ChromeBookmarkClient::CanBeEditedByUser(const BookmarkNode* node) {
192 return !IsDescendantOfManagedNode(node);
193 }
194
SetHistoryService(HistoryService * history_service)195 void ChromeBookmarkClient::SetHistoryService(HistoryService* history_service) {
196 DCHECK(history_service);
197 history_service_ = history_service;
198 favicon_changed_subscription_ = history_service_->AddFaviconChangedCallback(
199 base::Bind(&BookmarkModel::OnFaviconChanged, base::Unretained(model_)));
200 }
201
BookmarkModelChanged()202 void ChromeBookmarkClient::BookmarkModelChanged() {
203 }
204
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int old_index,const BookmarkNode * node,const std::set<GURL> & removed_urls)205 void ChromeBookmarkClient::BookmarkNodeRemoved(
206 BookmarkModel* model,
207 const BookmarkNode* parent,
208 int old_index,
209 const BookmarkNode* node,
210 const std::set<GURL>& removed_urls) {
211 if (history_service_)
212 history_service_->URLsNoLongerBookmarked(removed_urls);
213 }
214
BookmarkAllUserNodesRemoved(BookmarkModel * model,const std::set<GURL> & removed_urls)215 void ChromeBookmarkClient::BookmarkAllUserNodesRemoved(
216 BookmarkModel* model,
217 const std::set<GURL>& removed_urls) {
218 if (history_service_)
219 history_service_->URLsNoLongerBookmarked(removed_urls);
220 }
221
BookmarkModelLoaded(BookmarkModel * model,bool ids_reassigned)222 void ChromeBookmarkClient::BookmarkModelLoaded(BookmarkModel* model,
223 bool ids_reassigned) {
224 // Start tracking the managed bookmarks. This will detect any changes that
225 // may have occurred while the initial managed bookmarks were being loaded
226 // on the background.
227 managed_bookmarks_tracker_->Init(managed_node_);
228 }
229
230 // static
LoadExtraNodes(scoped_ptr<BookmarkPermanentNode> managed_node,scoped_ptr<base::ListValue> initial_managed_bookmarks,int64 * next_node_id)231 bookmarks::BookmarkPermanentNodeList ChromeBookmarkClient::LoadExtraNodes(
232 scoped_ptr<BookmarkPermanentNode> managed_node,
233 scoped_ptr<base::ListValue> initial_managed_bookmarks,
234 int64* next_node_id) {
235 // Load the initial contents of the |managed_node| now, and assign it an
236 // unused ID.
237 int64 managed_id = *next_node_id;
238 managed_node->set_id(managed_id);
239 *next_node_id = policy::ManagedBookmarksTracker::LoadInitial(
240 managed_node.get(), initial_managed_bookmarks.get(), managed_id + 1);
241 managed_node->set_visible(!managed_node->empty());
242 managed_node->SetTitle(
243 l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_MANAGED_FOLDER_DEFAULT_NAME));
244
245 bookmarks::BookmarkPermanentNodeList extra_nodes;
246 // Ownership of the managed node passed to the caller.
247 extra_nodes.push_back(managed_node.release());
248
249 return extra_nodes.Pass();
250 }
251
GetManagedBookmarksDomain()252 std::string ChromeBookmarkClient::GetManagedBookmarksDomain() {
253 policy::ProfilePolicyConnector* connector =
254 policy::ProfilePolicyConnectorFactory::GetForProfile(profile_);
255 if (connector->IsPolicyFromCloudPolicy(policy::key::kManagedBookmarks))
256 return connector->GetManagementDomain();
257 return std::string();
258 }
259