• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BackupCallback(const base::FilePath & path)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.
AddBookmarksToIndex(BookmarkLoadDetails * details,BookmarkNode * node)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 
LoadCallback(const base::FilePath & path,const base::WeakPtr<BookmarkStorage> & storage,scoped_ptr<BookmarkLoadDetails> details,base::SequencedTaskRunner * task_runner)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 
BookmarkLoadDetails(BookmarkPermanentNode * bb_node,BookmarkPermanentNode * other_folder_node,BookmarkPermanentNode * mobile_folder_node,const LoadExtraCallback & load_extra_callback,BookmarkIndex * index,int64 max_id)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 
~BookmarkLoadDetails()135 BookmarkLoadDetails::~BookmarkLoadDetails() {
136 }
137 
LoadExtraNodes()138 void BookmarkLoadDetails::LoadExtraNodes() {
139   extra_nodes_ = load_extra_callback_.Run(&max_id_);
140 }
141 
142 // BookmarkStorage -------------------------------------------------------------
143 
BookmarkStorage(BookmarkModel * model,const base::FilePath & profile_path,base::SequencedTaskRunner * sequenced_task_runner)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 
~BookmarkStorage()157 BookmarkStorage::~BookmarkStorage() {
158   if (writer_.HasPendingWrite())
159     writer_.DoScheduledWrite();
160 }
161 
LoadBookmarks(scoped_ptr<BookmarkLoadDetails> details,const scoped_refptr<base::SequencedTaskRunner> & task_runner)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 
ScheduleSave()173 void BookmarkStorage::ScheduleSave() {
174   writer_.ScheduleWrite(this);
175 }
176 
BookmarkModelDeleted()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 
SerializeData(std::string * output)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 
OnLoadFinished(scoped_ptr<BookmarkLoadDetails> details)193 void BookmarkStorage::OnLoadFinished(scoped_ptr<BookmarkLoadDetails> details) {
194   if (!model_)
195     return;
196 
197   model_->DoneLoading(details.Pass());
198 }
199 
SaveNow()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