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/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                   BookmarkStorage* storage,
     52                   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, details->bb_node());
    100     AddBookmarksToIndex(details, details->other_folder_node());
    101     AddBookmarksToIndex(details, details->mobile_folder_node());
    102     for (size_t i = 0; i < extra_nodes.size(); ++i)
    103       AddBookmarksToIndex(details, 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 }
    111 
    112 }  // namespace
    113 
    114 // BookmarkLoadDetails ---------------------------------------------------------
    115 
    116 BookmarkLoadDetails::BookmarkLoadDetails(
    117     BookmarkPermanentNode* bb_node,
    118     BookmarkPermanentNode* other_folder_node,
    119     BookmarkPermanentNode* mobile_folder_node,
    120     const LoadExtraCallback& load_extra_callback,
    121     BookmarkIndex* index,
    122     int64 max_id)
    123     : bb_node_(bb_node),
    124       other_folder_node_(other_folder_node),
    125       mobile_folder_node_(mobile_folder_node),
    126       load_extra_callback_(load_extra_callback),
    127       index_(index),
    128       model_sync_transaction_version_(
    129           BookmarkNode::kInvalidSyncTransactionVersion),
    130       max_id_(max_id),
    131       ids_reassigned_(false) {
    132 }
    133 
    134 BookmarkLoadDetails::~BookmarkLoadDetails() {
    135 }
    136 
    137 void BookmarkLoadDetails::LoadExtraNodes() {
    138   extra_nodes_ = load_extra_callback_.Run(&max_id_);
    139 }
    140 
    141 // BookmarkStorage -------------------------------------------------------------
    142 
    143 BookmarkStorage::BookmarkStorage(
    144     BookmarkModel* model,
    145     const base::FilePath& profile_path,
    146     base::SequencedTaskRunner* sequenced_task_runner)
    147     : model_(model),
    148       writer_(profile_path.Append(bookmarks::kBookmarksFileName),
    149               sequenced_task_runner) {
    150   sequenced_task_runner_ = sequenced_task_runner;
    151   writer_.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS));
    152   sequenced_task_runner_->PostTask(FROM_HERE,
    153                                    base::Bind(&BackupCallback, writer_.path()));
    154 }
    155 
    156 BookmarkStorage::~BookmarkStorage() {
    157   if (writer_.HasPendingWrite())
    158     writer_.DoScheduledWrite();
    159 }
    160 
    161 void BookmarkStorage::LoadBookmarks(
    162     scoped_ptr<BookmarkLoadDetails> details,
    163     const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
    164   DCHECK(!details_.get());
    165   DCHECK(details);
    166   details_ = details.Pass();
    167   sequenced_task_runner_->PostTask(FROM_HERE,
    168                                    base::Bind(&LoadCallback,
    169                                               writer_.path(),
    170                                               make_scoped_refptr(this),
    171                                               details_.get(),
    172                                               task_runner));
    173 }
    174 
    175 void BookmarkStorage::ScheduleSave() {
    176   writer_.ScheduleWrite(this);
    177 }
    178 
    179 void BookmarkStorage::BookmarkModelDeleted() {
    180   // We need to save now as otherwise by the time SaveNow is invoked
    181   // the model is gone.
    182   if (writer_.HasPendingWrite())
    183     SaveNow();
    184   model_ = NULL;
    185 }
    186 
    187 bool BookmarkStorage::SerializeData(std::string* output) {
    188   BookmarkCodec codec;
    189   scoped_ptr<base::Value> value(codec.Encode(model_));
    190   JSONStringValueSerializer serializer(output);
    191   serializer.set_pretty_print(true);
    192   return serializer.Serialize(*(value.get()));
    193 }
    194 
    195 void BookmarkStorage::OnLoadFinished() {
    196   if (!model_)
    197     return;
    198 
    199   model_->DoneLoading(details_.Pass());
    200 }
    201 
    202 bool BookmarkStorage::SaveNow() {
    203   if (!model_ || !model_->loaded()) {
    204     // We should only get here if we have a valid model and it's finished
    205     // loading.
    206     NOTREACHED();
    207     return false;
    208   }
    209 
    210   std::string data;
    211   if (!SerializeData(&data))
    212     return false;
    213   writer_.WriteNow(data);
    214   return true;
    215 }
    216 
    217 }  // namespace bookmarks
    218