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