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