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. 4 5 #include "chrome/browser/undo/bookmark_undo_service.h" 6 7 #include "chrome/browser/bookmarks/bookmark_model.h" 8 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 9 #include "chrome/browser/bookmarks/bookmark_node_data.h" 10 #include "chrome/browser/bookmarks/bookmark_utils.h" 11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/undo/bookmark_renumber_observer.h" 13 #include "chrome/browser/undo/bookmark_undo_service_factory.h" 14 #include "chrome/browser/undo/undo_manager_utils.h" 15 #include "chrome/browser/undo/undo_operation.h" 16 17 namespace { 18 19 // BookmarkUndoOperation ------------------------------------------------------ 20 21 // Base class for all bookmark related UndoOperations that facilitates access to 22 // the BookmarkUndoService. 23 class BookmarkUndoOperation : public UndoOperation, 24 public BookmarkRenumberObserver { 25 public: 26 explicit BookmarkUndoOperation(Profile* profile); 27 virtual ~BookmarkUndoOperation() {} 28 29 BookmarkModel* GetBookmarkModel() const; 30 BookmarkRenumberObserver* GetUndoRenumberObserver() const; 31 32 private: 33 Profile* profile_; 34 }; 35 36 BookmarkUndoOperation::BookmarkUndoOperation(Profile* profile) 37 : profile_(profile) { 38 } 39 40 BookmarkModel* BookmarkUndoOperation::GetBookmarkModel() const { 41 return BookmarkModelFactory::GetForProfile(profile_); 42 } 43 44 BookmarkRenumberObserver* BookmarkUndoOperation::GetUndoRenumberObserver() 45 const { 46 return BookmarkUndoServiceFactory::GetForProfile(profile_); 47 } 48 49 // BookmarkAddOperation ------------------------------------------------------- 50 51 // Handles the undo of the insertion of a bookmark or folder. 52 class BookmarkAddOperation : public BookmarkUndoOperation { 53 public: 54 BookmarkAddOperation(Profile* profile, const BookmarkNode* parent, int index); 55 virtual ~BookmarkAddOperation() {} 56 57 // UndoOperation: 58 virtual void Undo() OVERRIDE; 59 60 // BookmarkRenumberObserver: 61 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 62 63 private: 64 int64 parent_id_; 65 const int index_; 66 67 DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation); 68 }; 69 70 BookmarkAddOperation::BookmarkAddOperation(Profile* profile, 71 const BookmarkNode* parent, 72 int index) 73 : BookmarkUndoOperation(profile), 74 parent_id_(parent->id()), 75 index_(index) { 76 } 77 78 void BookmarkAddOperation::Undo() { 79 BookmarkModel* model = GetBookmarkModel(); 80 const BookmarkNode* parent = model->GetNodeByID(parent_id_); 81 DCHECK(parent); 82 83 model->Remove(parent, index_); 84 } 85 86 void BookmarkAddOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 87 if (parent_id_ == old_id) 88 parent_id_ = new_id; 89 } 90 91 // BookmarkRemoveOperation ---------------------------------------------------- 92 93 // Handles the undo of the deletion of a bookmark node. For a bookmark folder, 94 // the information for all descendant bookmark nodes is maintained. 95 // 96 // The BookmarkModel allows only single bookmark node to be removed. 97 class BookmarkRemoveOperation : public BookmarkUndoOperation { 98 public: 99 BookmarkRemoveOperation(Profile* profile, 100 const BookmarkNode* parent, 101 int old_index, 102 const BookmarkNode* node); 103 virtual ~BookmarkRemoveOperation() {} 104 105 // UndoOperation: 106 virtual void Undo() OVERRIDE; 107 108 // BookmarkRenumberObserver: 109 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 110 111 private: 112 void UpdateBookmarkIds(const BookmarkNodeData::Element& element, 113 const BookmarkNode* parent, 114 int index_added_at) const; 115 116 int64 parent_id_; 117 const int old_index_; 118 BookmarkNodeData removed_node_; 119 120 DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation); 121 }; 122 123 BookmarkRemoveOperation::BookmarkRemoveOperation(Profile* profile, 124 const BookmarkNode* parent, 125 int old_index, 126 const BookmarkNode* node) 127 : BookmarkUndoOperation(profile), 128 parent_id_(parent->id()), 129 old_index_(old_index), 130 removed_node_(node) { 131 } 132 133 void BookmarkRemoveOperation::Undo() { 134 DCHECK(removed_node_.is_valid()); 135 BookmarkModel* model = GetBookmarkModel(); 136 const BookmarkNode* parent = model->GetNodeByID(parent_id_); 137 DCHECK(parent); 138 139 bookmark_utils::CloneBookmarkNode(model, removed_node_.elements, parent, 140 old_index_, false); 141 UpdateBookmarkIds(removed_node_.elements[0], parent, old_index_); 142 } 143 144 void BookmarkRemoveOperation::UpdateBookmarkIds( 145 const BookmarkNodeData::Element& element, 146 const BookmarkNode* parent, 147 int index_added_at) const { 148 const BookmarkNode* node = parent->GetChild(index_added_at); 149 if (element.id() != node->id()) 150 GetUndoRenumberObserver()->OnBookmarkRenumbered(element.id(), node->id()); 151 if (!element.is_url) { 152 for (int i = 0; i < static_cast<int>(element.children.size()); ++i) 153 UpdateBookmarkIds(element.children[i], node, 0); 154 } 155 } 156 157 void BookmarkRemoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 158 if (parent_id_ == old_id) 159 parent_id_ = new_id; 160 } 161 162 // BookmarkEditOperation ------------------------------------------------------ 163 164 // Handles the undo of the modification of a bookmark node. 165 class BookmarkEditOperation : public BookmarkUndoOperation { 166 public: 167 BookmarkEditOperation(Profile* profile, 168 const BookmarkNode* node); 169 virtual ~BookmarkEditOperation() {} 170 171 // UndoOperation: 172 virtual void Undo() OVERRIDE; 173 174 // BookmarkRenumberObserver: 175 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 176 177 private: 178 int64 node_id_; 179 BookmarkNodeData original_bookmark_; 180 181 DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation); 182 }; 183 184 BookmarkEditOperation::BookmarkEditOperation(Profile* profile, 185 const BookmarkNode* node) 186 : BookmarkUndoOperation(profile), 187 node_id_(node->id()), 188 original_bookmark_(node) { 189 } 190 191 void BookmarkEditOperation::Undo() { 192 DCHECK(original_bookmark_.is_valid()); 193 BookmarkModel* model = GetBookmarkModel(); 194 const BookmarkNode* node = model->GetNodeByID(node_id_); 195 DCHECK(node); 196 197 model->SetTitle(node, original_bookmark_.elements[0].title); 198 if (original_bookmark_.elements[0].is_url) 199 model->SetURL(node, original_bookmark_.elements[0].url); 200 } 201 202 void BookmarkEditOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 203 if (node_id_ == old_id) 204 node_id_ = new_id; 205 } 206 207 // BookmarkMoveOperation ------------------------------------------------------ 208 209 // Handles the undo of a bookmark being moved to a new location. 210 class BookmarkMoveOperation : public BookmarkUndoOperation { 211 public: 212 BookmarkMoveOperation(Profile* profile, 213 const BookmarkNode* old_parent, 214 int old_index, 215 const BookmarkNode* new_parent, 216 int new_index); 217 virtual ~BookmarkMoveOperation() {} 218 219 // UndoOperation: 220 virtual void Undo() OVERRIDE; 221 222 // BookmarkRenumberObserver: 223 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 224 225 private: 226 int64 old_parent_id_; 227 int64 new_parent_id_; 228 int old_index_; 229 int new_index_; 230 231 DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation); 232 }; 233 234 BookmarkMoveOperation::BookmarkMoveOperation(Profile* profile, 235 const BookmarkNode* old_parent, 236 int old_index, 237 const BookmarkNode* new_parent, 238 int new_index) 239 : BookmarkUndoOperation(profile), 240 old_parent_id_(old_parent->id()), 241 new_parent_id_(new_parent->id()), 242 old_index_(old_index), 243 new_index_(new_index) { 244 } 245 246 void BookmarkMoveOperation::Undo() { 247 BookmarkModel* model = GetBookmarkModel(); 248 const BookmarkNode* old_parent = model->GetNodeByID(old_parent_id_); 249 const BookmarkNode* new_parent = model->GetNodeByID(new_parent_id_); 250 DCHECK(old_parent); 251 DCHECK(new_parent); 252 253 const BookmarkNode* node = new_parent->GetChild(new_index_); 254 int destination_index = old_index_; 255 256 // If the bookmark was moved up within the same parent then the destination 257 // index needs to be incremented since the old index did not account for the 258 // moved bookmark. 259 if (old_parent == new_parent && new_index_ < old_index_) 260 ++destination_index; 261 262 model->Move(node, old_parent, destination_index); 263 } 264 265 void BookmarkMoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 266 if (old_parent_id_ == old_id) 267 old_parent_id_ = new_id; 268 if (new_parent_id_ == old_id) 269 new_parent_id_ = new_id; 270 } 271 272 // BookmarkReorderOperation --------------------------------------------------- 273 274 // Handle the undo of reordering of bookmarks that can happen as a result of 275 // sorting a bookmark folder by name or the undo of that operation. The change 276 // of order is not recursive so only the order of the immediate children of the 277 // folder need to be restored. 278 class BookmarkReorderOperation : public BookmarkUndoOperation { 279 public: 280 BookmarkReorderOperation(Profile* profile, 281 const BookmarkNode* parent); 282 virtual ~BookmarkReorderOperation(); 283 284 // UndoOperation: 285 virtual void Undo() OVERRIDE; 286 287 // BookmarkRenumberObserver: 288 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 289 290 private: 291 int64 parent_id_; 292 std::vector<int64> ordered_bookmarks_; 293 294 DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation); 295 }; 296 297 BookmarkReorderOperation::BookmarkReorderOperation(Profile* profile, 298 const BookmarkNode* parent) 299 : BookmarkUndoOperation(profile), 300 parent_id_(parent->id()) { 301 ordered_bookmarks_.resize(parent->child_count()); 302 for (int i = 0; i < parent->child_count(); ++i) 303 ordered_bookmarks_[i] = parent->GetChild(i)->id(); 304 } 305 306 BookmarkReorderOperation::~BookmarkReorderOperation() { 307 } 308 309 void BookmarkReorderOperation::Undo() { 310 BookmarkModel* model = GetBookmarkModel(); 311 const BookmarkNode* parent = model->GetNodeByID(parent_id_); 312 DCHECK(parent); 313 314 std::vector<const BookmarkNode*> ordered_nodes; 315 for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) 316 ordered_nodes.push_back(model->GetNodeByID(ordered_bookmarks_[i])); 317 318 model->ReorderChildren(parent, ordered_nodes); 319 } 320 321 void BookmarkReorderOperation::OnBookmarkRenumbered(int64 old_id, 322 int64 new_id) { 323 if (parent_id_ == old_id) 324 parent_id_ = new_id; 325 for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) { 326 if (ordered_bookmarks_[i] == old_id) 327 ordered_bookmarks_[i] = new_id; 328 } 329 } 330 331 } // namespace 332 333 // BookmarkUndoService -------------------------------------------------------- 334 335 BookmarkUndoService::BookmarkUndoService(Profile* profile) : profile_(profile) { 336 } 337 338 BookmarkUndoService::~BookmarkUndoService() { 339 BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this); 340 } 341 342 void BookmarkUndoService::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 343 std::vector<UndoOperation*> all_operations = 344 undo_manager()->GetAllUndoOperations(); 345 for (std::vector<UndoOperation*>::iterator it = all_operations.begin(); 346 it != all_operations.end(); ++it) { 347 static_cast<BookmarkUndoOperation*>(*it)->OnBookmarkRenumbered(old_id, 348 new_id); 349 } 350 } 351 352 void BookmarkUndoService::Loaded(BookmarkModel* model, bool ids_reassigned) { 353 undo_manager_.RemoveAllOperations(); 354 } 355 356 void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel* model) { 357 undo_manager_.RemoveAllOperations(); 358 } 359 360 void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel* model, 361 const BookmarkNode* old_parent, 362 int old_index, 363 const BookmarkNode* new_parent, 364 int new_index) { 365 scoped_ptr<UndoOperation> op(new BookmarkMoveOperation(profile_, 366 old_parent, 367 old_index, 368 new_parent, 369 new_index)); 370 undo_manager()->AddUndoOperation(op.Pass()); 371 } 372 373 void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel* model, 374 const BookmarkNode* parent, 375 int index) { 376 scoped_ptr<UndoOperation> op(new BookmarkAddOperation(profile_, 377 parent, 378 index)); 379 undo_manager()->AddUndoOperation(op.Pass()); 380 } 381 382 void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel* model, 383 const BookmarkNode* parent, 384 int old_index, 385 const BookmarkNode* node) { 386 scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(profile_, 387 parent, 388 old_index, 389 node)); 390 undo_manager()->AddUndoOperation(op.Pass()); 391 } 392 393 void BookmarkUndoService::OnWillRemoveAllBookmarks(BookmarkModel* model) { 394 ScopedGroupingAction merge_removes(undo_manager()); 395 for (int i = 0; i < model->root_node()->child_count(); ++i) { 396 const BookmarkNode* permanent_node = model->root_node()->GetChild(i); 397 for (int j = permanent_node->child_count() - 1; j >= 0; --j) { 398 scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(profile_, 399 permanent_node, j, permanent_node->GetChild(j))); 400 undo_manager()->AddUndoOperation(op.Pass()); 401 } 402 } 403 } 404 405 void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel* model, 406 const BookmarkNode* node) { 407 scoped_ptr<UndoOperation> op(new BookmarkEditOperation(profile_, node)); 408 undo_manager()->AddUndoOperation(op.Pass()); 409 } 410 411 void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel* model, 412 const BookmarkNode* node) { 413 scoped_ptr<UndoOperation> op(new BookmarkReorderOperation(profile_, node)); 414 undo_manager()->AddUndoOperation(op.Pass()); 415 } 416