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

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: Fix review comments I missed. 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 }
49
50 *out_is_partner = false;
51 return base::StringToInt64(string_id, out_id);
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 }
88
89 BookmarksHandler::~BookmarksHandler() {
90 if (bookmark_model_)
91 bookmark_model_->RemoveObserver(this);
92
93 if (partner_bookmarks_shim_)
94 partner_bookmarks_shim_->RemoveObserver(this);
95 }
96
97 void BookmarksHandler::RegisterMessages() {
98 // Listen for the bookmark change. We need the both bookmark and folder
99 // change, the NotificationService is not sufficient.
100 Profile* profile = Profile::FromBrowserContext(
101 web_ui()->GetWebContents()->GetBrowserContext());
102
103 ChromeURLDataManager::AddDataSource(profile,
104 new FaviconSource(profile, FaviconSource::ANY));
105
106 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
107 if (bookmark_model_) {
108 bookmark_model_->AddObserver(this);
109 // Since a sync or import could have started before this class is
110 // initialized, we need to make sure that our initial state is
111 // up to date.
112 extensive_changes_ = bookmark_model_->IsDoingExtensiveChanges();
113 }
114
115 // Create the partner Bookmarks shim as early as possible (but don't attach).
116 if (!partner_bookmarks_shim_) {
117 partner_bookmarks_shim_ = PartnerBookmarksShim::GetInstance();
118 partner_bookmarks_shim_->AddObserver(this);
119 }
120
121 // Register ourselves as the handler for the bookmark javascript callbacks.
122 web_ui()->RegisterMessageCallback("getBookmarks",
123 base::Bind(&BookmarksHandler::HandleGetBookmarks,
124 base::Unretained(this)));
125 web_ui()->RegisterMessageCallback("deleteBookmark",
126 base::Bind(&BookmarksHandler::HandleDeleteBookmark,
127 base::Unretained(this)));
128 // TODO(tedchoc): Change shortcutToBookmark to
129 // createHomeScreenBookmarkShortcut once the ntp js code lands.
130 web_ui()->RegisterMessageCallback("shortcutToBookmark",
Evan Stade 2012/08/15 22:24:00 update this string to match
Ted C 2012/08/15 22:50:48 I'm waiting on this: http://codereview.chromium.or
Evan Stade 2012/08/15 22:57:27 ah i see. Didn't notice the TODO.
131 base::Bind(&BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut,
132 base::Unretained(this)));
133 }
134
135 void BookmarksHandler::HandleGetBookmarks(const ListValue* args) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137
138 bookmark_data_requested_ = true;
139 Profile* profile = Profile::FromBrowserContext(
140 web_ui()->GetWebContents()->GetBrowserContext());
141 if (!BookmarkModelFactory::GetForProfile(profile)->IsLoaded())
142 return; // is handled in Loaded().
143
144 // Attach the Partner Bookmarks shim under the Mobile Bookmarks.
145 // Cannot do this earlier because mobile_node is not yet set.
146 DCHECK(partner_bookmarks_shim_ != NULL);
147 if (bookmark_model_) {
148 partner_bookmarks_shim_->AttachTo(
149 bookmark_model_, bookmark_model_->mobile_node());
150 }
151 if (!partner_bookmarks_shim_->IsLoaded())
152 return; // is handled with a PartnerShimLoaded() callback
153
154 int64 id;
155 bool is_partner;
156 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner))
157 QueryBookmarkFolder(id, is_partner);
158 else
159 QueryInitialBookmarks();
160 }
161
162 void BookmarksHandler::HandleDeleteBookmark(const ListValue* args) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164 int64 id;
165 bool is_partner;
166 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) {
167 DCHECK(!is_partner);
168 const BookmarkNode* node = bookmark_model_->GetNodeByID(id);
169 if (node && node->parent()) {
170 const BookmarkNode* parent_node = node->parent();
171 bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
172 }
173 }
174 }
175
176 std::string BookmarksHandler::GetBookmarkIdForNtp(const BookmarkNode* node) {
177 return (partner_bookmarks_shim_->IsPartnerBookmark(node) ? "p" : "")
178 + Int64ToString(node->id());
Evan Stade 2012/08/15 22:24:00 operators go at end of (previous) line
Ted C 2012/08/15 22:50:48 Done.
179 }
180
181 void BookmarksHandler::SetParentInBookmarksResult(const BookmarkNode& parent,
182 DictionaryValue* result) {
183 result->SetString(kParentIdParam, GetBookmarkIdForNtp(&parent));
184 }
185
186 void BookmarksHandler::PopulateBookmark(const BookmarkNode* node,
187 ListValue* result) {
188 if (!result)
189 return;
190
191 DictionaryValue* filler_value = new DictionaryValue();
192 filler_value->SetString("title", node->GetTitle());
193 // Mark reserved system nodes and partner bookmarks as uneditable
194 // (i.e. the bookmark bar along with the "Other Bookmarks" folder).
195 filler_value->SetBoolean("editable",
196 partner_bookmarks_shim_->IsBookmarkEditable(node));
197 if (node->is_url()) {
198 filler_value->SetBoolean("folder", false);
199 filler_value->SetString("url", node->url().spec());
200 } else {
201 filler_value->SetBoolean("folder", true);
202 }
203 filler_value->SetString("id", GetBookmarkIdForNtp(node));
204 filler_value->SetString("type", BookmarkTypeAsString(node->type()));
205 result->Append(filler_value);
206 }
207
208 void BookmarksHandler::PopulateBookmarksInFolder(
209 const BookmarkNode* folder,
210 const scoped_ptr<DictionaryValue>& result) {
211 scoped_ptr<ListValue> bookmarks(new ListValue);
212
213 for (int i = 0; i < folder->child_count(); i++) {
214 const BookmarkNode* bookmark=
215 (const BookmarkNode*) folder->GetChild(i);
Evan Stade 2012/08/15 22:24:00 this cast should not be necessary. Also you are no
Ted C 2012/08/15 22:50:48 Done.
216 PopulateBookmark(bookmark, bookmarks.get());
217 }
218
219 // Make sure we iterate over the partner's attach point
220 DCHECK(partner_bookmarks_shim_ != NULL);
221 if (partner_bookmarks_shim_->HasPartnerBookmarks() &&
222 folder == partner_bookmarks_shim_->get_attach_point()) {
223 PopulateBookmark(
224 partner_bookmarks_shim_->GetPartnerBookmarksRoot(), bookmarks.get());
225 }
226
227 scoped_ptr<ListValue> folder_hierarchy(new ListValue);
228 const BookmarkNode* parent = partner_bookmarks_shim_->GetParentOf(folder);
229
230 while (parent != NULL) {
231 scoped_ptr<DictionaryValue> hierarchy_entry(new DictionaryValue);
Evan Stade 2012/08/15 22:24:00 bare pointer.
Ted C 2012/08/15 22:50:48 Done.
232 if (partner_bookmarks_shim_->IsRootNode(parent))
233 hierarchy_entry->SetBoolean("root", true);
234
235 hierarchy_entry->SetString("title", parent->GetTitle());
236 hierarchy_entry->SetString("id", GetBookmarkIdForNtp(parent));
237 folder_hierarchy->Append(hierarchy_entry.release());
238 parent = partner_bookmarks_shim_->GetParentOf(parent);
239 }
240
241 result->SetString("title", folder->GetTitle());
242 result->SetString("id", GetBookmarkIdForNtp(folder));
243 result->SetBoolean("root", partner_bookmarks_shim_->IsRootNode(folder));
244 result->Set("bookmarks", bookmarks.release());
245 result->Set("hierarchy", folder_hierarchy.release());
246 }
247
248 void BookmarksHandler::QueryBookmarkFolder(const int64& folder_id,
249 bool is_partner_bookmark) {
250 DCHECK(partner_bookmarks_shim_ != NULL);
251 const BookmarkNode* bookmarks =
252 partner_bookmarks_shim_->GetNodeByID(folder_id, is_partner_bookmark);
253 if (bookmarks) {
254 scoped_ptr<DictionaryValue> result(new DictionaryValue);
255 PopulateBookmarksInFolder(bookmarks, result);
256 SendResult(result);
257 } else {
258 // If we receive an ID that no longer maps to a bookmark folder, just
259 // return the initial bookmark folder.
260 QueryInitialBookmarks();
261 }
262 }
263
264 void BookmarksHandler::QueryInitialBookmarks() {
265 scoped_ptr<DictionaryValue> result(new DictionaryValue);
266 DCHECK(partner_bookmarks_shim_ != NULL);
267 PopulateBookmarksInFolder(
268 // We have to go to the partner Root if it exists
269 partner_bookmarks_shim_->HasPartnerBookmarks() ?
270 partner_bookmarks_shim_->GetPartnerBookmarksRoot() :
271 bookmark_model_->mobile_node(),
272 result);
273 SendResult(result);
274 }
275
276 void BookmarksHandler::SendResult(const scoped_ptr<DictionaryValue>& result) {
Evan Stade 2012/08/15 22:24:00 should take a const DictionaryValue&
Ted C 2012/08/15 22:50:48 Done.
277 if (result.get())
278 web_ui()->CallJavascriptFunction("ntp.bookmarks", *(result.get()));
279 }
280
281 void BookmarksHandler::Loaded(BookmarkModel* model, bool ids_reassigned) {
282 BookmarkModelChanged();
283 }
284
285 void BookmarksHandler::PartnerShimLoaded(PartnerBookmarksShim* shim) {
286 BookmarkModelChanged();
287 }
288
289 void BookmarksHandler::ShimBeingDeleted(PartnerBookmarksShim* shim) {
290 partner_bookmarks_shim_ = NULL;
291 }
292
293 void BookmarksHandler::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
294 extensive_changes_ = true;
295 }
296
297 void BookmarksHandler::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
298 extensive_changes_ = false;
299 BookmarkModelChanged();
300 }
301
302 void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model,
303 const BookmarkNode* parent,
304 int old_index,
305 const BookmarkNode* node) {
306 DictionaryValue result;
307 SetParentInBookmarksResult(*parent, &result);
308 result.SetString(kNodeIdParam, Int64ToString(node->id()));
309 NotifyModelChanged(result);
310 }
311
312 void BookmarksHandler::BookmarkNodeAdded(BookmarkModel* model,
313 const BookmarkNode* parent,
314 int index) {
315 DictionaryValue result;
316 SetParentInBookmarksResult(*parent, &result);
317 NotifyModelChanged(result);
318 }
319
320 void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model,
321 const BookmarkNode* node) {
322 DCHECK(partner_bookmarks_shim_);
323 DCHECK(!partner_bookmarks_shim_->IsPartnerBookmark(node));
324 DictionaryValue result;
325 SetParentInBookmarksResult(*node->parent(), &result);
326 result.SetString(kNodeIdParam, Int64ToString(node->id()));
327 NotifyModelChanged(result);
328 }
329
330 void BookmarksHandler::BookmarkModelChanged() {
331 if (bookmark_data_requested_ && !extensive_changes_)
332 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
333 }
334
335 void BookmarksHandler::NotifyModelChanged(const DictionaryValue& status) {
336 if (bookmark_data_requested_ && !extensive_changes_)
337 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged", status);
338 }
339
340 void BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut(
341 const ListValue* args) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343 Profile* profile = Profile::FromBrowserContext(
344 web_ui()->GetWebContents()->GetBrowserContext());
345 if (!profile)
346 return;
347
348 int64 id;
349 bool is_partner;
350 if (args && !args->empty() && ParseNtpBookmarkId(args, &id, &is_partner)) {
351 DCHECK(partner_bookmarks_shim_ != NULL);
352 BookmarkNode* node = const_cast<BookmarkNode*>(
Evan Stade 2012/08/15 22:24:00 don't use const cast.
Ted C 2012/08/15 22:50:48 Done.
353 partner_bookmarks_shim_->GetNodeByID(id, is_partner));
354 if (!node)
355 return;
356
357 FaviconService* favicon_service = profile->GetFaviconService(
358 Profile::EXPLICIT_ACCESS);
359 FaviconService::Handle handle = favicon_service->GetFaviconForURL(
360 node->url(),
361 history::FAVICON | history::TOUCH_ICON,
362 &cancelable_consumer_,
363 base::Bind(&BookmarksHandler::OnShortcutFaviconDataAvailable,
364 base::Unretained(this)));
365 cancelable_consumer_.SetClientData(favicon_service, handle, node);
366 }
367 }
368
369 void BookmarksHandler::OnShortcutFaviconDataAvailable(
370 FaviconService::Handle handle,
371 history::FaviconData favicon) {
372 SkColor color = SK_ColorWHITE;
373 SkBitmap favicon_bitmap;
374 if (favicon.is_valid()) {
375 color = GetDominantColorForFavicon(favicon.image_data);
376 gfx::PNGCodec::Decode(favicon.image_data->front(),
377 favicon.image_data->size(),
378 &favicon_bitmap);
379 }
380
381 Profile* profile = Profile::FromBrowserContext(
382 web_ui()->GetWebContents()->GetBrowserContext());
383 BookmarkNode* node = cancelable_consumer_.GetClientData(
384 profile->GetFaviconService(Profile::EXPLICIT_ACCESS), handle);
385
386 TabAndroid* tab = TabAndroid::FromWebContents(
387 web_ui()->GetWebContents());
388 if (tab) {
389 tab->AddShortcutToBookmark(node->url(), node->GetTitle(),
390 favicon_bitmap, SkColorGetR(color),
391 SkColorGetG(color), SkColorGetB(color));
392 }
393 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698