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