Home | History | Annotate | Download | only in bookmarks
      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/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 "chrome/common/startup_metric_utils.h"
     20 #include "content/public/browser/browser_context.h"
     21 #include "content/public/browser/browser_thread.h"
     22 
     23 using base::TimeTicks;
     24 using content::BrowserThread;
     25 
     26 namespace {
     27 
     28 // Extension used for backup files (copy of main file created during startup).
     29 const base::FilePath::CharType kBackupExtension[] = FILE_PATH_LITERAL("bak");
     30 
     31 // How often we save.
     32 const int kSaveDelayMS = 2500;
     33 
     34 void BackupCallback(const base::FilePath& path) {
     35   base::FilePath backup_path = path.ReplaceExtension(kBackupExtension);
     36   base::CopyFile(path, backup_path);
     37 }
     38 
     39 // Adds node to the model's index, recursing through all children as well.
     40 void AddBookmarksToIndex(BookmarkLoadDetails* details,
     41                          BookmarkNode* node) {
     42   if (node->is_url()) {
     43     if (node->url().is_valid())
     44       details->index()->Add(node);
     45   } else {
     46     for (int i = 0; i < node->child_count(); ++i)
     47       AddBookmarksToIndex(details, node->GetChild(i));
     48   }
     49 }
     50 
     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<Value> root(serializer.Deserialize(NULL, NULL));
     60 
     61     if (root.get()) {
     62       // Building the index can take a while, so we do it on the background
     63       // thread.
     64       int64 max_node_id = 0;
     65       BookmarkCodec codec;
     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(codec.model_meta_info());
     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   BrowserThread::PostTask(
     87       BrowserThread::UI, 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       max_id_(max_id),
    106       ids_reassigned_(false) {
    107 }
    108 
    109 BookmarkLoadDetails::~BookmarkLoadDetails() {
    110 }
    111 
    112 // BookmarkStorage -------------------------------------------------------------
    113 
    114 BookmarkStorage::BookmarkStorage(
    115     content::BrowserContext* context,
    116     BookmarkModel* model,
    117     base::SequencedTaskRunner* sequenced_task_runner)
    118     : model_(model),
    119       writer_(context->GetPath().Append(chrome::kBookmarksFileName),
    120               sequenced_task_runner) {
    121   sequenced_task_runner_ = sequenced_task_runner;
    122   writer_.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS));
    123   sequenced_task_runner_->PostTask(FROM_HERE,
    124                                    base::Bind(&BackupCallback, writer_.path()));
    125 }
    126 
    127 BookmarkStorage::~BookmarkStorage() {
    128   if (writer_.HasPendingWrite())
    129     writer_.DoScheduledWrite();
    130 }
    131 
    132 void BookmarkStorage::LoadBookmarks(BookmarkLoadDetails* details) {
    133   DCHECK(!details_.get());
    134   DCHECK(details);
    135   details_.reset(details);
    136   sequenced_task_runner_->PostTask(
    137       FROM_HERE,
    138       base::Bind(&LoadCallback, writer_.path(), make_scoped_refptr(this),
    139                  details_.get()));
    140 }
    141 
    142 void BookmarkStorage::ScheduleSave() {
    143   writer_.ScheduleWrite(this);
    144 }
    145 
    146 void BookmarkStorage::BookmarkModelDeleted() {
    147   // We need to save now as otherwise by the time SaveNow is invoked
    148   // the model is gone.
    149   if (writer_.HasPendingWrite())
    150     SaveNow();
    151   model_ = NULL;
    152 }
    153 
    154 bool BookmarkStorage::SerializeData(std::string* output) {
    155   BookmarkCodec codec;
    156   scoped_ptr<Value> value(codec.Encode(model_));
    157   JSONStringValueSerializer serializer(output);
    158   serializer.set_pretty_print(true);
    159   return serializer.Serialize(*(value.get()));
    160 }
    161 
    162 void BookmarkStorage::OnLoadFinished() {
    163   if (!model_)
    164     return;
    165 
    166   model_->DoneLoading(details_.release());
    167 }
    168 
    169 bool BookmarkStorage::SaveNow() {
    170   if (!model_ || !model_->loaded()) {
    171     // We should only get here if we have a valid model and it's finished
    172     // loading.
    173     NOTREACHED();
    174     return false;
    175   }
    176 
    177   std::string data;
    178   if (!SerializeData(&data))
    179     return false;
    180   writer_.WriteNow(data);
    181   return true;
    182 }
    183