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.
5 #include "chrome/browser/bookmarks/bookmark_storage.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 "components/startup_metric_utils/startup_metric_utils.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
23 using base::TimeTicks
;
24 using content::BrowserThread
;
28 // Extension used for backup files (copy of main file created during startup).
29 const base::FilePath::CharType kBackupExtension
[] = FILE_PATH_LITERAL("bak");
32 const int kSaveDelayMS
= 2500;
34 void BackupCallback(const base::FilePath
& path
) {
35 base::FilePath backup_path
= path
.ReplaceExtension(kBackupExtension
);
36 base::CopyFile(path
, backup_path
);
39 // Adds node to the model's index, recursing through all children as well.
40 void AddBookmarksToIndex(BookmarkLoadDetails
* details
,
43 if (node
->url().is_valid())
44 details
->index()->Add(node
);
46 for (int i
= 0; i
< node
->child_count(); ++i
)
47 AddBookmarksToIndex(details
, node
->GetChild(i
));
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
<base::Value
> root(serializer
.Deserialize(NULL
, NULL
));
62 // Building the index can take a while, so we do it on the background
64 int64 max_node_id
= 0;
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_map(codec
.model_meta_info_map());
74 details
->set_model_sync_transaction_version(
75 codec
.model_sync_transaction_version());
76 UMA_HISTOGRAM_TIMES("Bookmarks.DecodeTime",
77 TimeTicks::Now() - start_time
);
79 start_time
= TimeTicks::Now();
80 AddBookmarksToIndex(details
, details
->bb_node());
81 AddBookmarksToIndex(details
, details
->other_folder_node());
82 AddBookmarksToIndex(details
, details
->mobile_folder_node());
83 UMA_HISTOGRAM_TIMES("Bookmarks.CreateBookmarkIndexTime",
84 TimeTicks::Now() - start_time
);
88 BrowserThread::PostTask(
89 BrowserThread::UI
, FROM_HERE
,
90 base::Bind(&BookmarkStorage::OnLoadFinished
, storage
));
95 // BookmarkLoadDetails ---------------------------------------------------------
97 BookmarkLoadDetails::BookmarkLoadDetails(
98 BookmarkPermanentNode
* bb_node
,
99 BookmarkPermanentNode
* other_folder_node
,
100 BookmarkPermanentNode
* mobile_folder_node
,
101 BookmarkIndex
* index
,
104 other_folder_node_(other_folder_node
),
105 mobile_folder_node_(mobile_folder_node
),
107 model_sync_transaction_version_(
108 BookmarkNode::kInvalidSyncTransactionVersion
),
110 ids_reassigned_(false) {
113 BookmarkLoadDetails::~BookmarkLoadDetails() {
116 // BookmarkStorage -------------------------------------------------------------
118 BookmarkStorage::BookmarkStorage(
119 content::BrowserContext
* context
,
120 BookmarkModel
* model
,
121 base::SequencedTaskRunner
* sequenced_task_runner
)
123 writer_(context
->GetPath().Append(chrome::kBookmarksFileName
),
124 sequenced_task_runner
) {
125 sequenced_task_runner_
= sequenced_task_runner
;
126 writer_
.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS
));
127 sequenced_task_runner_
->PostTask(FROM_HERE
,
128 base::Bind(&BackupCallback
, writer_
.path()));
131 BookmarkStorage::~BookmarkStorage() {
132 if (writer_
.HasPendingWrite())
133 writer_
.DoScheduledWrite();
136 void BookmarkStorage::LoadBookmarks(BookmarkLoadDetails
* details
) {
137 DCHECK(!details_
.get());
139 details_
.reset(details
);
140 sequenced_task_runner_
->PostTask(
142 base::Bind(&LoadCallback
, writer_
.path(), make_scoped_refptr(this),
146 void BookmarkStorage::ScheduleSave() {
147 writer_
.ScheduleWrite(this);
150 void BookmarkStorage::BookmarkModelDeleted() {
151 // We need to save now as otherwise by the time SaveNow is invoked
152 // the model is gone.
153 if (writer_
.HasPendingWrite())
158 bool BookmarkStorage::SerializeData(std::string
* output
) {
160 scoped_ptr
<base::Value
> value(codec
.Encode(model_
));
161 JSONStringValueSerializer
serializer(output
);
162 serializer
.set_pretty_print(true);
163 return serializer
.Serialize(*(value
.get()));
166 void BookmarkStorage::OnLoadFinished() {
170 model_
->DoneLoading(details_
.release());
173 bool BookmarkStorage::SaveNow() {
174 if (!model_
|| !model_
->loaded()) {
175 // We should only get here if we have a valid model and it's finished
182 if (!SerializeData(&data
))
184 writer_
.WriteNow(data
);