1 // Copyright 2013 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.
5 #include "chrome/browser/undo/bookmark_undo_service.h"
7 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/undo/bookmark_renumber_observer.h"
10 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
11 #include "chrome/browser/undo/undo_operation.h"
12 #include "chrome/grit/generated_resources.h"
13 #include "components/bookmarks/browser/bookmark_model.h"
14 #include "components/bookmarks/browser/bookmark_node_data.h"
15 #include "components/bookmarks/browser/bookmark_utils.h"
16 #include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
18 using bookmarks::BookmarkModel
;
19 using bookmarks::BookmarkNode
;
20 using bookmarks::BookmarkNodeData
;
24 // BookmarkUndoOperation ------------------------------------------------------
26 // Base class for all bookmark related UndoOperations that facilitates access to
27 // the BookmarkUndoService.
28 class BookmarkUndoOperation
: public UndoOperation
,
29 public BookmarkRenumberObserver
{
31 explicit BookmarkUndoOperation(Profile
* profile
);
32 ~BookmarkUndoOperation() override
{}
34 BookmarkModel
* GetBookmarkModel() const;
35 BookmarkRenumberObserver
* GetUndoRenumberObserver() const;
41 BookmarkUndoOperation::BookmarkUndoOperation(Profile
* profile
)
45 BookmarkModel
* BookmarkUndoOperation::GetBookmarkModel() const {
46 return BookmarkModelFactory::GetForProfile(profile_
);
49 BookmarkRenumberObserver
* BookmarkUndoOperation::GetUndoRenumberObserver()
51 return BookmarkUndoServiceFactory::GetForProfile(profile_
);
54 // BookmarkAddOperation -------------------------------------------------------
56 // Handles the undo of the insertion of a bookmark or folder.
57 class BookmarkAddOperation
: public BookmarkUndoOperation
{
59 BookmarkAddOperation(Profile
* profile
, const BookmarkNode
* parent
, int index
);
60 ~BookmarkAddOperation() override
{}
64 int GetUndoLabelId() const override
;
65 int GetRedoLabelId() const override
;
67 // BookmarkRenumberObserver:
68 void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) override
;
74 DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation
);
77 BookmarkAddOperation::BookmarkAddOperation(Profile
* profile
,
78 const BookmarkNode
* parent
,
80 : BookmarkUndoOperation(profile
),
81 parent_id_(parent
->id()),
85 void BookmarkAddOperation::Undo() {
86 BookmarkModel
* model
= GetBookmarkModel();
87 const BookmarkNode
* parent
=
88 bookmarks::GetBookmarkNodeByID(model
, parent_id_
);
91 model
->Remove(parent
, index_
);
94 int BookmarkAddOperation::GetUndoLabelId() const {
95 return IDS_BOOKMARK_BAR_UNDO_ADD
;
98 int BookmarkAddOperation::GetRedoLabelId() const {
99 return IDS_BOOKMARK_BAR_REDO_DELETE
;
102 void BookmarkAddOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
103 if (parent_id_
== old_id
)
107 // BookmarkRemoveOperation ----------------------------------------------------
109 // Handles the undo of the deletion of a bookmark node. For a bookmark folder,
110 // the information for all descendant bookmark nodes is maintained.
112 // The BookmarkModel allows only single bookmark node to be removed.
113 class BookmarkRemoveOperation
: public BookmarkUndoOperation
{
115 BookmarkRemoveOperation(Profile
* profile
,
116 const BookmarkNode
* parent
,
118 const BookmarkNode
* node
);
119 ~BookmarkRemoveOperation() override
{}
122 void Undo() override
;
123 int GetUndoLabelId() const override
;
124 int GetRedoLabelId() const override
;
126 // BookmarkRenumberObserver:
127 void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) override
;
130 void UpdateBookmarkIds(const BookmarkNodeData::Element
& element
,
131 const BookmarkNode
* parent
,
132 int index_added_at
) const;
135 const int old_index_
;
136 BookmarkNodeData removed_node_
;
138 DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation
);
141 BookmarkRemoveOperation::BookmarkRemoveOperation(Profile
* profile
,
142 const BookmarkNode
* parent
,
144 const BookmarkNode
* node
)
145 : BookmarkUndoOperation(profile
),
146 parent_id_(parent
->id()),
147 old_index_(old_index
),
148 removed_node_(node
) {
151 void BookmarkRemoveOperation::Undo() {
152 DCHECK(removed_node_
.is_valid());
153 BookmarkModel
* model
= GetBookmarkModel();
154 const BookmarkNode
* parent
=
155 bookmarks::GetBookmarkNodeByID(model
, parent_id_
);
158 bookmarks::CloneBookmarkNode(
159 model
, removed_node_
.elements
, parent
, old_index_
, false);
160 UpdateBookmarkIds(removed_node_
.elements
[0], parent
, old_index_
);
163 int BookmarkRemoveOperation::GetUndoLabelId() const {
164 return IDS_BOOKMARK_BAR_UNDO_DELETE
;
167 int BookmarkRemoveOperation::GetRedoLabelId() const {
168 return IDS_BOOKMARK_BAR_REDO_ADD
;
171 void BookmarkRemoveOperation::UpdateBookmarkIds(
172 const BookmarkNodeData::Element
& element
,
173 const BookmarkNode
* parent
,
174 int index_added_at
) const {
175 const BookmarkNode
* node
= parent
->GetChild(index_added_at
);
176 if (element
.id() != node
->id())
177 GetUndoRenumberObserver()->OnBookmarkRenumbered(element
.id(), node
->id());
178 if (!element
.is_url
) {
179 for (int i
= 0; i
< static_cast<int>(element
.children
.size()); ++i
)
180 UpdateBookmarkIds(element
.children
[i
], node
, 0);
184 void BookmarkRemoveOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
185 if (parent_id_
== old_id
)
189 // BookmarkEditOperation ------------------------------------------------------
191 // Handles the undo of the modification of a bookmark node.
192 class BookmarkEditOperation
: public BookmarkUndoOperation
{
194 BookmarkEditOperation(Profile
* profile
,
195 const BookmarkNode
* node
);
196 ~BookmarkEditOperation() override
{}
199 void Undo() override
;
200 int GetUndoLabelId() const override
;
201 int GetRedoLabelId() const override
;
203 // BookmarkRenumberObserver:
204 void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) override
;
208 BookmarkNodeData original_bookmark_
;
210 DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation
);
213 BookmarkEditOperation::BookmarkEditOperation(Profile
* profile
,
214 const BookmarkNode
* node
)
215 : BookmarkUndoOperation(profile
),
216 node_id_(node
->id()),
217 original_bookmark_(node
) {
220 void BookmarkEditOperation::Undo() {
221 DCHECK(original_bookmark_
.is_valid());
222 BookmarkModel
* model
= GetBookmarkModel();
223 const BookmarkNode
* node
= bookmarks::GetBookmarkNodeByID(model
, node_id_
);
226 model
->SetTitle(node
, original_bookmark_
.elements
[0].title
);
227 if (original_bookmark_
.elements
[0].is_url
)
228 model
->SetURL(node
, original_bookmark_
.elements
[0].url
);
231 int BookmarkEditOperation::GetUndoLabelId() const {
232 return IDS_BOOKMARK_BAR_UNDO_EDIT
;
235 int BookmarkEditOperation::GetRedoLabelId() const {
236 return IDS_BOOKMARK_BAR_REDO_EDIT
;
239 void BookmarkEditOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
240 if (node_id_
== old_id
)
244 // BookmarkMoveOperation ------------------------------------------------------
246 // Handles the undo of a bookmark being moved to a new location.
247 class BookmarkMoveOperation
: public BookmarkUndoOperation
{
249 BookmarkMoveOperation(Profile
* profile
,
250 const BookmarkNode
* old_parent
,
252 const BookmarkNode
* new_parent
,
254 ~BookmarkMoveOperation() override
{}
255 int GetUndoLabelId() const override
;
256 int GetRedoLabelId() const override
;
259 void Undo() override
;
261 // BookmarkRenumberObserver:
262 void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) override
;
265 int64 old_parent_id_
;
266 int64 new_parent_id_
;
270 DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation
);
273 BookmarkMoveOperation::BookmarkMoveOperation(Profile
* profile
,
274 const BookmarkNode
* old_parent
,
276 const BookmarkNode
* new_parent
,
278 : BookmarkUndoOperation(profile
),
279 old_parent_id_(old_parent
->id()),
280 new_parent_id_(new_parent
->id()),
281 old_index_(old_index
),
282 new_index_(new_index
) {
285 void BookmarkMoveOperation::Undo() {
286 BookmarkModel
* model
= GetBookmarkModel();
287 const BookmarkNode
* old_parent
=
288 bookmarks::GetBookmarkNodeByID(model
, old_parent_id_
);
289 const BookmarkNode
* new_parent
=
290 bookmarks::GetBookmarkNodeByID(model
, new_parent_id_
);
294 const BookmarkNode
* node
= new_parent
->GetChild(new_index_
);
295 int destination_index
= old_index_
;
297 // If the bookmark was moved up within the same parent then the destination
298 // index needs to be incremented since the old index did not account for the
300 if (old_parent
== new_parent
&& new_index_
< old_index_
)
303 model
->Move(node
, old_parent
, destination_index
);
306 int BookmarkMoveOperation::GetUndoLabelId() const {
307 return IDS_BOOKMARK_BAR_UNDO_MOVE
;
310 int BookmarkMoveOperation::GetRedoLabelId() const {
311 return IDS_BOOKMARK_BAR_REDO_MOVE
;
314 void BookmarkMoveOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
315 if (old_parent_id_
== old_id
)
316 old_parent_id_
= new_id
;
317 if (new_parent_id_
== old_id
)
318 new_parent_id_
= new_id
;
321 // BookmarkReorderOperation ---------------------------------------------------
323 // Handle the undo of reordering of bookmarks that can happen as a result of
324 // sorting a bookmark folder by name or the undo of that operation. The change
325 // of order is not recursive so only the order of the immediate children of the
326 // folder need to be restored.
327 class BookmarkReorderOperation
: public BookmarkUndoOperation
{
329 BookmarkReorderOperation(Profile
* profile
,
330 const BookmarkNode
* parent
);
331 ~BookmarkReorderOperation() override
;
334 void Undo() override
;
335 int GetUndoLabelId() const override
;
336 int GetRedoLabelId() const override
;
338 // BookmarkRenumberObserver:
339 void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) override
;
343 std::vector
<int64
> ordered_bookmarks_
;
345 DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation
);
348 BookmarkReorderOperation::BookmarkReorderOperation(Profile
* profile
,
349 const BookmarkNode
* parent
)
350 : BookmarkUndoOperation(profile
),
351 parent_id_(parent
->id()) {
352 ordered_bookmarks_
.resize(parent
->child_count());
353 for (int i
= 0; i
< parent
->child_count(); ++i
)
354 ordered_bookmarks_
[i
] = parent
->GetChild(i
)->id();
357 BookmarkReorderOperation::~BookmarkReorderOperation() {
360 void BookmarkReorderOperation::Undo() {
361 BookmarkModel
* model
= GetBookmarkModel();
362 const BookmarkNode
* parent
=
363 bookmarks::GetBookmarkNodeByID(model
, parent_id_
);
366 std::vector
<const BookmarkNode
*> ordered_nodes
;
367 for (size_t i
= 0; i
< ordered_bookmarks_
.size(); ++i
) {
368 ordered_nodes
.push_back(
369 bookmarks::GetBookmarkNodeByID(model
, ordered_bookmarks_
[i
]));
372 model
->ReorderChildren(parent
, ordered_nodes
);
375 int BookmarkReorderOperation::GetUndoLabelId() const {
376 return IDS_BOOKMARK_BAR_UNDO_REORDER
;
379 int BookmarkReorderOperation::GetRedoLabelId() const {
380 return IDS_BOOKMARK_BAR_REDO_REORDER
;
383 void BookmarkReorderOperation::OnBookmarkRenumbered(int64 old_id
,
385 if (parent_id_
== old_id
)
387 for (size_t i
= 0; i
< ordered_bookmarks_
.size(); ++i
) {
388 if (ordered_bookmarks_
[i
] == old_id
)
389 ordered_bookmarks_
[i
] = new_id
;
395 // BookmarkUndoService --------------------------------------------------------
397 BookmarkUndoService::BookmarkUndoService(Profile
* profile
) : profile_(profile
) {
400 BookmarkUndoService::~BookmarkUndoService() {
401 BookmarkModelFactory::GetForProfile(profile_
)->RemoveObserver(this);
404 void BookmarkUndoService::BookmarkModelLoaded(BookmarkModel
* model
,
405 bool ids_reassigned
) {
406 undo_manager_
.RemoveAllOperations();
409 void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel
* model
) {
410 undo_manager_
.RemoveAllOperations();
413 void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel
* model
,
414 const BookmarkNode
* old_parent
,
416 const BookmarkNode
* new_parent
,
418 scoped_ptr
<UndoOperation
> op(new BookmarkMoveOperation(profile_
,
423 undo_manager()->AddUndoOperation(op
.Pass());
426 void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel
* model
,
427 const BookmarkNode
* parent
,
429 scoped_ptr
<UndoOperation
> op(new BookmarkAddOperation(profile_
,
432 undo_manager()->AddUndoOperation(op
.Pass());
435 void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel
* model
,
436 const BookmarkNode
* parent
,
438 const BookmarkNode
* node
) {
439 scoped_ptr
<UndoOperation
> op(new BookmarkRemoveOperation(profile_
,
443 undo_manager()->AddUndoOperation(op
.Pass());
446 void BookmarkUndoService::OnWillRemoveAllUserBookmarks(BookmarkModel
* model
) {
447 bookmarks::ScopedGroupBookmarkActions
merge_removes(model
);
448 for (int i
= 0; i
< model
->root_node()->child_count(); ++i
) {
449 const BookmarkNode
* permanent_node
= model
->root_node()->GetChild(i
);
450 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
451 scoped_ptr
<UndoOperation
> op(new BookmarkRemoveOperation(profile_
,
452 permanent_node
, j
, permanent_node
->GetChild(j
)));
453 undo_manager()->AddUndoOperation(op
.Pass());
458 void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel
* model
,
459 const BookmarkNode
* node
) {
460 scoped_ptr
<UndoOperation
> op(new BookmarkEditOperation(profile_
, node
));
461 undo_manager()->AddUndoOperation(op
.Pass());
464 void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel
* model
,
465 const BookmarkNode
* node
) {
466 scoped_ptr
<UndoOperation
> op(new BookmarkReorderOperation(profile_
, node
));
467 undo_manager()->AddUndoOperation(op
.Pass());
470 void BookmarkUndoService::GroupedBookmarkChangesBeginning(
471 BookmarkModel
* model
) {
472 undo_manager()->StartGroupingActions();
475 void BookmarkUndoService::GroupedBookmarkChangesEnded(BookmarkModel
* model
) {
476 undo_manager()->EndGroupingActions();
479 void BookmarkUndoService::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
480 std::vector
<UndoOperation
*> all_operations
=
481 undo_manager()->GetAllUndoOperations();
482 for (std::vector
<UndoOperation
*>::iterator it
= all_operations
.begin();
483 it
!= all_operations
.end(); ++it
) {
484 static_cast<BookmarkUndoOperation
*>(*it
)->OnBookmarkRenumbered(old_id
,