blob: 7ca6157944470c774f6c58d1542f762f74a3f4cd [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "chrome/browser/bookmark_bar_model.h"
31
32#include "base/gfx/png_decoder.h"
[email protected]f25387b2008-08-21 15:20:3333#include "chrome/browser/history/query_parser.h"
initial.commit09911bf2008-07-26 23:55:2934#include "chrome/browser/profile.h"
[email protected]f25387b2008-08-21 15:20:3335#include "chrome/browser/bookmark_storage.h"
36#include "chrome/common/scoped_vector.h"
37
initial.commit09911bf2008-07-26 23:55:2938#include "generated_resources.h"
39
[email protected]f25387b2008-08-21 15:20:3340namespace {
41
42// Functions used for sorting.
43bool MoreRecentlyModified(BookmarkBarNode* n1, BookmarkBarNode* n2) {
44 return n1->date_group_modified() > n2->date_group_modified();
45}
46
47bool MoreRecentlyAdded(BookmarkBarNode* n1, BookmarkBarNode* n2) {
48 return n1->date_added() > n2->date_added();
49}
50
51} // namespace
52
initial.commit09911bf2008-07-26 23:55:2953// BookmarkBarNode ------------------------------------------------------------
54
[email protected]f25387b2008-08-21 15:20:3355namespace {
56
57// ID for BookmarkBarNodes.
58// Various places assume an invalid id if == 0, for that reason we start with 1.
59int next_id_ = 1;
60
61}
62
initial.commit09911bf2008-07-26 23:55:2963const SkBitmap& BookmarkBarNode::GetFavIcon() {
64 if (!loaded_favicon_) {
65 loaded_favicon_ = true;
66 model_->LoadFavIcon(this);
67 }
68 return favicon_;
69}
70
[email protected]f25387b2008-08-21 15:20:3371BookmarkBarNode::BookmarkBarNode(BookmarkBarModel* model, const GURL& url)
initial.commit09911bf2008-07-26 23:55:2972 : model_(model),
[email protected]f25387b2008-08-21 15:20:3373 id_(next_id_++),
initial.commit09911bf2008-07-26 23:55:2974 loaded_favicon_(false),
75 favicon_load_handle_(0),
[email protected]f25387b2008-08-21 15:20:3376 url_(url),
77 type_(!url.is_empty() ? history::StarredEntry::URL :
78 history::StarredEntry::BOOKMARK_BAR),
initial.commit09911bf2008-07-26 23:55:2979 date_added_(Time::Now()) {
initial.commit09911bf2008-07-26 23:55:2980}
81
82void BookmarkBarNode::Reset(const history::StarredEntry& entry) {
[email protected]f25387b2008-08-21 15:20:3383 DCHECK(entry.type != history::StarredEntry::URL ||
84 entry.url == url_);
85
initial.commit09911bf2008-07-26 23:55:2986 favicon_ = SkBitmap();
initial.commit09911bf2008-07-26 23:55:2987 type_ = entry.type;
88 date_added_ = entry.date_added;
89 date_group_modified_ = entry.date_group_modified;
90 SetTitle(entry.title);
91}
92
initial.commit09911bf2008-07-26 23:55:2993// BookmarkBarModel -----------------------------------------------------------
94
95BookmarkBarModel::BookmarkBarModel(Profile* profile)
96 : profile_(profile),
97 loaded_(false),
98#pragma warning(suppress: 4355) // Okay to pass "this" here.
[email protected]f25387b2008-08-21 15:20:3399 root_(this, GURL()),
initial.commit09911bf2008-07-26 23:55:29100 bookmark_bar_node_(NULL),
101 other_node_(NULL) {
[email protected]f25387b2008-08-21 15:20:33102 // Create the bookmark bar and other bookmarks folders. These always exist.
103 CreateBookmarkBarNode();
104 CreateOtherBookmarksNode();
[email protected]4d0cd7ce2008-08-11 16:40:57105
[email protected]f25387b2008-08-21 15:20:33106 // And add them to the root.
107 //
108 // WARNING: order is important here, various places assume bookmark bar then
109 // other node.
110 root_.Add(0, bookmark_bar_node_);
111 root_.Add(1, other_node_);
112
113 if (!profile_) {
114 // Profile is null during testing.
initial.commit09911bf2008-07-26 23:55:29115 loaded_ = true;
116 return;
117 }
118
[email protected]f25387b2008-08-21 15:20:33119 // Listen for changes to starred icons so that we can update the favicon of
120 // the node appropriately.
121 NotificationService::current()->AddObserver(
122 this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_));
123
124 // Load the bookmarks. BookmarkStorage notifies us when done.
125 store_ = new BookmarkStorage(profile_, this);
126 store_->LoadBookmarks(false);
initial.commit09911bf2008-07-26 23:55:29127}
128
129BookmarkBarModel::~BookmarkBarModel() {
130 if (profile_) {
131 NotificationService::current()->RemoveObserver(
[email protected]f25387b2008-08-21 15:20:33132 this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_));
133 }
134
135 if (store_) {
136 // The store maintains a reference back to us. We need to tell it we're gone
137 // so that it doesn't try and invoke a method back on us again.
138 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29139 }
140}
141
142BookmarkBarNode* BookmarkBarModel::GetParentForNewNodes() {
143 std::vector<BookmarkBarNode*> nodes;
144
145 GetMostRecentlyModifiedGroupNodes(&root_, 1, &nodes);
146 return nodes.empty() ? bookmark_bar_node_ : nodes[0];
147}
148
149std::vector<BookmarkBarNode*> BookmarkBarModel::GetMostRecentlyModifiedGroups(
150 size_t max_count) {
151 std::vector<BookmarkBarNode*> nodes;
152 GetMostRecentlyModifiedGroupNodes(&root_, max_count, &nodes);
153
154 if (nodes.size() < max_count) {
155 // Add the bookmark bar and other nodes if there is space.
156 if (find(nodes.begin(), nodes.end(), bookmark_bar_node_) == nodes.end())
157 nodes.push_back(bookmark_bar_node_);
158
159 if (nodes.size() < max_count &&
160 find(nodes.begin(), nodes.end(), other_node_) == nodes.end()) {
161 nodes.push_back(other_node_);
162 }
163 }
164 return nodes;
165}
166
[email protected]f25387b2008-08-21 15:20:33167void BookmarkBarModel::GetMostRecentlyAddedEntries(
168 size_t count,
169 std::vector<BookmarkBarNode*>* nodes) {
170 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
171 i != nodes_ordered_by_url_set_.end(); ++i) {
172 std::vector<BookmarkBarNode*>::iterator insert_position =
173 std::upper_bound(nodes->begin(), nodes->end(), *i, &MoreRecentlyAdded);
174 if (nodes->size() < count || insert_position != nodes->end()) {
175 nodes->insert(insert_position, *i);
176 while (nodes->size() > count)
177 nodes->pop_back();
178 }
initial.commit09911bf2008-07-26 23:55:29179 }
180}
181
[email protected]f25387b2008-08-21 15:20:33182void BookmarkBarModel::GetBookmarksMatchingText(
183 const std::wstring& text,
184 std::vector<BookmarkBarNode*>* nodes) {
185 QueryParser parser;
186 ScopedVector<QueryNode> query_nodes;
187 parser.ParseQuery(text, &query_nodes.get());
188 if (query_nodes.empty())
initial.commit09911bf2008-07-26 23:55:29189 return;
190
[email protected]f25387b2008-08-21 15:20:33191 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
192 i != nodes_ordered_by_url_set_.end(); ++i) {
193 if (parser.DoesQueryMatch((*i)->GetTitle(), query_nodes.get()))
194 nodes->push_back(*i);
initial.commit09911bf2008-07-26 23:55:29195 }
196}
197
[email protected]f25387b2008-08-21 15:20:33198void BookmarkBarModel::Remove(BookmarkBarNode* parent, int index) {
199 if (!loaded_ || !IsValidIndex(parent, index, false) || parent == &root_) {
200 NOTREACHED();
201 return;
202 }
203 RemoveAndDeleteNode(parent->GetChild(index));
204}
205
initial.commit09911bf2008-07-26 23:55:29206void BookmarkBarModel::Move(BookmarkBarNode* node,
207 BookmarkBarNode* new_parent,
208 int index) {
[email protected]f25387b2008-08-21 15:20:33209 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
210 new_parent == &root_ || node == &root_ || node == bookmark_bar_node_ ||
initial.commit09911bf2008-07-26 23:55:29211 node == other_node_) {
212 NOTREACHED();
213 return;
214 }
215
216 if (new_parent->HasAncestor(node)) {
217 // Can't make an ancestor of the node be a child of the node.
218 NOTREACHED();
219 return;
220 }
221
222 SetDateGroupModified(new_parent, Time::Now());
223
224 BookmarkBarNode* old_parent = node->GetParent();
225 int old_index = old_parent->IndexOfChild(node);
226
227 if (old_parent == new_parent &&
228 (index == old_index || index == old_index + 1)) {
229 // Node is already in this position, nothing to do.
230 return;
231 }
232
233 if (old_parent == new_parent && index > old_index)
234 index--;
235 new_parent->Add(index, node);
236
[email protected]f25387b2008-08-21 15:20:33237 if (store_.get())
238 store_->ScheduleSave();
239
initial.commit09911bf2008-07-26 23:55:29240 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
241 BookmarkNodeMoved(this, old_parent, old_index,
242 new_parent, index));
243}
244
245void BookmarkBarModel::SetTitle(BookmarkBarNode* node,
246 const std::wstring& title) {
[email protected]f25387b2008-08-21 15:20:33247 if (!node) {
248 NOTREACHED();
249 return;
250 }
initial.commit09911bf2008-07-26 23:55:29251 if (node->GetTitle() == title)
252 return;
[email protected]f25387b2008-08-21 15:20:33253
initial.commit09911bf2008-07-26 23:55:29254 node->SetTitle(title);
[email protected]f25387b2008-08-21 15:20:33255
256 if (store_.get())
257 store_->ScheduleSave();
258
initial.commit09911bf2008-07-26 23:55:29259 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
260 BookmarkNodeChanged(this, node));
261}
262
263BookmarkBarNode* BookmarkBarModel::GetNodeByURL(const GURL& url) {
[email protected]f25387b2008-08-21 15:20:33264 BookmarkBarNode tmp_node(this, url);
initial.commit09911bf2008-07-26 23:55:29265 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
266 return (i != nodes_ordered_by_url_set_.end()) ? *i : NULL;
267}
268
[email protected]f25387b2008-08-21 15:20:33269BookmarkBarNode* BookmarkBarModel::GetNodeByID(int id) {
initial.commit09911bf2008-07-26 23:55:29270 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33271 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29272}
273
274BookmarkBarNode* BookmarkBarModel::AddGroup(
275 BookmarkBarNode* parent,
276 int index,
277 const std::wstring& title) {
[email protected]f25387b2008-08-21 15:20:33278 if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29279 // Can't add to the root.
280 NOTREACHED();
281 return NULL;
282 }
283
[email protected]f25387b2008-08-21 15:20:33284 BookmarkBarNode* new_node = new BookmarkBarNode(this, GURL());
initial.commit09911bf2008-07-26 23:55:29285 new_node->SetTitle(title);
286 new_node->type_ = history::StarredEntry::USER_GROUP;
287
288 return AddNode(parent, index, new_node);
289}
290
291BookmarkBarNode* BookmarkBarModel::AddURL(BookmarkBarNode* parent,
292 int index,
293 const std::wstring& title,
294 const GURL& url) {
295 return AddURLWithCreationTime(parent, index, title, url, Time::Now());
296}
297
298BookmarkBarNode* BookmarkBarModel::AddURLWithCreationTime(
299 BookmarkBarNode* parent,
300 int index,
301 const std::wstring& title,
302 const GURL& url,
303 const Time& creation_time) {
[email protected]f25387b2008-08-21 15:20:33304 if (!loaded_ || !url.is_valid() || parent == &root_ ||
305 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29306 NOTREACHED();
307 return NULL;
308 }
initial.commit09911bf2008-07-26 23:55:29309
310 BookmarkBarNode* existing_node = GetNodeByURL(url);
311 if (existing_node) {
312 Move(existing_node, parent, index);
313 SetTitle(existing_node, title);
314 return existing_node;
315 }
316
317 SetDateGroupModified(parent, creation_time);
318
[email protected]f25387b2008-08-21 15:20:33319 BookmarkBarNode* new_node = new BookmarkBarNode(this, url);
initial.commit09911bf2008-07-26 23:55:29320 new_node->SetTitle(title);
initial.commit09911bf2008-07-26 23:55:29321 new_node->date_added_ = creation_time;
322 new_node->type_ = history::StarredEntry::URL;
323
324 nodes_ordered_by_url_set_.insert(new_node);
325
326 return AddNode(parent, index, new_node);
327}
328
329void BookmarkBarModel::SetURLStarred(const GURL& url,
330 const std::wstring& title,
331 bool is_starred) {
332 BookmarkBarNode* node = GetNodeByURL(url);
333 if (is_starred && !node) {
334 // Add the url.
335 BookmarkBarNode* parent = GetParentForNewNodes();
336 AddURL(parent, parent->GetChildCount(), title, url);
337 } else if (!is_starred && node) {
338 Remove(node->GetParent(), node->GetParent()->IndexOfChild(node));
339 }
340}
341
342void BookmarkBarModel::ResetDateGroupModified(BookmarkBarNode* node) {
343 SetDateGroupModified(node, Time());
344}
345
346void BookmarkBarModel::FavIconLoaded(BookmarkBarNode* node) {
347 // Send out notification to the observer.
348 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
349 BookmarkNodeFavIconLoaded(this, node));
350}
351
[email protected]f25387b2008-08-21 15:20:33352void BookmarkBarModel::RemoveNode(BookmarkBarNode* node,
353 std::set<GURL>* removed_urls) {
354 if (!loaded_ || !node || node == &root_ || node == bookmark_bar_node_ ||
355 node == other_node_) {
356 NOTREACHED();
357 return;
358 }
359
initial.commit09911bf2008-07-26 23:55:29360 if (node->GetType() == history::StarredEntry::URL) {
361 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
362 DCHECK(i != nodes_ordered_by_url_set_.end());
363 nodes_ordered_by_url_set_.erase(i);
[email protected]f25387b2008-08-21 15:20:33364 removed_urls->insert(node->GetURL());
initial.commit09911bf2008-07-26 23:55:29365 }
366
367 CancelPendingFavIconLoadRequests(node);
368
369 // Recurse through children.
370 for (int i = node->GetChildCount() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33371 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29372}
373
[email protected]f25387b2008-08-21 15:20:33374void BookmarkBarModel::OnBookmarkStorageLoadedBookmarks(
375 bool file_exists,
376 bool loaded_from_history) {
initial.commit09911bf2008-07-26 23:55:29377 if (loaded_) {
378 NOTREACHED();
379 return;
380 }
381
[email protected]f25387b2008-08-21 15:20:33382 if (file_exists || loaded_from_history || !profile_ ||
383 !profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) {
384 if (loaded_from_history) {
385 // We were just populated from the historical file. Schedule a save so
386 // that the main file is up to date.
387 store_->ScheduleSave();
388 }
initial.commit09911bf2008-07-26 23:55:29389
[email protected]f25387b2008-08-21 15:20:33390 // The file exists, we're loaded.
391 DoneLoading();
392 return;
393 }
394
395 // The file doesn't exist. If the bookmarks were in the db the history db
396 // will copy them to a file on init. Schedule an empty request to history so
397 // that we know history will have saved the bookmarks (if it had them). When
398 // the callback is processed
399 // when done we'll try and load the bookmarks again.
400 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->
401 ScheduleEmptyCallback(&load_consumer_,
402 NewCallback(this, &BookmarkBarModel::OnHistoryDone));
403}
404
405void BookmarkBarModel::OnHistoryDone(HistoryService::Handle handle) {
406 if (loaded_) {
407 NOTREACHED();
408 return;
409 }
410
411 // If the bookmarks were stored in the db the db will have migrated them to
412 // a file now. Try loading from the file.
413 store_->LoadBookmarks(true);
414}
415
416void BookmarkBarModel::DoneLoading() {
417 // Update nodes_ordered_by_url_set_ from the nodes.
418 PopulateNodesByURL(&root_);
419
initial.commit09911bf2008-07-26 23:55:29420 loaded_ = true;
421
[email protected]f25387b2008-08-21 15:20:33422 // Notify our direct observers.
initial.commit09911bf2008-07-26 23:55:29423 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, Loaded(this));
424
[email protected]f25387b2008-08-21 15:20:33425 // And generic notification.
initial.commit09911bf2008-07-26 23:55:29426 NotificationService::current()->Notify(
427 NOTIFY_BOOKMARK_MODEL_LOADED,
428 Source<Profile>(profile_),
429 NotificationService::NoDetails());
430}
431
initial.commit09911bf2008-07-26 23:55:29432void BookmarkBarModel::RemoveAndDeleteNode(BookmarkBarNode* delete_me) {
433 scoped_ptr<BookmarkBarNode> node(delete_me);
434
435 BookmarkBarNode* parent = node->GetParent();
436 DCHECK(parent);
437 int index = parent->IndexOfChild(node.get());
438 parent->Remove(index);
[email protected]f25387b2008-08-21 15:20:33439 history::URLsStarredDetails details(false);
440 RemoveNode(node.get(), &details.changed_urls);
initial.commit09911bf2008-07-26 23:55:29441
[email protected]f25387b2008-08-21 15:20:33442 if (store_.get())
443 store_->ScheduleSave();
444
initial.commit09911bf2008-07-26 23:55:29445 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
446 BookmarkNodeRemoved(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33447
448 NotificationService::current()->Notify(NOTIFY_URLS_STARRED,
449 Source<Profile>(profile_),
450 Details<history::URLsStarredDetails>(&details));
initial.commit09911bf2008-07-26 23:55:29451}
452
453BookmarkBarNode* BookmarkBarModel::AddNode(BookmarkBarNode* parent,
[email protected]f25387b2008-08-21 15:20:33454 int index,
455 BookmarkBarNode* node) {
initial.commit09911bf2008-07-26 23:55:29456 parent->Add(index, node);
457
[email protected]f25387b2008-08-21 15:20:33458 if (store_.get())
459 store_->ScheduleSave();
460
initial.commit09911bf2008-07-26 23:55:29461 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
462 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33463
464 if (node->GetType() == history::StarredEntry::URL) {
465 history::URLsStarredDetails details(true);
466 details.changed_urls.insert(node->GetURL());
467 NotificationService::current()->Notify(NOTIFY_URLS_STARRED,
468 Source<Profile>(profile_),
469 Details<history::URLsStarredDetails>(&details));
470 }
initial.commit09911bf2008-07-26 23:55:29471 return node;
472}
473
[email protected]f25387b2008-08-21 15:20:33474BookmarkBarNode* BookmarkBarModel::GetNodeByID(BookmarkBarNode* node,
475 int id) {
476 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29477 return node;
[email protected]f25387b2008-08-21 15:20:33478
initial.commit09911bf2008-07-26 23:55:29479 for (int i = 0; i < node->GetChildCount(); ++i) {
[email protected]f25387b2008-08-21 15:20:33480 BookmarkBarNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29481 if (result)
482 return result;
483 }
484 return NULL;
485}
486
[email protected]f25387b2008-08-21 15:20:33487bool BookmarkBarModel::IsValidIndex(BookmarkBarNode* parent,
488 int index,
489 bool allow_end) {
490 return (parent &&
491 (index >= 0 && (index < parent->GetChildCount() ||
492 (allow_end && index == parent->GetChildCount()))));
493 }
494
initial.commit09911bf2008-07-26 23:55:29495void BookmarkBarModel::SetDateGroupModified(BookmarkBarNode* parent,
496 const Time time) {
497 DCHECK(parent);
498 parent->date_group_modified_ = time;
[email protected]f25387b2008-08-21 15:20:33499
500 if (store_.get())
501 store_->ScheduleSave();
initial.commit09911bf2008-07-26 23:55:29502}
503
504void BookmarkBarModel::CreateBookmarkBarNode() {
505 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29506 entry.type = history::StarredEntry::BOOKMARK_BAR;
507 bookmark_bar_node_ = CreateRootNodeFromStarredEntry(entry);
508}
509
510void BookmarkBarModel::CreateOtherBookmarksNode() {
511 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29512 entry.type = history::StarredEntry::OTHER;
513 other_node_ = CreateRootNodeFromStarredEntry(entry);
514}
515
516BookmarkBarNode* BookmarkBarModel::CreateRootNodeFromStarredEntry(
517 const history::StarredEntry& entry) {
518 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
519 entry.type == history::StarredEntry::OTHER);
[email protected]f25387b2008-08-21 15:20:33520 BookmarkBarNode* node = new BookmarkBarNode(this, GURL());
initial.commit09911bf2008-07-26 23:55:29521 node->Reset(entry);
522 if (entry.type == history::StarredEntry::BOOKMARK_BAR)
523 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME));
524 else
525 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
initial.commit09911bf2008-07-26 23:55:29526 return node;
527}
528
initial.commit09911bf2008-07-26 23:55:29529void BookmarkBarModel::OnFavIconDataAvailable(
530 HistoryService::Handle handle,
531 bool know_favicon,
532 scoped_refptr<RefCountedBytes> data,
533 bool expired,
534 GURL icon_url) {
535 SkBitmap fav_icon;
[email protected]4167c3a2008-08-21 18:12:20536 BookmarkBarNode* node =
537 load_consumer_.GetClientData(
538 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
539 DCHECK(node);
540 node->favicon_load_handle_ = 0;
initial.commit09911bf2008-07-26 23:55:29541 if (know_favicon && data.get() &&
542 PNGDecoder::Decode(&data->data, &fav_icon)) {
initial.commit09911bf2008-07-26 23:55:29543 node->favicon_ = fav_icon;
initial.commit09911bf2008-07-26 23:55:29544 FavIconLoaded(node);
545 }
546}
547
548void BookmarkBarModel::LoadFavIcon(BookmarkBarNode* node) {
549 if (node->GetType() != history::StarredEntry::URL)
550 return;
551
552 DCHECK(node->GetURL().is_valid());
553 HistoryService* history_service =
554 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
555 if (!history_service)
556 return;
557
558 HistoryService::Handle handle = history_service->GetFavIconForURL(
559 node->GetURL(), &load_consumer_,
560 NewCallback(this, &BookmarkBarModel::OnFavIconDataAvailable));
561 load_consumer_.SetClientData(history_service, handle, node);
[email protected]f25387b2008-08-21 15:20:33562 node->favicon_load_handle_ = handle;
initial.commit09911bf2008-07-26 23:55:29563}
564
565void BookmarkBarModel::CancelPendingFavIconLoadRequests(BookmarkBarNode* node) {
566 if (node->favicon_load_handle_) {
567 HistoryService* history =
568 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
569 if (history)
570 history->CancelRequest(node->favicon_load_handle_);
571 node->favicon_load_handle_ = 0;
572 }
573}
574
initial.commit09911bf2008-07-26 23:55:29575void BookmarkBarModel::GetMostRecentlyModifiedGroupNodes(
576 BookmarkBarNode* parent,
577 size_t count,
578 std::vector<BookmarkBarNode*>* nodes) {
[email protected]f25387b2008-08-21 15:20:33579 if (parent != &root_ && parent->is_folder() &&
580 parent->date_group_modified() > Time()) {
initial.commit09911bf2008-07-26 23:55:29581 if (count == 0) {
582 nodes->push_back(parent);
583 } else {
584 std::vector<BookmarkBarNode*>::iterator i =
585 std::upper_bound(nodes->begin(), nodes->end(), parent,
586 &MoreRecentlyModified);
587 if (nodes->size() < count || i != nodes->end()) {
588 nodes->insert(i, parent);
589 while (nodes->size() > count)
590 nodes->pop_back();
591 }
592 }
593 } // else case, the root node, which we don't care about or imported nodes
594 // (which have a time of 0).
595 for (int i = 0; i < parent->GetChildCount(); ++i) {
596 BookmarkBarNode* child = parent->GetChild(i);
[email protected]f25387b2008-08-21 15:20:33597 if (child->is_folder())
initial.commit09911bf2008-07-26 23:55:29598 GetMostRecentlyModifiedGroupNodes(child, count, nodes);
initial.commit09911bf2008-07-26 23:55:29599 }
600}
601
602void BookmarkBarModel::Observe(NotificationType type,
603 const NotificationSource& source,
604 const NotificationDetails& details) {
605 switch (type) {
[email protected]f25387b2008-08-21 15:20:33606 case NOTIFY_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29607 // Prevent the observers from getting confused for multiple favicon loads.
608 Details<history::FavIconChangeDetails> favicon_details(details);
609 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
610 i != favicon_details->urls.end(); ++i) {
611 BookmarkBarNode* node = GetNodeByURL(*i);
612 if (node) {
613 // Got an updated favicon, for a URL, do a new request.
614 node->InvalidateFavicon();
615 CancelPendingFavIconLoadRequests(node);
616 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
617 BookmarkNodeChanged(this, node));
618 }
619 }
620 break;
621 }
622
623 default:
624 NOTREACHED();
625 break;
626 }
627}
[email protected]f25387b2008-08-21 15:20:33628
629void BookmarkBarModel::PopulateNodesByURL(BookmarkBarNode* node) {
630 if (node->is_url())
631 nodes_ordered_by_url_set_.insert(node);
632 for (int i = 0; i < node->GetChildCount(); ++i)
633 PopulateNodesByURL(node->GetChild(i));
634}