1 // Copyright 2014 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 "components/bookmarks/browser/bookmark_storage.h" 6 7 #include "base/bind.h" 8 #include "base/compiler_specific.h" 9 #include "base/files/file_util.h" 10 #include "base/json/json_file_value_serializer.h" 11 #include "base/json/json_string_value_serializer.h" 12 #include "base/metrics/histogram.h" 13 #include "base/sequenced_task_runner.h" 14 #include "base/time/time.h" 15 #include "components/bookmarks/browser/bookmark_codec.h" 16 #include "components/bookmarks/browser/bookmark_index.h" 17 #include "components/bookmarks/browser/bookmark_model.h" 18 #include "components/bookmarks/common/bookmark_constants.h" 19 #include "components/startup_metric_utils/startup_metric_utils.h" 20 21 using base::TimeTicks; 22 23 namespace bookmarks { 24 25 namespace { 26 27 // Extension used for backup files (copy of main file created during startup). 28 const base::FilePath::CharType kBackupExtension[] = FILE_PATH_LITERAL("bak"); 29 30 // How often we save. 31 const int kSaveDelayMS = 2500; 32 33 void BackupCallback(const base::FilePath& path) { 34 base::FilePath backup_path = path.ReplaceExtension(kBackupExtension); 35 base::CopyFile(path, backup_path); 36 } 37 38 // Adds node to the model's index, recursing through all children as well. 39 void AddBookmarksToIndex(BookmarkLoadDetails* details, 40 BookmarkNode* node) { 41 if (node->is_url()) { 42 if (node->url().is_valid()) 43 details->index()->Add(node); 44 } else { 45 for (int i = 0; i < node->child_count(); ++i) 46 AddBookmarksToIndex(details, node->GetChild(i)); 47 } 48 } 49 50 void LoadCallback(const base::FilePath& path, 51 const base::WeakPtr<BookmarkStorage>& storage, 52 scoped_ptr<BookmarkLoadDetails> details, 53 base::SequencedTaskRunner* task_runner) { 54 startup_metric_utils::ScopedSlowStartupUMA 55 scoped_timer("Startup.SlowStartupBookmarksLoad"); 56 bool load_index = false; 57 bool bookmark_file_exists = base::PathExists(path); 58 if (bookmark_file_exists) { 59 JSONFileValueSerializer serializer(path); 60 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, NULL)); 61 62 if (root.get()) { 63 // Building the index can take a while, so we do it on the background 64 // thread. 65 int64 max_node_id = 0; 66 BookmarkCodec codec; 67 TimeTicks start_time = TimeTicks::Now(); 68 codec.Decode(details->bb_node(), details->other_folder_node(), 69 details->mobile_folder_node(), &max_node_id, *root.get()); 70 details->set_max_id(std::max(max_node_id, details->max_id())); 71 details->set_computed_checksum(codec.computed_checksum()); 72 details->set_stored_checksum(codec.stored_checksum()); 73 details->set_ids_reassigned(codec.ids_reassigned()); 74 details->set_model_meta_info_map(codec.model_meta_info_map()); 75 details->set_model_sync_transaction_version( 76 codec.model_sync_transaction_version()); 77 UMA_HISTOGRAM_TIMES("Bookmarks.DecodeTime", 78 TimeTicks::Now() - start_time); 79 80 load_index = true; 81 } 82 } 83 84 // Load any extra root nodes now, after the IDs have been potentially 85 // reassigned. 86 details->LoadExtraNodes(); 87 88 // Load the index if there are any bookmarks in the extra nodes. 89 const BookmarkPermanentNodeList& extra_nodes = details->extra_nodes(); 90 for (size_t i = 0; i < extra_nodes.size(); ++i) { 91 if (!extra_nodes[i]->empty()) { 92 load_index = true; 93 break; 94 } 95 } 96 97 if (load_index) { 98 TimeTicks start_time = TimeTicks::Now(); 99 AddBookmarksToIndex(details.get(), details->bb_node()); 100 AddBookmarksToIndex(details.get(), details->other_folder_node()); 101 AddBookmarksToIndex(details.get(), details->mobile_folder_node()); 102 for (size_t i = 0; i < extra_nodes.size(); ++i) 103 AddBookmarksToIndex(details.get(), extra_nodes[i]); 104 UMA_HISTOGRAM_TIMES("Bookmarks.CreateBookmarkIndexTime", 105 TimeTicks::Now() - start_time); 106 } 107 108 task_runner->PostTask(FROM_HERE, 109 base::Bind(&BookmarkStorage::OnLoadFinished, storage, 110 base::Passed(&details))); 111 } 112 113 } // namespace 114 115 // BookmarkLoadDetails --------------------------------------------------------- 116 117 BookmarkLoadDetails::BookmarkLoadDetails( 118 BookmarkPermanentNode* bb_node, 119 BookmarkPermanentNode* other_folder_node, 120 BookmarkPermanentNode* mobile_folder_node, 121 const LoadExtraCallback& load_extra_callback, 122 BookmarkIndex* index, 123 int64 max_id) 124 : bb_node_(bb_node), 125 other_folder_node_(other_folder_node), 126 mobile_folder_node_(mobile_folder_node), 127 load_extra_callback_(load_extra_callback), 128 index_(index), 129 model_sync_transaction_version_( 130 BookmarkNode::kInvalidSyncTransactionVersion), 131 max_id_(max_id), 132 ids_reassigned_(false) { 133 } 134 135 BookmarkLoadDetails::~BookmarkLoadDetails() { 136 } 137 138 void BookmarkLoadDetails::LoadExtraNodes() { 139 extra_nodes_ = load_extra_callback_.Run(&max_id_); 140 } 141 142 // BookmarkStorage ------------------------------------------------------------- 143 144 BookmarkStorage::BookmarkStorage( 145 BookmarkModel* model, 146 const base::FilePath& profile_path, 147 base::SequencedTaskRunner* sequenced_task_runner) 148 : model_(model), 149 writer_(profile_path.Append(kBookmarksFileName), sequenced_task_runner), 150 weak_factory_(this) { 151 sequenced_task_runner_ = sequenced_task_runner; 152 writer_.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS)); 153 sequenced_task_runner_->PostTask(FROM_HERE, 154 base::Bind(&BackupCallback, writer_.path())); 155 } 156 157 BookmarkStorage::~BookmarkStorage() { 158 if (writer_.HasPendingWrite()) 159 writer_.DoScheduledWrite(); 160 } 161 162 void BookmarkStorage::LoadBookmarks( 163 scoped_ptr<BookmarkLoadDetails> details, 164 const scoped_refptr<base::SequencedTaskRunner>& task_runner) { 165 sequenced_task_runner_->PostTask(FROM_HERE, 166 base::Bind(&LoadCallback, 167 writer_.path(), 168 weak_factory_.GetWeakPtr(), 169 base::Passed(&details), 170 task_runner)); 171 } 172 173 void BookmarkStorage::ScheduleSave() { 174 writer_.ScheduleWrite(this); 175 } 176 177 void BookmarkStorage::BookmarkModelDeleted() { 178 // We need to save now as otherwise by the time SaveNow is invoked 179 // the model is gone. 180 if (writer_.HasPendingWrite()) 181 SaveNow(); 182 model_ = NULL; 183 } 184 185 bool BookmarkStorage::SerializeData(std::string* output) { 186 BookmarkCodec codec; 187 scoped_ptr<base::Value> value(codec.Encode(model_)); 188 JSONStringValueSerializer serializer(output); 189 serializer.set_pretty_print(true); 190 return serializer.Serialize(*(value.get())); 191 } 192 193 void BookmarkStorage::OnLoadFinished(scoped_ptr<BookmarkLoadDetails> details) { 194 if (!model_) 195 return; 196 197 model_->DoneLoading(details.Pass()); 198 } 199 200 bool BookmarkStorage::SaveNow() { 201 if (!model_ || !model_->loaded()) { 202 // We should only get here if we have a valid model and it's finished 203 // loading. 204 NOTREACHED(); 205 return false; 206 } 207 208 std::string data; 209 if (!SerializeData(&data)) 210 return false; 211 writer_.WriteNow(data); 212 return true; 213 } 214 215 } // namespace bookmarks 216