Home | History | Annotate | Download | only in browser
      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 
     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.
     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 
     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 
    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 
    135 BookmarkLoadDetails::~BookmarkLoadDetails() {
    136 }
    137 
    138 void BookmarkLoadDetails::LoadExtraNodes() {
    139   extra_nodes_ = load_extra_callback_.Run(&max_id_);
    140 }
    141 
    142 // BookmarkStorage -------------------------------------------------------------
    143 
    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 
    157 BookmarkStorage::~BookmarkStorage() {
    158   if (writer_.HasPendingWrite())
    159     writer_.DoScheduledWrite();
    160 }
    161 
    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 
    173 void BookmarkStorage::ScheduleSave() {
    174   writer_.ScheduleWrite(this);
    175 }
    176 
    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 
    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 
    193 void BookmarkStorage::OnLoadFinished(scoped_ptr<BookmarkLoadDetails> details) {
    194   if (!model_)
    195     return;
    196 
    197   model_->DoneLoading(details.Pass());
    198 }
    199 
    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