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.
5 #include "components/bookmarks/browser/bookmark_storage.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"
21 using base::TimeTicks
;
27 // Extension used for backup files (copy of main file created during startup).
28 const base::FilePath::CharType kBackupExtension
[] = FILE_PATH_LITERAL("bak");
31 const int kSaveDelayMS
= 2500;
33 void BackupCallback(const base::FilePath
& path
) {
34 base::FilePath backup_path
= path
.ReplaceExtension(kBackupExtension
);
35 base::CopyFile(path
, backup_path
);
38 // Adds node to the model's index, recursing through all children as well.
39 void AddBookmarksToIndex(BookmarkLoadDetails
* details
,
42 if (node
->url().is_valid())
43 details
->index()->Add(node
);
45 for (int i
= 0; i
< node
->child_count(); ++i
)
46 AddBookmarksToIndex(details
, node
->GetChild(i
));
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
));
63 // Building the index can take a while, so we do it on the background
65 int64 max_node_id
= 0;
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
);
84 // Load any extra root nodes now, after the IDs have been potentially
86 details
->LoadExtraNodes();
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()) {
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
);
108 task_runner
->PostTask(FROM_HERE
,
109 base::Bind(&BookmarkStorage::OnLoadFinished
, storage
,
110 base::Passed(&details
)));
115 // BookmarkLoadDetails ---------------------------------------------------------
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
,
125 other_folder_node_(other_folder_node
),
126 mobile_folder_node_(mobile_folder_node
),
127 load_extra_callback_(load_extra_callback
),
129 model_sync_transaction_version_(
130 BookmarkNode::kInvalidSyncTransactionVersion
),
132 ids_reassigned_(false) {
135 BookmarkLoadDetails::~BookmarkLoadDetails() {
138 void BookmarkLoadDetails::LoadExtraNodes() {
139 extra_nodes_
= load_extra_callback_
.Run(&max_id_
);
142 // BookmarkStorage -------------------------------------------------------------
144 BookmarkStorage::BookmarkStorage(
145 BookmarkModel
* model
,
146 const base::FilePath
& profile_path
,
147 base::SequencedTaskRunner
* sequenced_task_runner
)
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()));
157 BookmarkStorage::~BookmarkStorage() {
158 if (writer_
.HasPendingWrite())
159 writer_
.DoScheduledWrite();
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
,
168 weak_factory_
.GetWeakPtr(),
169 base::Passed(&details
),
173 void BookmarkStorage::ScheduleSave() {
174 writer_
.ScheduleWrite(this);
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())
185 bool BookmarkStorage::SerializeData(std::string
* output
) {
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()));
193 void BookmarkStorage::OnLoadFinished(scoped_ptr
<BookmarkLoadDetails
> details
) {
197 model_
->DoneLoading(details
.Pass());
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
209 if (!SerializeData(&data
))
211 writer_
.WriteNow(data
);
215 } // namespace bookmarks