Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(521)

Side by Side Diff: chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc

Issue 10829326: Add the webui handler for Android's bookmark section on the NTP. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Resolve review comments Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/ui/webui/ntp/android/bookmarks_handler.h"
6
7 #include "base/logging.h"
8 #include "base/memory/ref_counted_memory.h"
9 #include "base/string_number_conversions.h"
10 #include "base/string_util.h"
11 #include "chrome/browser/android/tab_android.h"
12 #include "chrome/browser/bookmarks/bookmark_model.h"
13 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
17 #include "chrome/browser/ui/webui/favicon_source.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/web_contents.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/gfx/codec/png_codec.h"
22 #include "ui/gfx/color_analysis.h"
23
24 using base::Int64ToString;
25 using content::BrowserThread;
26
27 namespace {
28
29 static const char* kParentIdParam = "parent_id";
30 static const char* kNodeIdParam = "node_id";
31
32 // Parses a bookmark ID passed back from the NTP. The IDs differ from the
33 // normal int64 bookmark ID because we prepend a "p" if the ID represents a
34 // partner bookmark.
35 bool ParseNtpBookmarkId(const ListValue* args,
36 int64* out_id,
37 bool* out_is_partner) {
38 std::string string_id;
39 if (!args->GetString(0, &string_id))
40 return false;
41
42 if (string_id.empty())
43 return false;
44
45 if (StartsWithASCII(string_id, "p", true)) {
46 *out_is_partner = true;
47 return base::StringToInt64(string_id.substr(1), out_id);
48 } else {
49 *out_is_partner = false;
50 return base::StringToInt64(string_id, out_id);
51 }
52 }
53
54 std::string BookmarkTypeAsString(BookmarkNode::Type type) {
55 switch (type) {
56 case BookmarkNode::URL:
57 return "URL";
58 case BookmarkNode::FOLDER:
59 return "FOLDER";
60 case BookmarkNode::BOOKMARK_BAR:
61 return "BOOKMARK_BAR";
62 case BookmarkNode::OTHER_NODE:
63 return "OTHER_NODE";
64 case BookmarkNode::MOBILE:
65 return "MOBILE";
66 default:
67 return "UNKNOWN";
68 }
69 }
70
71 SkColor GetDominantColorForFavicon(scoped_refptr<base::RefCountedMemory> png) {
72 color_utils::GridSampler sampler;
73 // 100 here is the darkness_limit which represents the minimum sum of the RGB
74 // components that is acceptable as a color choice. This can be from 0 to 765.
75 // 665 here is the brightness_limit represents the maximum sum of the RGB
76 // components that is acceptable as a color choice. This can be from 0 to 765.
77 return color_utils::CalculateKMeanColorOfPNG(png, 100, 665, sampler);
78 }
79
80 } // namespace
81
82 BookmarksHandler::BookmarksHandler()
83 : bookmark_model_(NULL),
84 partner_bookmarks_shim_(NULL),
85 bookmark_data_requested_(false),
86 extensive_changes_(false),
87 creating_shortcut_(false) {
88 }
89
90 BookmarksHandler::~BookmarksHandler() {
91 if (bookmark_model_)
92 bookmark_model_->RemoveObserver(this);
93
94 if (partner_bookmarks_shim_)
95 partner_bookmarks_shim_->RemoveObserver(this);
96 }
97
98 void BookmarksHandler::RegisterMessages() {
99 // Listen for the bookmark change. We need the both bookmark and folder
100 // change, the NotificationService is not sufficient.
101 Profile* profile = Profile::FromBrowserContext(
102 web_ui()->GetWebContents()->GetBrowserContext());
103
104 ChromeURLDataManager::AddDataSource(profile,
105 new FaviconSource(profile, FaviconSource::ANY));
106
107 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
108 if (bookmark_model_) {
109 bookmark_model_->AddObserver(this);
110 // Since a sync or import could have started before this class is
111 // initialized, we need to make sure that our initial state is
112 // up to date.
113 extensive_changes_ = bookmark_model_->IsDoingExtensiveChanges();
114 }
115
116 // Create the partner Bookmarks shim as early as possible (but don't attach).
117 if (!partner_bookmarks_shim_) {
118 partner_bookmarks_shim_ = PartnerBookmarksShim::GetInstance();
119 partner_bookmarks_shim_->AddObserver(this);
120 }
121
122 // Register ourselves as the handler for the bookmark javascript callbacks.
123 web_ui()->RegisterMessageCallback("getBookmarks",
124 base::Bind(&BookmarksHandler::HandleGetBookmarks,
125 base::Unretained(this)));
126 web_ui()->RegisterMessageCallback("deleteBookmark",
127 base::Bind(&BookmarksHandler::HandleDeleteBookmark,
128 base::Unretained(this)));
129 web_ui()->RegisterMessageCallback("shortcutToBookmark",
130 base::Bind(&BookmarksHandler::HandleShortcutToBookmark,
131 base::Unretained(this)));
132 }
133
134 void BookmarksHandler::HandleGetBookmarks(const ListValue* args) {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
136
137 bookmark_data_requested_ = true;
138 Profile* profile = Profile::FromBrowserContext(
139 web_ui()->GetWebContents()->GetBrowserContext());
140 if (!BookmarkModelFactory::GetForProfile(profile)->IsLoaded())
141 return; // is handled in Loaded().
142
143 // Attach the Partner Bookmarks shim under the Mobile Bookmarks.
144 // Cannot do this earlier because mobile_node is not yet set.
145 DCHECK(partner_bookmarks_shim_ != NULL);
146 if (bookmark_model_)
147 partner_bookmarks_shim_->AttachTo(
148 bookmark_model_, bookmark_model_->mobile_node());
149 if (!partner_bookmarks_shim_->IsLoaded())
150 return; // is handled with a PartnerShimLoaded() callback
151
152 int64 id;
153 bool is_partner;
154 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner))
155 QueryBookmarkFolder(id, is_partner);
156 else
157 QueryInitialBookmarks();
158 }
159
160 void BookmarksHandler::HandleDeleteBookmark(const ListValue* args) {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
162 int64 id;
163 bool is_partner;
164 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) {
165 DCHECK(!is_partner);
166 const BookmarkNode* node = bookmark_model_->GetNodeByID(id);
167 if (node && node->parent()) {
168 const BookmarkNode* parent_node = node->parent();
169 bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
170 }
171 }
172 }
173
174 std::string BookmarksHandler::GetBookmarkIdForNtp(const BookmarkNode* node) {
175 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
176 return "p" + Int64ToString(node->id());
177 return Int64ToString(node->id());
178 }
179
180 void BookmarksHandler::SetParentInBookmarksResult(const BookmarkNode& parent,
181 DictionaryValue* result) {
182 result->SetString(kParentIdParam, GetBookmarkIdForNtp(&parent));
183 }
184
185 void BookmarksHandler::PopulateBookmark(const BookmarkNode* node,
186 ListValue* result) {
187 if (!result)
188 return;
189
190 DictionaryValue* filler_value = new DictionaryValue();
191 filler_value->SetString("title", node->GetTitle());
192 // Mark reserved system nodes and partner bookmarks as uneditable
193 // (i.e. the bookmark bar along with the "Other Bookmarks" folder).
194 filler_value->SetBoolean("editable",
195 partner_bookmarks_shim_->IsBookmarkEditable(node));
196 if (node->is_url()) {
197 filler_value->SetBoolean("folder", false);
198 filler_value->SetString("url", node->url().spec());
199 } else {
200 filler_value->SetBoolean("folder", true);
201 }
202 filler_value->SetString("id", GetBookmarkIdForNtp(node));
203 filler_value->SetString("type", BookmarkTypeAsString(node->type()));
204 result->Append(filler_value);
205 }
206
207 void BookmarksHandler::PopulateBookmarksInFolder(
208 const BookmarkNode* folder,
209 const scoped_ptr<DictionaryValue>& result) {
210 scoped_ptr<ListValue> bookmarks(new ListValue);
211
212 for (int i = 0; i < folder->child_count(); i++) {
213 const BookmarkNode* bookmark=
214 (const BookmarkNode*) folder->GetChild(i);
215 PopulateBookmark(bookmark, bookmarks.get());
216 }
217
218 // Make sure we iterate over the partner's attach point
219 DCHECK(partner_bookmarks_shim_ != NULL);
220 if (partner_bookmarks_shim_->HasPartnerBookmarks() &&
221 folder == partner_bookmarks_shim_->get_attach_point()) {
222 PopulateBookmark(
223 partner_bookmarks_shim_->GetPartnerBookmarksRoot(), bookmarks.get());
224 }
225
226 scoped_ptr<ListValue> folder_hierarchy(new ListValue);
227 const BookmarkNode* parent = partner_bookmarks_shim_->GetParentOf(folder);
228
229 while (parent != NULL) {
230 scoped_ptr<DictionaryValue> hierarchy_entry(new DictionaryValue);
231 if (partner_bookmarks_shim_->IsRootNode(parent))
232 hierarchy_entry->SetBoolean("root", true);
233
234 hierarchy_entry->SetString("title", parent->GetTitle());
235 hierarchy_entry->SetString("id", GetBookmarkIdForNtp(parent));
236 folder_hierarchy->Append(hierarchy_entry.release());
237 parent = partner_bookmarks_shim_->GetParentOf(parent);
238 }
239
240 result->SetString("title", folder->GetTitle());
241 result->SetString("id", GetBookmarkIdForNtp(folder));
242 result->SetBoolean("root", partner_bookmarks_shim_->IsRootNode(folder));
243 result->Set("bookmarks", bookmarks.release());
244 result->Set("hierarchy", folder_hierarchy.release());
245 }
246
247 void BookmarksHandler::QueryBookmarkFolder(const int64& folder_id,
248 bool is_partner_bookmark) {
249 DCHECK(partner_bookmarks_shim_ != NULL);
250 const BookmarkNode* bookmarks =
251 partner_bookmarks_shim_->GetNodeByID(folder_id, is_partner_bookmark);
252 if (bookmarks) {
253 scoped_ptr<DictionaryValue> result(new DictionaryValue);
254 PopulateBookmarksInFolder(bookmarks, result);
255 SendResult(result);
256 } else {
257 // If we receive an ID that no longer maps to a bookmark folder, just
258 // return the initial bookmark folder.
259 QueryInitialBookmarks();
260 }
261 }
262
263 void BookmarksHandler::QueryInitialBookmarks() {
264 scoped_ptr<DictionaryValue> result(new DictionaryValue);
265 DCHECK(partner_bookmarks_shim_ != NULL);
266 PopulateBookmarksInFolder(
267 // We have to go to the partner Root if it exists
268 partner_bookmarks_shim_->HasPartnerBookmarks() ?
269 partner_bookmarks_shim_->GetPartnerBookmarksRoot() :
270 bookmark_model_->mobile_node(),
271 result);
272 SendResult(result);
273 }
274
275 void BookmarksHandler::SendResult(const scoped_ptr<DictionaryValue>& result) {
276 if (result.get())
277 web_ui()->CallJavascriptFunction("ntp.bookmarks", *(result.get()));
278 }
279
280 void BookmarksHandler::Loaded(BookmarkModel* model, bool ids_reassigned) {
281 BookmarkModelChanged();
282 }
283
284 void BookmarksHandler::PartnerShimLoaded(PartnerBookmarksShim* shim) {
285 BookmarkModelChanged();
286 }
287
288 void BookmarksHandler::ShimBeingDeleted(PartnerBookmarksShim* shim) {
289 partner_bookmarks_shim_ = NULL;
290 }
291
292 void BookmarksHandler::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
293 extensive_changes_ = true;
294 }
295
296 void BookmarksHandler::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
297 extensive_changes_ = false;
298 BookmarkModelChanged();
299 }
300
301 void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model,
302 const BookmarkNode* parent,
303 int old_index,
304 const BookmarkNode* node) {
305 DictionaryValue result;
306 SetParentInBookmarksResult(*parent, &result);
307 result.SetString(kNodeIdParam, Int64ToString(node->id()));
308 NotifyModelChanged(result);
309 }
310
311 void BookmarksHandler::BookmarkNodeAdded(BookmarkModel* model,
312 const BookmarkNode* parent,
313 int index) {
314 DictionaryValue result;
315 SetParentInBookmarksResult(*parent, &result);
316 NotifyModelChanged(result);
317 }
318
319 void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model,
320 const BookmarkNode* node) {
321 DCHECK(partner_bookmarks_shim_);
322 DCHECK(!partner_bookmarks_shim_->IsPartnerBookmark(node));
323 DictionaryValue result;
324 SetParentInBookmarksResult(*(node->parent()), &result);
Evan Stade 2012/08/15 00:05:10 parens around node->parent() not necessary
Ted C 2012/08/15 21:21:00 Done.
325 result.SetString(kNodeIdParam, Int64ToString(node->id()));
326 NotifyModelChanged(result);
327 }
328
329 void BookmarksHandler::BookmarkModelChanged() {
330 if (bookmark_data_requested_ && !extensive_changes_)
331 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
332 }
333
334 void BookmarksHandler::NotifyModelChanged(const DictionaryValue& status) {
335 if (bookmark_data_requested_ && !extensive_changes_)
336 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged", status);
Evan Stade 2012/08/15 00:05:10 what is status used for? why is it needed here but
Ted C 2012/08/15 21:21:00 Status allows us to tell the NTP what exact node w
337 }
338
339 void BookmarksHandler::HandleShortcutToBookmark(const ListValue* args) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341 Profile* profile = Profile::FromBrowserContext(
342 web_ui()->GetWebContents()->GetBrowserContext());
343 if (!profile)
344 return;
345
346 int64 id;
347 bool is_partner;
348 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) {
349 DCHECK(partner_bookmarks_shim_ != NULL);
350 BookmarkNode* node = const_cast<BookmarkNode*>(
351 partner_bookmarks_shim_->GetNodeByID(id, is_partner));
352 if (!node)
353 return;
354
355 creating_shortcut_ = true;
356 FaviconService* favicon_service = profile->GetFaviconService(
357 Profile::EXPLICIT_ACCESS);
358 FaviconService::Handle handle = favicon_service->GetFaviconForURL(
359 node->url(),
360 history::FAVICON | history::TOUCH_ICON,
361 &cancelable_consumer_,
362 base::Bind(&BookmarksHandler::OnFaviconDataAvailable,
363 base::Unretained(this)));
364 cancelable_consumer_.SetClientData(favicon_service, handle, node);
365 }
366 }
367
368 void BookmarksHandler::OnFaviconDataAvailable(
369 FaviconService::Handle handle,
370 history::FaviconData favicon) {
371 if (!creating_shortcut_)
Evan Stade 2012/08/15 00:05:10 why is this necessary?
Ted C 2012/08/15 21:21:00 I think it was here for legacy reasons. I removed
372 return;
373
374 SkColor color = SK_ColorWHITE;
375 SkBitmap favicon_bitmap;
376 if (favicon.is_valid()) {
377 color = GetDominantColorForFavicon(favicon.image_data);
378 gfx::PNGCodec::Decode(favicon.image_data->front(),
379 favicon.image_data->size(),
380 &favicon_bitmap);
381 }
382
383 Profile* profile = Profile::FromBrowserContext(
384 web_ui()->GetWebContents()->GetBrowserContext());
385 BookmarkNode* node = cancelable_consumer_.GetClientData(
386 profile->GetFaviconService(Profile::EXPLICIT_ACCESS), handle);
387
388 TabAndroid* tab = TabAndroid::FromWebContents(
389 web_ui()->GetWebContents());
390 if (tab) {
391 tab->AddShortcutToBookmark(node->url(), node->GetTitle(),
392 favicon_bitmap, SkColorGetR(color),
393 SkColorGetG(color), SkColorGetB(color));
394 }
395 creating_shortcut_ = false;
396 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698