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

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

Issue 259393006: Remove WebUI NTP on Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 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/metrics/histogram.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/android/tab_android.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/favicon/favicon_service_factory.h"
16 #include "chrome/browser/profiles/incognito_helpers.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/browser/ui/webui/favicon_source.h"
20 #include "chrome/common/pref_names.h"
21 #include "components/bookmarks/core/browser/bookmark_model.h"
22 #include "components/bookmarks/core/browser/bookmark_utils.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/url_data_source.h"
25 #include "content/public/browser/web_contents.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/gfx/codec/png_codec.h"
28 #include "ui/gfx/color_analysis.h"
29 #include "ui/gfx/favicon_size.h"
30
31 using base::Int64ToString;
32 using content::BrowserThread;
33
34 namespace {
35
36 static const char* kParentIdParam = "parent_id";
37 static const char* kNodeIdParam = "node_id";
38
39 // Defines actions taken by the user over the partner bookmarks on NTP for
40 // NewTabPage.BookmarkActionAndroid histogram.
41 // Should be kept in sync with the values in histograms.xml.
42 enum PartnerBookmarkAction {
43 BOOKMARK_ACTION_DELETE_BOOKMARK_PARTNER = 0,
44 BOOKMARK_ACTION_DELETE_ROOT_FOLDER_PARTNER = 1,
45 BOOKMARK_ACTION_EDIT_BOOKMARK_PARTNER = 2,
46 BOOKMARK_ACTION_EDIT_ROOT_FOLDER_PARTNER = 3,
47 BOOKMARK_ACTION_BUCKET_BOUNDARY = 4
48 };
49
50 // Helper to record a bookmark action in BookmarkActionAndroid histogram.
51 void RecordBookmarkAction(PartnerBookmarkAction type) {
52 UMA_HISTOGRAM_ENUMERATION("NewTabPage.BookmarkActionAndroid", type,
53 BOOKMARK_ACTION_BUCKET_BOUNDARY);
54 }
55
56 std::string BookmarkTypeAsString(BookmarkNode::Type type) {
57 switch (type) {
58 case BookmarkNode::URL:
59 return "URL";
60 case BookmarkNode::FOLDER:
61 return "FOLDER";
62 case BookmarkNode::BOOKMARK_BAR:
63 return "BOOKMARK_BAR";
64 case BookmarkNode::OTHER_NODE:
65 return "OTHER_NODE";
66 case BookmarkNode::MOBILE:
67 return "MOBILE";
68 default:
69 return "UNKNOWN";
70 }
71 }
72
73 SkColor GetDominantColorForFavicon(scoped_refptr<base::RefCountedMemory> png) {
74 color_utils::GridSampler sampler;
75 // 100 here is the darkness_limit which represents the minimum sum of the RGB
76 // components that is acceptable as a color choice. This can be from 0 to 765.
77 // 665 here is the brightness_limit represents the maximum sum of the RGB
78 // components that is acceptable as a color choice. This can be from 0 to 765.
79 return color_utils::CalculateKMeanColorOfPNG(png, 100, 665, &sampler);
80 }
81
82 } // namespace
83
84 BookmarksHandler::BookmarksHandler()
85 : bookmark_model_(NULL),
86 partner_bookmarks_shim_(NULL),
87 bookmark_data_requested_(false),
88 extensive_changes_(false) {
89 }
90
91 BookmarksHandler::~BookmarksHandler() {
92 if (bookmark_model_)
93 bookmark_model_->RemoveObserver(this);
94
95 if (partner_bookmarks_shim_)
96 partner_bookmarks_shim_->RemoveObserver(this);
97
98 if (managed_bookmarks_shim_)
99 managed_bookmarks_shim_->RemoveObserver(this);
100 }
101
102 void BookmarksHandler::RegisterMessages() {
103 // Listen for the bookmark change. We need the both bookmark and folder
104 // change, the NotificationService is not sufficient.
105 Profile* profile = Profile::FromBrowserContext(
106 web_ui()->GetWebContents()->GetBrowserContext());
107
108 content::URLDataSource::Add(
109 profile, new FaviconSource(profile, FaviconSource::ANY));
110
111 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
112 if (bookmark_model_) {
113 bookmark_model_->AddObserver(this);
114 // Since a sync or import could have started before this class is
115 // initialized, we need to make sure that our initial state is
116 // up to date.
117 extensive_changes_ = bookmark_model_->IsDoingExtensiveChanges();
118 }
119
120 // Create the partner Bookmarks shim as early as possible (but don't attach).
121 if (!partner_bookmarks_shim_) {
122 partner_bookmarks_shim_ = PartnerBookmarksShim::BuildForBrowserContext(
123 chrome::GetBrowserContextRedirectedInIncognito(
124 web_ui()->GetWebContents()->GetBrowserContext()));
125 partner_bookmarks_shim_->AddObserver(this);
126 }
127
128 managed_bookmarks_shim_.reset(new ManagedBookmarksShim(profile->GetPrefs()));
129 managed_bookmarks_shim_->AddObserver(this);
130
131 // Register ourselves as the handler for the bookmark javascript callbacks.
132 web_ui()->RegisterMessageCallback("getBookmarks",
133 base::Bind(&BookmarksHandler::HandleGetBookmarks,
134 base::Unretained(this)));
135 web_ui()->RegisterMessageCallback("deleteBookmark",
136 base::Bind(&BookmarksHandler::HandleDeleteBookmark,
137 base::Unretained(this)));
138 web_ui()->RegisterMessageCallback("editBookmark",
139 base::Bind(&BookmarksHandler::HandleEditBookmark,
140 base::Unretained(this)));
141 web_ui()->RegisterMessageCallback("createHomeScreenBookmarkShortcut",
142 base::Bind(&BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut,
143 base::Unretained(this)));
144 }
145
146 void BookmarksHandler::HandleGetBookmarks(const base::ListValue* args) {
147 DCHECK_CURRENTLY_ON(BrowserThread::UI);
148
149 bookmark_data_requested_ = true;
150 if (!AreModelsLoaded())
151 return; // is handled in Loaded()/PartnerShimLoaded() callback.
152
153 const BookmarkNode* node = GetNodeByID(args);
154 if (node)
155 QueryBookmarkFolder(node);
156 else
157 QueryInitialBookmarks();
158 }
159
160 void BookmarksHandler::HandleDeleteBookmark(const base::ListValue* args) {
161 DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 if (!AreModelsLoaded())
163 return;
164
165 const BookmarkNode* node = GetNodeByID(args);
166 if (!node)
167 return;
168
169 if (!IsEditable(node)) {
170 NOTREACHED();
171 return;
172 }
173
174 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) {
175 if (partner_bookmarks_shim_->GetPartnerBookmarksRoot() == node)
176 RecordBookmarkAction(BOOKMARK_ACTION_DELETE_ROOT_FOLDER_PARTNER);
177 else
178 RecordBookmarkAction(BOOKMARK_ACTION_DELETE_BOOKMARK_PARTNER);
179 partner_bookmarks_shim_->RemoveBookmark(node);
180 return;
181 }
182
183 const BookmarkNode* parent_node = node->parent();
184 bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
185 }
186
187 void BookmarksHandler::HandleEditBookmark(const base::ListValue* args) {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 if (!AreModelsLoaded())
190 return;
191
192 const BookmarkNode* node = GetNodeByID(args);
193 if (!node)
194 return;
195
196 if (!IsEditable(node)) {
197 NOTREACHED();
198 return;
199 }
200
201 TabAndroid* tab = TabAndroid::FromWebContents(web_ui()->GetWebContents());
202 if (tab) {
203 if (partner_bookmarks_shim_->IsPartnerBookmark(node)) {
204 if (partner_bookmarks_shim_->GetPartnerBookmarksRoot() == node)
205 RecordBookmarkAction(BOOKMARK_ACTION_EDIT_ROOT_FOLDER_PARTNER);
206 else
207 RecordBookmarkAction(BOOKMARK_ACTION_EDIT_BOOKMARK_PARTNER);
208 }
209 tab->EditBookmark(node->id(),
210 GetTitle(node),
211 node->is_folder(),
212 partner_bookmarks_shim_->IsPartnerBookmark(node));
213 }
214 }
215
216 bool BookmarksHandler::AreModelsLoaded() const {
217 Profile* profile = Profile::FromBrowserContext(
218 web_ui()->GetWebContents()->GetBrowserContext());
219 if (!profile)
220 return false;
221
222 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
223 if (!model || !model->loaded())
224 return false;
225
226 return partner_bookmarks_shim_ && partner_bookmarks_shim_->IsLoaded();
227 }
228
229 void BookmarksHandler::NotifyModelChanged(const base::DictionaryValue& status) {
230 DCHECK(AreModelsLoaded());
231
232 if (bookmark_data_requested_ && !extensive_changes_)
233 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged", status);
234 }
235
236 std::string BookmarksHandler::GetBookmarkIdForNtp(const BookmarkNode* node) {
237 DCHECK(AreModelsLoaded());
238
239 std::string prefix;
240 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
241 prefix = "p";
242 else if (managed_bookmarks_shim_->IsManagedBookmark(node))
243 prefix = "m";
244 return prefix + Int64ToString(node->id());
245 }
246
247 void BookmarksHandler::SetParentInBookmarksResult(
248 const BookmarkNode* parent,
249 base::DictionaryValue* result) {
250 result->SetString(kParentIdParam, GetBookmarkIdForNtp(parent));
251 }
252
253 void BookmarksHandler::PopulateBookmark(const BookmarkNode* node,
254 base::ListValue* result) {
255 if (!result)
256 return;
257
258 DCHECK(AreModelsLoaded());
259 if (!IsReachable(node))
260 return;
261
262 base::DictionaryValue* filler_value = new base::DictionaryValue();
263 filler_value->SetString("title", GetTitle(node));
264 filler_value->SetBoolean("editable", IsEditable(node));
265 if (node->is_url()) {
266 filler_value->SetBoolean("folder", false);
267 filler_value->SetString("url", node->url().spec());
268 } else {
269 filler_value->SetBoolean("folder", true);
270 }
271 filler_value->SetString("id", GetBookmarkIdForNtp(node));
272 filler_value->SetString("type", BookmarkTypeAsString(node->type()));
273 result->Append(filler_value);
274 }
275
276 void BookmarksHandler::PopulateBookmarksInFolder(
277 const BookmarkNode* folder,
278 base::DictionaryValue* result) {
279 DCHECK(AreModelsLoaded());
280 if (!IsReachable(folder))
281 return;
282
283 base::ListValue* bookmarks = new base::ListValue();
284
285 // If this is the Mobile bookmarks folder then add the "Managed bookmarks"
286 // folder first, so that it's the first entry.
287 if (bookmark_model_ && folder == bookmark_model_->mobile_node() &&
288 managed_bookmarks_shim_->HasManagedBookmarks()) {
289 PopulateBookmark(managed_bookmarks_shim_->GetManagedBookmarksRoot(),
290 bookmarks);
291 }
292
293 for (int i = 0; i < folder->child_count(); i++) {
294 const BookmarkNode* bookmark= folder->GetChild(i);
295 PopulateBookmark(bookmark, bookmarks);
296 }
297
298 // Make sure we iterate over the partner's attach point
299 if (bookmark_model_ && folder == bookmark_model_->mobile_node() &&
300 partner_bookmarks_shim_->HasPartnerBookmarks()) {
301 PopulateBookmark(partner_bookmarks_shim_->GetPartnerBookmarksRoot(),
302 bookmarks);
303 }
304
305 base::ListValue* folder_hierarchy = new base::ListValue();
306 const BookmarkNode* parent = GetParentOf(folder);
307
308 while (parent != NULL) {
309 base::DictionaryValue* hierarchy_entry = new base::DictionaryValue();
310 if (IsRoot(parent))
311 hierarchy_entry->SetBoolean("root", true);
312
313 hierarchy_entry->SetString("title", GetTitle(parent));
314 hierarchy_entry->SetString("id", GetBookmarkIdForNtp(parent));
315 folder_hierarchy->Append(hierarchy_entry);
316 parent = GetParentOf(parent);
317 }
318
319 result->SetString("title", GetTitle(folder));
320 result->SetString("id", GetBookmarkIdForNtp(folder));
321 result->SetBoolean("root", IsRoot(folder));
322 result->Set("bookmarks", bookmarks);
323 result->Set("hierarchy", folder_hierarchy);
324 }
325
326 void BookmarksHandler::QueryBookmarkFolder(const BookmarkNode* node) {
327 DCHECK(AreModelsLoaded());
328 if (node->is_folder() && IsReachable(node)) {
329 base::DictionaryValue result;
330 PopulateBookmarksInFolder(node, &result);
331 SendResult(result);
332 } else {
333 // If we receive an ID that no longer maps to a bookmark folder, just
334 // return the initial bookmark folder.
335 QueryInitialBookmarks();
336 }
337 }
338
339 void BookmarksHandler::QueryInitialBookmarks() {
340 DCHECK(AreModelsLoaded());
341 base::DictionaryValue result;
342 PopulateBookmarksInFolder(bookmark_model_->mobile_node(), &result);
343 SendResult(result);
344 }
345
346 void BookmarksHandler::SendResult(const base::DictionaryValue& result) {
347 web_ui()->CallJavascriptFunction("ntp.bookmarks", result);
348 }
349
350 void BookmarksHandler::BookmarkModelLoaded(BookmarkModel* model,
351 bool ids_reassigned) {
352 if (AreModelsLoaded())
353 BookmarkModelChanged();
354 }
355
356 void BookmarksHandler::PartnerShimChanged(PartnerBookmarksShim* shim) {
357 if (AreModelsLoaded())
358 BookmarkModelChanged();
359 }
360
361 void BookmarksHandler::PartnerShimLoaded(PartnerBookmarksShim* shim) {
362 if (AreModelsLoaded())
363 BookmarkModelChanged();
364 }
365
366 void BookmarksHandler::ShimBeingDeleted(PartnerBookmarksShim* shim) {
367 partner_bookmarks_shim_ = NULL;
368 }
369
370 void BookmarksHandler::OnManagedBookmarksChanged() {
371 if (AreModelsLoaded())
372 BookmarkModelChanged();
373 }
374
375 void BookmarksHandler::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
376 extensive_changes_ = true;
377 }
378
379 void BookmarksHandler::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
380 extensive_changes_ = false;
381 if (AreModelsLoaded())
382 BookmarkModelChanged();
383 }
384
385 void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model,
386 const BookmarkNode* parent,
387 int old_index,
388 const BookmarkNode* node) {
389 if (!AreModelsLoaded())
390 return;
391
392 base::DictionaryValue result;
393 SetParentInBookmarksResult(parent, &result);
394 result.SetString(kNodeIdParam, Int64ToString(node->id()));
395 NotifyModelChanged(result);
396 }
397
398 void BookmarksHandler::BookmarkAllNodesRemoved(BookmarkModel* model) {
399 if (!AreModelsLoaded())
400 return;
401
402 if (bookmark_data_requested_ && !extensive_changes_)
403 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
404 }
405
406 void BookmarksHandler::BookmarkNodeAdded(
407 BookmarkModel* model, const BookmarkNode* parent, int index) {
408 if (!AreModelsLoaded())
409 return;
410
411 base::DictionaryValue result;
412 SetParentInBookmarksResult(parent, &result);
413 NotifyModelChanged(result);
414 }
415
416 void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model,
417 const BookmarkNode* node) {
418 if (!AreModelsLoaded())
419 return;
420
421 DCHECK(!partner_bookmarks_shim_->IsPartnerBookmark(node));
422 base::DictionaryValue result;
423 SetParentInBookmarksResult(node->parent(), &result);
424 result.SetString(kNodeIdParam, Int64ToString(node->id()));
425 NotifyModelChanged(result);
426 }
427
428 void BookmarksHandler::BookmarkModelChanged() {
429 if (!AreModelsLoaded())
430 return;
431
432 if (bookmark_data_requested_ && !extensive_changes_)
433 web_ui()->CallJavascriptFunction("ntp.bookmarkChanged");
434 }
435
436 void BookmarksHandler::HandleCreateHomeScreenBookmarkShortcut(
437 const base::ListValue* args) {
438 DCHECK_CURRENTLY_ON(BrowserThread::UI);
439 if (!AreModelsLoaded())
440 return;
441
442 Profile* profile = Profile::FromBrowserContext(
443 web_ui()->GetWebContents()->GetBrowserContext());
444 if (!profile)
445 return;
446
447 const BookmarkNode* node = GetNodeByID(args);
448 if (!node)
449 return;
450
451 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
452 profile, Profile::EXPLICIT_ACCESS);
453 favicon_service->GetRawFaviconForURL(
454 FaviconService::FaviconForURLParams(node->url(),
455 favicon_base::TOUCH_PRECOMPOSED_ICON |
456 favicon_base::TOUCH_ICON |
457 favicon_base::FAVICON,
458 0), // request the largest icon.
459 ui::SCALE_FACTOR_100P, // density doesn't matter for the largest icon.
460 base::Bind(&BookmarksHandler::OnShortcutFaviconDataAvailable,
461 base::Unretained(this),
462 node),
463 &cancelable_task_tracker_);
464 }
465
466 void BookmarksHandler::OnShortcutFaviconDataAvailable(
467 const BookmarkNode* node,
468 const favicon_base::FaviconBitmapResult& bitmap_result) {
469 if (!AreModelsLoaded())
470 return;
471
472 SkColor color = SK_ColorWHITE;
473 SkBitmap favicon_bitmap;
474 if (bitmap_result.is_valid()) {
475 color = GetDominantColorForFavicon(bitmap_result.bitmap_data);
476 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
477 bitmap_result.bitmap_data->size(),
478 &favicon_bitmap);
479 }
480 TabAndroid* tab = TabAndroid::FromWebContents(web_ui()->GetWebContents());
481 if (tab) {
482 tab->AddShortcutToBookmark(node->url(),
483 GetTitle(node),
484 favicon_bitmap, SkColorGetR(color),
485 SkColorGetG(color), SkColorGetB(color));
486 }
487 }
488
489 const BookmarkNode* BookmarksHandler::GetNodeByID(
490 const base::ListValue* args) const {
491 DCHECK(AreModelsLoaded());
492
493 // Parses a bookmark ID passed back from the NTP. The IDs differ from the
494 // normal int64 bookmark ID because we prepend a "p" if the ID represents a
495 // partner bookmark, and an "m" if the ID represents a managed bookmark.
496
497 if (!args || args->empty())
498 return NULL;
499
500 std::string string_id;
501 if (!args->GetString(0, &string_id) || string_id.empty()) {
502 NOTREACHED();
503 return NULL;
504 }
505
506 bool is_partner = string_id[0] == 'p';
507 bool is_managed = string_id[0] == 'm';
508
509 if (is_partner || is_managed)
510 string_id = string_id.substr(1);
511
512 int64 id = 0;
513 if (!base::StringToInt64(string_id, &id)) {
514 NOTREACHED();
515 return NULL;
516 }
517
518 if (is_managed)
519 return managed_bookmarks_shim_->GetNodeByID(id);
520
521 if (is_partner)
522 return partner_bookmarks_shim_->GetNodeByID(id);
523
524 return GetBookmarkNodeByID(bookmark_model_, id);
525 }
526
527 const BookmarkNode* BookmarksHandler::GetParentOf(
528 const BookmarkNode* node) const {
529 DCHECK(AreModelsLoaded());
530 if (node == managed_bookmarks_shim_->GetManagedBookmarksRoot() ||
531 node == partner_bookmarks_shim_->GetPartnerBookmarksRoot()) {
532 return bookmark_model_->mobile_node();
533 }
534
535 return node->parent();
536 }
537
538 base::string16 BookmarksHandler::GetTitle(const BookmarkNode* node) const {
539 DCHECK(AreModelsLoaded());
540 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
541 return partner_bookmarks_shim_->GetTitle(node);
542
543 return node->GetTitle();
544 }
545
546 bool BookmarksHandler::IsReachable(const BookmarkNode* node) const {
547 DCHECK(AreModelsLoaded());
548 if (!partner_bookmarks_shim_->IsPartnerBookmark(node))
549 return true;
550
551 return partner_bookmarks_shim_->IsReachable(node);
552 }
553
554 bool BookmarksHandler::IsEditable(const BookmarkNode* node) const {
555 DCHECK(AreModelsLoaded());
556
557 // Reserved system nodes and managed bookmarks are not editable.
558 // Additionally, bookmark editing may be completely disabled
559 // via a managed preference.
560 if (!node ||
561 (node->type() != BookmarkNode::FOLDER &&
562 node->type() != BookmarkNode::URL)) {
563 return false;
564 }
565
566 const PrefService* pref = Profile::FromBrowserContext(
567 web_ui()->GetWebContents()->GetBrowserContext())->GetPrefs();
568 if (!pref->GetBoolean(prefs::kEditBookmarksEnabled))
569 return false;
570
571 if (partner_bookmarks_shim_->IsPartnerBookmark(node))
572 return true;
573
574 return !managed_bookmarks_shim_->IsManagedBookmark(node);
575 }
576
577 bool BookmarksHandler::IsRoot(const BookmarkNode* node) const {
578 DCHECK(AreModelsLoaded());
579
580 return node->is_root() &&
581 node != partner_bookmarks_shim_->GetPartnerBookmarksRoot() &&
582 node != managed_bookmarks_shim_->GetManagedBookmarksRoot();
583 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/webui/ntp/android/bookmarks_handler.h ('k') | chrome/browser/ui/webui/ntp/android/context_menu_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698