| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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/bookmark_storage.h" | |
| 6 | |
| 7 #include "base/file_util.h" | |
| 8 #include "base/json_writer.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "chrome/browser/bookmark_bar_model.h" | |
| 11 #include "chrome/browser/bookmark_codec.h" | |
| 12 #include "chrome/browser/profile.h" | |
| 13 #include "chrome/common/chrome_constants.h" | |
| 14 #include "chrome/common/json_value_serializer.h" | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // Extension used for backup files (copy of main file created during startup). | |
| 19 const wchar_t* const kBackupExtension = L"bak"; | |
| 20 | |
| 21 // Extension for the temporary file. We write to the temp file than move to | |
| 22 // kBookmarksFileName. | |
| 23 const wchar_t* const kTmpExtension = L"tmp"; | |
| 24 | |
| 25 // How often we save. | |
| 26 const int kSaveDelayMS = 2500; | |
| 27 | |
| 28 } // namespace | |
| 29 | |
| 30 // BookmarkStorage ------------------------------------------------------------- | |
| 31 | |
| 32 BookmarkStorage::BookmarkStorage(Profile* profile, BookmarkBarModel* model) | |
| 33 : model_(model), | |
| 34 #pragma warning(suppress: 4355) // Okay to pass "this" here. | |
| 35 save_factory_(this), | |
| 36 backend_thread_(g_browser_process->file_thread()) { | |
| 37 std::wstring path = profile->GetPath(); | |
| 38 file_util::AppendToPath(&path, chrome::kBookmarksFileName); | |
| 39 std::wstring tmp_history_path = profile->GetPath(); | |
| 40 file_util::AppendToPath(&tmp_history_path, chrome::kHistoryBookmarksFileName); | |
| 41 backend_ = new BookmarkStorageBackend(path, tmp_history_path); | |
| 42 } | |
| 43 | |
| 44 void BookmarkStorage::LoadBookmarks(bool load_from_history) { | |
| 45 if (!backend_thread()) { | |
| 46 backend_->Read(scoped_refptr<BookmarkStorage>(this), NULL, | |
| 47 load_from_history); | |
| 48 } else { | |
| 49 backend_thread()->message_loop()->PostTask( | |
| 50 FROM_HERE, | |
| 51 NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Read, | |
| 52 scoped_refptr<BookmarkStorage>(this), | |
| 53 MessageLoop::current(), load_from_history)); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 void BookmarkStorage::ScheduleSave() { | |
| 58 if (!backend_thread()) { | |
| 59 SaveNow(); | |
| 60 } else if (save_factory_.empty()) { | |
| 61 MessageLoop::current()->PostDelayedTask( | |
| 62 FROM_HERE, save_factory_.NewRunnableMethod(&BookmarkStorage::SaveNow), | |
| 63 kSaveDelayMS); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 void BookmarkStorage::BookmarkModelDeleted() { | |
| 68 if (!save_factory_.empty()) { | |
| 69 // There's a pending save. We need to save now as otherwise by the time | |
| 70 // SaveNow is invoked the model is gone. | |
| 71 save_factory_.RevokeAll(); | |
| 72 SaveNow(); | |
| 73 } | |
| 74 model_ = NULL; | |
| 75 } | |
| 76 | |
| 77 void BookmarkStorage::LoadedBookmarks(Value* root_value, | |
| 78 bool bookmark_file_exists, | |
| 79 bool loaded_from_history) { | |
| 80 scoped_ptr<Value> value_ref(root_value); | |
| 81 | |
| 82 if (model_) { | |
| 83 if (root_value) { | |
| 84 BookmarkCodec codec; | |
| 85 codec.Decode(model_, *root_value); | |
| 86 } | |
| 87 model_->OnBookmarkStorageLoadedBookmarks(bookmark_file_exists, | |
| 88 loaded_from_history); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 void BookmarkStorage::SaveNow() { | |
| 93 if (!model_ || !model_->IsLoaded()) { | |
| 94 // We should only get here if we have a valid model and it's finished | |
| 95 // loading. | |
| 96 NOTREACHED(); | |
| 97 return; | |
| 98 } | |
| 99 | |
| 100 BookmarkCodec codec; | |
| 101 Value* value = codec.Encode(model_); | |
| 102 // The backend deletes value in write. | |
| 103 if (!backend_thread()) { | |
| 104 backend_->Write(value); | |
| 105 } else { | |
| 106 backend_thread()->message_loop()->PostTask( | |
| 107 FROM_HERE, | |
| 108 NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Write, | |
| 109 value)); | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 // BookmarkStorageBackend ------------------------------------------------------ | |
| 114 | |
| 115 BookmarkStorageBackend::BookmarkStorageBackend( | |
| 116 const std::wstring& path, | |
| 117 const std::wstring& tmp_history_path) | |
| 118 : path_(path), | |
| 119 tmp_history_path_(tmp_history_path) { | |
| 120 // Make a backup of the current file. | |
| 121 std::wstring backup_path = path; | |
| 122 file_util::ReplaceExtension(&backup_path, kBackupExtension); | |
| 123 file_util::CopyFile(path, backup_path); | |
| 124 } | |
| 125 | |
| 126 void BookmarkStorageBackend::Write(Value* value) { | |
| 127 DCHECK(value); | |
| 128 | |
| 129 // We own Value. | |
| 130 scoped_ptr<Value> value_ref(value); | |
| 131 | |
| 132 std::string content; | |
| 133 JSONWriter::Write(value, true, &content); | |
| 134 | |
| 135 // Write to a temp file, then rename. | |
| 136 std::wstring tmp_file = path_; | |
| 137 file_util::ReplaceExtension(&tmp_file, kTmpExtension); | |
| 138 | |
| 139 int bytes_written = file_util::WriteFile(tmp_file, content.c_str(), | |
| 140 static_cast<int>(content.length())); | |
| 141 if (bytes_written != -1) { | |
| 142 if (!MoveFileEx(tmp_file.c_str(), path_.c_str(), | |
| 143 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { | |
| 144 // Rename failed. Try again on the off chance someone has locked either | |
| 145 // file and hope we're successful the second time through. | |
| 146 BOOL move_result = | |
| 147 MoveFileEx(tmp_file.c_str(), path_.c_str(), | |
| 148 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); | |
| 149 DCHECK(move_result); | |
| 150 } | |
| 151 // Nuke the history file so that we don't attempt to load from it again. | |
| 152 file_util::Delete(tmp_history_path_, false); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void BookmarkStorageBackend::Read(scoped_refptr<BookmarkStorage> service, | |
| 157 MessageLoop* message_loop, | |
| 158 bool load_from_history) { | |
| 159 const std::wstring& path = load_from_history ? tmp_history_path_ : path_; | |
| 160 bool bookmark_file_exists = file_util::PathExists(path); | |
| 161 Value* root = NULL; | |
| 162 if (bookmark_file_exists) { | |
| 163 JSONFileValueSerializer serializer(path); | |
| 164 serializer.Deserialize(&root); | |
| 165 } | |
| 166 | |
| 167 // BookmarkStorage takes ownership of root. | |
| 168 if (message_loop) { | |
| 169 message_loop->PostTask(FROM_HERE, NewRunnableMethod( | |
| 170 service.get(), &BookmarkStorage::LoadedBookmarks, root, | |
| 171 bookmark_file_exists, load_from_history)); | |
| 172 } else { | |
| 173 service->LoadedBookmarks(root, bookmark_file_exists, load_from_history); | |
| 174 } | |
| 175 } | |
| OLD | NEW |