blob: afb42dfe62037e0f5e85e52c61af352d5aa58e8f [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
[email protected]d8e41ed2008-09-11 15:22:325#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:296
7#include "base/gfx/png_decoder.h"
[email protected]9333f182008-12-09 17:34:178#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]68de8b72008-09-09 23:08:139#include "chrome/browser/bookmarks/bookmark_storage.h"
initial.commit09911bf2008-07-26 23:55:2910#include "chrome/browser/profile.h"
[email protected]a4feef82008-10-02 15:11:2211#include "chrome/common/l10n_util.h"
[email protected]f25387b2008-08-21 15:20:3312#include "chrome/common/scoped_vector.h"
13
initial.commit09911bf2008-07-26 23:55:2914#include "generated_resources.h"
15
[email protected]e1acf6f2008-10-27 20:43:3316using base::Time;
17
[email protected]d8e41ed2008-09-11 15:22:3218// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2919
[email protected]f25387b2008-08-21 15:20:3320namespace {
21
[email protected]d8e41ed2008-09-11 15:22:3222// ID for BookmarkNodes.
[email protected]f25387b2008-08-21 15:20:3323// Various places assume an invalid id if == 0, for that reason we start with 1.
24int next_id_ = 1;
25
26}
27
[email protected]d8e41ed2008-09-11 15:22:3228const SkBitmap& BookmarkNode::GetFavIcon() {
initial.commit09911bf2008-07-26 23:55:2929 if (!loaded_favicon_) {
30 loaded_favicon_ = true;
31 model_->LoadFavIcon(this);
32 }
33 return favicon_;
34}
35
[email protected]d8e41ed2008-09-11 15:22:3236BookmarkNode::BookmarkNode(BookmarkModel* model, const GURL& url)
initial.commit09911bf2008-07-26 23:55:2937 : model_(model),
[email protected]f25387b2008-08-21 15:20:3338 id_(next_id_++),
initial.commit09911bf2008-07-26 23:55:2939 loaded_favicon_(false),
40 favicon_load_handle_(0),
[email protected]f25387b2008-08-21 15:20:3341 url_(url),
42 type_(!url.is_empty() ? history::StarredEntry::URL :
43 history::StarredEntry::BOOKMARK_BAR),
initial.commit09911bf2008-07-26 23:55:2944 date_added_(Time::Now()) {
initial.commit09911bf2008-07-26 23:55:2945}
46
[email protected]d8e41ed2008-09-11 15:22:3247void BookmarkNode::Reset(const history::StarredEntry& entry) {
[email protected]f25387b2008-08-21 15:20:3348 DCHECK(entry.type != history::StarredEntry::URL ||
49 entry.url == url_);
50
initial.commit09911bf2008-07-26 23:55:2951 favicon_ = SkBitmap();
initial.commit09911bf2008-07-26 23:55:2952 type_ = entry.type;
53 date_added_ = entry.date_added;
54 date_group_modified_ = entry.date_group_modified;
55 SetTitle(entry.title);
56}
57
[email protected]d8e41ed2008-09-11 15:22:3258// BookmarkModel --------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2959
[email protected]d8e41ed2008-09-11 15:22:3260BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:2961 : profile_(profile),
62 loaded_(false),
63#pragma warning(suppress: 4355) // Okay to pass "this" here.
[email protected]f25387b2008-08-21 15:20:3364 root_(this, GURL()),
initial.commit09911bf2008-07-26 23:55:2965 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:4666 other_node_(NULL),
[email protected]b3e2fad02008-10-31 03:32:0667 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]90ef13132008-08-27 03:27:4668 waiting_for_history_load_(false),
69 loaded_signal_(CreateEvent(NULL, TRUE, FALSE, NULL)) {
[email protected]f25387b2008-08-21 15:20:3370 // Create the bookmark bar and other bookmarks folders. These always exist.
[email protected]d8e41ed2008-09-11 15:22:3271 CreateBookmarkNode();
[email protected]f25387b2008-08-21 15:20:3372 CreateOtherBookmarksNode();
[email protected]4d0cd7ce2008-08-11 16:40:5773
[email protected]f25387b2008-08-21 15:20:3374 // And add them to the root.
75 //
76 // WARNING: order is important here, various places assume bookmark bar then
77 // other node.
78 root_.Add(0, bookmark_bar_node_);
79 root_.Add(1, other_node_);
80
81 if (!profile_) {
82 // Profile is null during testing.
[email protected]90ef13132008-08-27 03:27:4683 DoneLoading();
initial.commit09911bf2008-07-26 23:55:2984 }
initial.commit09911bf2008-07-26 23:55:2985}
86
[email protected]d8e41ed2008-09-11 15:22:3287BookmarkModel::~BookmarkModel() {
[email protected]90ef13132008-08-27 03:27:4688 if (profile_ && store_.get()) {
initial.commit09911bf2008-07-26 23:55:2989 NotificationService::current()->RemoveObserver(
[email protected]f25387b2008-08-21 15:20:3390 this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_));
91 }
92
[email protected]90ef13132008-08-27 03:27:4693 if (waiting_for_history_load_) {
94 NotificationService::current()->RemoveObserver(
95 this, NOTIFY_HISTORY_LOADED, Source<Profile>(profile_));
96 }
97
[email protected]d8e41ed2008-09-11 15:22:3298 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:5199 BookmarkModelBeingDeleted(this));
100
[email protected]f25387b2008-08-21 15:20:33101 if (store_) {
102 // The store maintains a reference back to us. We need to tell it we're gone
103 // so that it doesn't try and invoke a method back on us again.
104 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29105 }
106}
107
[email protected]d8e41ed2008-09-11 15:22:32108void BookmarkModel::Load() {
[email protected]90ef13132008-08-27 03:27:46109 if (store_.get()) {
110 // If the store is non-null, it means Load was already invoked. Load should
111 // only be invoked once.
112 NOTREACHED();
113 return;
114 }
115
[email protected]bb100cb2008-09-16 23:17:01116 LOG(INFO) << "Loading bookmarks";
117
[email protected]90ef13132008-08-27 03:27:46118 // Listen for changes to favicons so that we can update the favicon of the
119 // node appropriately.
120 NotificationService::current()->AddObserver(
121 this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_));
122
123 // Load the bookmarks. BookmarkStorage notifies us when done.
124 store_ = new BookmarkStorage(profile_, this);
125 store_->LoadBookmarks(false);
126}
127
[email protected]d8e41ed2008-09-11 15:22:32128BookmarkNode* BookmarkModel::GetParentForNewNodes() {
[email protected]9333f182008-12-09 17:34:17129 std::vector<BookmarkNode*> nodes =
130 bookmark_utils::GetMostRecentlyModifiedGroups(this, 1);
initial.commit09911bf2008-07-26 23:55:29131 return nodes.empty() ? bookmark_bar_node_ : nodes[0];
132}
133
[email protected]d8e41ed2008-09-11 15:22:32134void BookmarkModel::Remove(BookmarkNode* parent, int index) {
[email protected]f25387b2008-08-21 15:20:33135 if (!loaded_ || !IsValidIndex(parent, index, false) || parent == &root_) {
136 NOTREACHED();
137 return;
138 }
139 RemoveAndDeleteNode(parent->GetChild(index));
140}
141
[email protected]d8e41ed2008-09-11 15:22:32142void BookmarkModel::Move(BookmarkNode* node,
143 BookmarkNode* new_parent,
144 int index) {
[email protected]f25387b2008-08-21 15:20:33145 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
146 new_parent == &root_ || node == &root_ || node == bookmark_bar_node_ ||
initial.commit09911bf2008-07-26 23:55:29147 node == other_node_) {
148 NOTREACHED();
149 return;
150 }
151
152 if (new_parent->HasAncestor(node)) {
153 // Can't make an ancestor of the node be a child of the node.
154 NOTREACHED();
155 return;
156 }
157
158 SetDateGroupModified(new_parent, Time::Now());
159
[email protected]d8e41ed2008-09-11 15:22:32160 BookmarkNode* old_parent = node->GetParent();
initial.commit09911bf2008-07-26 23:55:29161 int old_index = old_parent->IndexOfChild(node);
162
163 if (old_parent == new_parent &&
164 (index == old_index || index == old_index + 1)) {
165 // Node is already in this position, nothing to do.
166 return;
167 }
168
169 if (old_parent == new_parent && index > old_index)
170 index--;
171 new_parent->Add(index, node);
172
[email protected]f25387b2008-08-21 15:20:33173 if (store_.get())
174 store_->ScheduleSave();
175
[email protected]d8e41ed2008-09-11 15:22:32176 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29177 BookmarkNodeMoved(this, old_parent, old_index,
178 new_parent, index));
179}
180
[email protected]d8e41ed2008-09-11 15:22:32181void BookmarkModel::SetTitle(BookmarkNode* node,
182 const std::wstring& title) {
[email protected]f25387b2008-08-21 15:20:33183 if (!node) {
184 NOTREACHED();
185 return;
186 }
initial.commit09911bf2008-07-26 23:55:29187 if (node->GetTitle() == title)
188 return;
[email protected]f25387b2008-08-21 15:20:33189
initial.commit09911bf2008-07-26 23:55:29190 node->SetTitle(title);
[email protected]f25387b2008-08-21 15:20:33191
192 if (store_.get())
193 store_->ScheduleSave();
194
[email protected]d8e41ed2008-09-11 15:22:32195 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29196 BookmarkNodeChanged(this, node));
197}
198
[email protected]848cd05e2008-09-19 18:33:48199void BookmarkModel::GetNodesByURL(const GURL& url,
200 std::vector<BookmarkNode*>* nodes) {
[email protected]90ef13132008-08-27 03:27:46201 AutoLock url_lock(url_lock_);
[email protected]d8e41ed2008-09-11 15:22:32202 BookmarkNode tmp_node(this, url);
initial.commit09911bf2008-07-26 23:55:29203 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]848cd05e2008-09-19 18:33:48204 while (i != nodes_ordered_by_url_set_.end() && (*i)->GetURL() == url) {
205 nodes->push_back(*i);
206 ++i;
207 }
208}
209
210BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(const GURL& url) {
211 std::vector<BookmarkNode*> nodes;
212 GetNodesByURL(url, &nodes);
213 if (nodes.empty())
214 return NULL;
215
[email protected]9333f182008-12-09 17:34:17216 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48217 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29218}
219
[email protected]d8e41ed2008-09-11 15:22:32220void BookmarkModel::GetBookmarks(std::vector<GURL>* urls) {
[email protected]90ef13132008-08-27 03:27:46221 AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48222 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46223 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
224 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]848cd05e2008-09-19 18:33:48225 const GURL* url = &((*i)->url_);
226 // Only add unique URLs.
227 if (!last_url || *url != *last_url)
228 urls->push_back(*url);
229 last_url = url;
[email protected]90ef13132008-08-27 03:27:46230 }
231}
232
[email protected]848cd05e2008-09-19 18:33:48233bool BookmarkModel::IsBookmarked(const GURL& url) {
234 AutoLock url_lock(url_lock_);
[email protected]dddc1b42008-10-09 20:56:59235 return IsBookmarkedNoLock(url);
[email protected]848cd05e2008-09-19 18:33:48236}
237
[email protected]d8e41ed2008-09-11 15:22:32238BookmarkNode* BookmarkModel::GetNodeByID(int id) {
initial.commit09911bf2008-07-26 23:55:29239 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33240 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29241}
242
[email protected]d8e41ed2008-09-11 15:22:32243BookmarkNode* BookmarkModel::AddGroup(
244 BookmarkNode* parent,
initial.commit09911bf2008-07-26 23:55:29245 int index,
246 const std::wstring& title) {
[email protected]f25387b2008-08-21 15:20:33247 if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29248 // Can't add to the root.
249 NOTREACHED();
250 return NULL;
251 }
252
[email protected]d8e41ed2008-09-11 15:22:32253 BookmarkNode* new_node = new BookmarkNode(this, GURL());
[email protected]7ccb5c82008-11-13 23:19:25254 new_node->date_group_modified_ = Time::Now();
initial.commit09911bf2008-07-26 23:55:29255 new_node->SetTitle(title);
256 new_node->type_ = history::StarredEntry::USER_GROUP;
257
[email protected]848cd05e2008-09-19 18:33:48258 return AddNode(parent, index, new_node, false);
initial.commit09911bf2008-07-26 23:55:29259}
260
[email protected]d8e41ed2008-09-11 15:22:32261BookmarkNode* BookmarkModel::AddURL(BookmarkNode* parent,
262 int index,
263 const std::wstring& title,
264 const GURL& url) {
initial.commit09911bf2008-07-26 23:55:29265 return AddURLWithCreationTime(parent, index, title, url, Time::Now());
266}
267
[email protected]d8e41ed2008-09-11 15:22:32268BookmarkNode* BookmarkModel::AddURLWithCreationTime(
269 BookmarkNode* parent,
initial.commit09911bf2008-07-26 23:55:29270 int index,
271 const std::wstring& title,
272 const GURL& url,
273 const Time& creation_time) {
[email protected]f25387b2008-08-21 15:20:33274 if (!loaded_ || !url.is_valid() || parent == &root_ ||
275 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29276 NOTREACHED();
277 return NULL;
278 }
initial.commit09911bf2008-07-26 23:55:29279
[email protected]848cd05e2008-09-19 18:33:48280 bool was_bookmarked = IsBookmarked(url);
initial.commit09911bf2008-07-26 23:55:29281
282 SetDateGroupModified(parent, creation_time);
283
[email protected]d8e41ed2008-09-11 15:22:32284 BookmarkNode* new_node = new BookmarkNode(this, url);
initial.commit09911bf2008-07-26 23:55:29285 new_node->SetTitle(title);
initial.commit09911bf2008-07-26 23:55:29286 new_node->date_added_ = creation_time;
287 new_node->type_ = history::StarredEntry::URL;
288
[email protected]776e7492008-10-23 16:47:41289 {
290 // Only hold the lock for the duration of the insert.
291 AutoLock url_lock(url_lock_);
292 nodes_ordered_by_url_set_.insert(new_node);
293 }
initial.commit09911bf2008-07-26 23:55:29294
[email protected]848cd05e2008-09-19 18:33:48295 return AddNode(parent, index, new_node, was_bookmarked);
initial.commit09911bf2008-07-26 23:55:29296}
297
[email protected]d8e41ed2008-09-11 15:22:32298void BookmarkModel::SetURLStarred(const GURL& url,
299 const std::wstring& title,
300 bool is_starred) {
[email protected]848cd05e2008-09-19 18:33:48301 std::vector<BookmarkNode*> bookmarks;
302 GetNodesByURL(url, &bookmarks);
303 bool bookmarks_exist = !bookmarks.empty();
304 if (is_starred == bookmarks_exist)
305 return; // Nothing to do, state already matches.
306
307 if (is_starred) {
308 // Create a bookmark.
[email protected]d8e41ed2008-09-11 15:22:32309 BookmarkNode* parent = GetParentForNewNodes();
initial.commit09911bf2008-07-26 23:55:29310 AddURL(parent, parent->GetChildCount(), title, url);
[email protected]848cd05e2008-09-19 18:33:48311 } else {
312 // Remove all the bookmarks.
313 for (size_t i = 0; i < bookmarks.size(); ++i) {
314 BookmarkNode* node = bookmarks[i];
315 Remove(node->GetParent(), node->GetParent()->IndexOfChild(node));
316 }
initial.commit09911bf2008-07-26 23:55:29317 }
318}
319
[email protected]d8e41ed2008-09-11 15:22:32320void BookmarkModel::ResetDateGroupModified(BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29321 SetDateGroupModified(node, Time());
322}
323
[email protected]9876bb1c2008-12-16 20:42:25324void BookmarkModel::ClearStore() {
325 if (profile_ && store_.get()) {
326 NotificationService::current()->RemoveObserver(
327 this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_));
328 }
329 store_ = NULL;
330}
331
[email protected]dddc1b42008-10-09 20:56:59332bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
333 BookmarkNode tmp_node(this, url);
334 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
335 nodes_ordered_by_url_set_.end());
336}
337
[email protected]d8e41ed2008-09-11 15:22:32338void BookmarkModel::FavIconLoaded(BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29339 // Send out notification to the observer.
[email protected]d8e41ed2008-09-11 15:22:32340 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29341 BookmarkNodeFavIconLoaded(this, node));
342}
343
[email protected]d8e41ed2008-09-11 15:22:32344void BookmarkModel::RemoveNode(BookmarkNode* node,
345 std::set<GURL>* removed_urls) {
[email protected]f25387b2008-08-21 15:20:33346 if (!loaded_ || !node || node == &root_ || node == bookmark_bar_node_ ||
347 node == other_node_) {
348 NOTREACHED();
349 return;
350 }
351
initial.commit09911bf2008-07-26 23:55:29352 if (node->GetType() == history::StarredEntry::URL) {
[email protected]90ef13132008-08-27 03:27:46353 // NOTE: this is called in such a way that url_lock_ is already held. As
354 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29355 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
356 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48357 // i points to the first node with the URL, advance until we find the
358 // node we're removing.
359 while (*i != node)
360 ++i;
initial.commit09911bf2008-07-26 23:55:29361 nodes_ordered_by_url_set_.erase(i);
[email protected]f25387b2008-08-21 15:20:33362 removed_urls->insert(node->GetURL());
initial.commit09911bf2008-07-26 23:55:29363 }
364
365 CancelPendingFavIconLoadRequests(node);
366
367 // Recurse through children.
368 for (int i = node->GetChildCount() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33369 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29370}
371
[email protected]d8e41ed2008-09-11 15:22:32372void BookmarkModel::OnBookmarkStorageLoadedBookmarks(
[email protected]f25387b2008-08-21 15:20:33373 bool file_exists,
374 bool loaded_from_history) {
initial.commit09911bf2008-07-26 23:55:29375 if (loaded_) {
376 NOTREACHED();
377 return;
378 }
379
[email protected]bb100cb2008-09-16 23:17:01380 LOG(INFO) << "Loaded bookmarks, file_exists=" << file_exists <<
381 " from_history=" << loaded_from_history;
382
[email protected]f25387b2008-08-21 15:20:33383 if (file_exists || loaded_from_history || !profile_ ||
384 !profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) {
[email protected]729065e3c2008-09-02 22:06:44385 // The file exists, we're loaded.
386 DoneLoading();
387
[email protected]f25387b2008-08-21 15:20:33388 if (loaded_from_history) {
389 // We were just populated from the historical file. Schedule a save so
390 // that the main file is up to date.
391 store_->ScheduleSave();
392 }
[email protected]f25387b2008-08-21 15:20:33393 return;
394 }
395
[email protected]90ef13132008-08-27 03:27:46396 // The file doesn't exist. This means one of two things:
397 // 1. A clean profile.
398 // 2. The user is migrating from an older version where bookmarks were saved
399 // in history.
400 // We assume step 2. If history had the bookmarks, history will write the
401 // bookmarks to a file for us. We need to wait until history has finished
402 // loading before reading from that file.
403 HistoryService* history =
404 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
405 if (!history->backend_loaded()) {
406 // The backend isn't finished loading. Wait for it.
[email protected]bb100cb2008-09-16 23:17:01407 LOG(INFO) << " waiting for history to finish";
408
[email protected]90ef13132008-08-27 03:27:46409 waiting_for_history_load_ = true;
410 NotificationService::current()->AddObserver(
411 this, NOTIFY_HISTORY_LOADED, Source<Profile>(profile_));
412 } else {
413 OnHistoryDone();
414 }
[email protected]f25387b2008-08-21 15:20:33415}
416
[email protected]d8e41ed2008-09-11 15:22:32417void BookmarkModel::OnHistoryDone() {
[email protected]f25387b2008-08-21 15:20:33418 if (loaded_) {
419 NOTREACHED();
420 return;
421 }
422
[email protected]bb100cb2008-09-16 23:17:01423 LOG(INFO) << " history done, reloading";
424
[email protected]f25387b2008-08-21 15:20:33425 // If the bookmarks were stored in the db the db will have migrated them to
426 // a file now. Try loading from the file.
427 store_->LoadBookmarks(true);
428}
429
[email protected]d8e41ed2008-09-11 15:22:32430void BookmarkModel::DoneLoading() {
[email protected]90ef13132008-08-27 03:27:46431 {
432 AutoLock url_lock(url_lock_);
433 // Update nodes_ordered_by_url_set_ from the nodes.
434 PopulateNodesByURL(&root_);
435 }
[email protected]f25387b2008-08-21 15:20:33436
initial.commit09911bf2008-07-26 23:55:29437 loaded_ = true;
438
[email protected]90ef13132008-08-27 03:27:46439 if (loaded_signal_.Get())
440 SetEvent(loaded_signal_.Get());
441
442
[email protected]f25387b2008-08-21 15:20:33443 // Notify our direct observers.
[email protected]d8e41ed2008-09-11 15:22:32444 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, Loaded(this));
initial.commit09911bf2008-07-26 23:55:29445
[email protected]f25387b2008-08-21 15:20:33446 // And generic notification.
initial.commit09911bf2008-07-26 23:55:29447 NotificationService::current()->Notify(
448 NOTIFY_BOOKMARK_MODEL_LOADED,
449 Source<Profile>(profile_),
450 NotificationService::NoDetails());
451}
452
[email protected]d8e41ed2008-09-11 15:22:32453void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
454 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29455
[email protected]d8e41ed2008-09-11 15:22:32456 BookmarkNode* parent = node->GetParent();
initial.commit09911bf2008-07-26 23:55:29457 DCHECK(parent);
458 int index = parent->IndexOfChild(node.get());
459 parent->Remove(index);
[email protected]f25387b2008-08-21 15:20:33460 history::URLsStarredDetails details(false);
[email protected]90ef13132008-08-27 03:27:46461 {
462 AutoLock url_lock(url_lock_);
463 RemoveNode(node.get(), &details.changed_urls);
[email protected]848cd05e2008-09-19 18:33:48464
465 // RemoveNode adds an entry to changed_urls for each node of type URL. As we
466 // allow duplicates we need to remove any entries that are still bookmarked.
467 for (std::set<GURL>::iterator i = details.changed_urls.begin();
468 i != details.changed_urls.end(); ){
[email protected]dddc1b42008-10-09 20:56:59469 if (IsBookmarkedNoLock(*i))
[email protected]848cd05e2008-09-19 18:33:48470 i = details.changed_urls.erase(i);
471 else
472 ++i;
473 }
[email protected]90ef13132008-08-27 03:27:46474 }
initial.commit09911bf2008-07-26 23:55:29475
[email protected]f25387b2008-08-21 15:20:33476 if (store_.get())
477 store_->ScheduleSave();
478
[email protected]d8e41ed2008-09-11 15:22:32479 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41480 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]f25387b2008-08-21 15:20:33481
[email protected]848cd05e2008-09-19 18:33:48482 if (details.changed_urls.empty()) {
483 // No point in sending out notification if the starred state didn't change.
484 return;
485 }
486
[email protected]90ef13132008-08-27 03:27:46487 if (profile_) {
488 HistoryService* history =
489 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
490 if (history)
491 history->URLsNoLongerBookmarked(details.changed_urls);
492 }
493
[email protected]f25387b2008-08-21 15:20:33494 NotificationService::current()->Notify(NOTIFY_URLS_STARRED,
495 Source<Profile>(profile_),
496 Details<history::URLsStarredDetails>(&details));
initial.commit09911bf2008-07-26 23:55:29497}
498
[email protected]d8e41ed2008-09-11 15:22:32499BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
500 int index,
[email protected]848cd05e2008-09-19 18:33:48501 BookmarkNode* node,
502 bool was_bookmarked) {
initial.commit09911bf2008-07-26 23:55:29503 parent->Add(index, node);
504
[email protected]f25387b2008-08-21 15:20:33505 if (store_.get())
506 store_->ScheduleSave();
507
[email protected]d8e41ed2008-09-11 15:22:32508 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29509 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33510
[email protected]848cd05e2008-09-19 18:33:48511 if (node->GetType() == history::StarredEntry::URL && !was_bookmarked) {
[email protected]f25387b2008-08-21 15:20:33512 history::URLsStarredDetails details(true);
513 details.changed_urls.insert(node->GetURL());
514 NotificationService::current()->Notify(NOTIFY_URLS_STARRED,
515 Source<Profile>(profile_),
516 Details<history::URLsStarredDetails>(&details));
517 }
initial.commit09911bf2008-07-26 23:55:29518 return node;
519}
520
[email protected]d8e41ed2008-09-11 15:22:32521void BookmarkModel::BlockTillLoaded() {
[email protected]90ef13132008-08-27 03:27:46522 if (loaded_signal_.Get())
523 WaitForSingleObject(loaded_signal_.Get(), INFINITE);
524}
525
[email protected]d8e41ed2008-09-11 15:22:32526BookmarkNode* BookmarkModel::GetNodeByID(BookmarkNode* node, int id) {
[email protected]f25387b2008-08-21 15:20:33527 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29528 return node;
[email protected]f25387b2008-08-21 15:20:33529
initial.commit09911bf2008-07-26 23:55:29530 for (int i = 0; i < node->GetChildCount(); ++i) {
[email protected]d8e41ed2008-09-11 15:22:32531 BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29532 if (result)
533 return result;
534 }
535 return NULL;
536}
537
[email protected]d8e41ed2008-09-11 15:22:32538bool BookmarkModel::IsValidIndex(BookmarkNode* parent,
539 int index,
540 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41541 return (parent && parent->is_folder() &&
[email protected]f25387b2008-08-21 15:20:33542 (index >= 0 && (index < parent->GetChildCount() ||
543 (allow_end && index == parent->GetChildCount()))));
544 }
545
[email protected]d8e41ed2008-09-11 15:22:32546void BookmarkModel::SetDateGroupModified(BookmarkNode* parent,
547 const Time time) {
initial.commit09911bf2008-07-26 23:55:29548 DCHECK(parent);
549 parent->date_group_modified_ = time;
[email protected]f25387b2008-08-21 15:20:33550
551 if (store_.get())
552 store_->ScheduleSave();
initial.commit09911bf2008-07-26 23:55:29553}
554
[email protected]d8e41ed2008-09-11 15:22:32555void BookmarkModel::CreateBookmarkNode() {
initial.commit09911bf2008-07-26 23:55:29556 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29557 entry.type = history::StarredEntry::BOOKMARK_BAR;
558 bookmark_bar_node_ = CreateRootNodeFromStarredEntry(entry);
559}
560
[email protected]d8e41ed2008-09-11 15:22:32561void BookmarkModel::CreateOtherBookmarksNode() {
initial.commit09911bf2008-07-26 23:55:29562 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29563 entry.type = history::StarredEntry::OTHER;
564 other_node_ = CreateRootNodeFromStarredEntry(entry);
565}
566
[email protected]d8e41ed2008-09-11 15:22:32567BookmarkNode* BookmarkModel::CreateRootNodeFromStarredEntry(
initial.commit09911bf2008-07-26 23:55:29568 const history::StarredEntry& entry) {
569 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
570 entry.type == history::StarredEntry::OTHER);
[email protected]d8e41ed2008-09-11 15:22:32571 BookmarkNode* node = new BookmarkNode(this, GURL());
initial.commit09911bf2008-07-26 23:55:29572 node->Reset(entry);
573 if (entry.type == history::StarredEntry::BOOKMARK_BAR)
574 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME));
575 else
576 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
initial.commit09911bf2008-07-26 23:55:29577 return node;
578}
579
[email protected]d8e41ed2008-09-11 15:22:32580void BookmarkModel::OnFavIconDataAvailable(
initial.commit09911bf2008-07-26 23:55:29581 HistoryService::Handle handle,
582 bool know_favicon,
583 scoped_refptr<RefCountedBytes> data,
584 bool expired,
585 GURL icon_url) {
586 SkBitmap fav_icon;
[email protected]d8e41ed2008-09-11 15:22:32587 BookmarkNode* node =
[email protected]4167c3a2008-08-21 18:12:20588 load_consumer_.GetClientData(
589 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
590 DCHECK(node);
591 node->favicon_load_handle_ = 0;
initial.commit09911bf2008-07-26 23:55:29592 if (know_favicon && data.get() &&
593 PNGDecoder::Decode(&data->data, &fav_icon)) {
initial.commit09911bf2008-07-26 23:55:29594 node->favicon_ = fav_icon;
initial.commit09911bf2008-07-26 23:55:29595 FavIconLoaded(node);
596 }
597}
598
[email protected]d8e41ed2008-09-11 15:22:32599void BookmarkModel::LoadFavIcon(BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29600 if (node->GetType() != history::StarredEntry::URL)
601 return;
602
603 DCHECK(node->GetURL().is_valid());
604 HistoryService* history_service =
605 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
606 if (!history_service)
607 return;
608
609 HistoryService::Handle handle = history_service->GetFavIconForURL(
610 node->GetURL(), &load_consumer_,
[email protected]d8e41ed2008-09-11 15:22:32611 NewCallback(this, &BookmarkModel::OnFavIconDataAvailable));
initial.commit09911bf2008-07-26 23:55:29612 load_consumer_.SetClientData(history_service, handle, node);
[email protected]f25387b2008-08-21 15:20:33613 node->favicon_load_handle_ = handle;
initial.commit09911bf2008-07-26 23:55:29614}
615
[email protected]d8e41ed2008-09-11 15:22:32616void BookmarkModel::CancelPendingFavIconLoadRequests(BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29617 if (node->favicon_load_handle_) {
618 HistoryService* history =
619 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
620 if (history)
621 history->CancelRequest(node->favicon_load_handle_);
622 node->favicon_load_handle_ = 0;
623 }
624}
625
[email protected]d8e41ed2008-09-11 15:22:32626void BookmarkModel::Observe(NotificationType type,
627 const NotificationSource& source,
628 const NotificationDetails& details) {
initial.commit09911bf2008-07-26 23:55:29629 switch (type) {
[email protected]f25387b2008-08-21 15:20:33630 case NOTIFY_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29631 // Prevent the observers from getting confused for multiple favicon loads.
632 Details<history::FavIconChangeDetails> favicon_details(details);
633 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
634 i != favicon_details->urls.end(); ++i) {
[email protected]848cd05e2008-09-19 18:33:48635 std::vector<BookmarkNode*> nodes;
636 GetNodesByURL(*i, &nodes);
637 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29638 // Got an updated favicon, for a URL, do a new request.
[email protected]848cd05e2008-09-19 18:33:48639 BookmarkNode* node = nodes[i];
initial.commit09911bf2008-07-26 23:55:29640 node->InvalidateFavicon();
641 CancelPendingFavIconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32642 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29643 BookmarkNodeChanged(this, node));
644 }
645 }
646 break;
647 }
648
[email protected]90ef13132008-08-27 03:27:46649 case NOTIFY_HISTORY_LOADED: {
650 if (waiting_for_history_load_) {
651 waiting_for_history_load_ = false;
652 NotificationService::current()->RemoveObserver(
653 this, NOTIFY_HISTORY_LOADED, Source<Profile>(profile_));
654 OnHistoryDone();
655 } else {
656 NOTREACHED();
657 }
658 break;
659 }
660
initial.commit09911bf2008-07-26 23:55:29661 default:
662 NOTREACHED();
663 break;
664 }
665}
[email protected]f25387b2008-08-21 15:20:33666
[email protected]d8e41ed2008-09-11 15:22:32667void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46668 // NOTE: this is called with url_lock_ already held. As such, this doesn't
669 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33670 if (node->is_url())
671 nodes_ordered_by_url_set_.insert(node);
672 for (int i = 0; i < node->GetChildCount(); ++i)
673 PopulateNodesByURL(node->GetChild(i));
674}