summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-01 17:36:48 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-01 17:36:48 +0000
commit1fcb8b01fe855dde2485b82d620cdaecb580a3bd (patch)
tree5b626c7f5bb3f1272f1ef4c42b273dd54d4a1cc5 /chrome/browser
parent4bc09852839086aef45cea030b5973ea3f197e23 (diff)
downloadchromium_src-1fcb8b01fe855dde2485b82d620cdaecb580a3bd.zip
chromium_src-1fcb8b01fe855dde2485b82d620cdaecb580a3bd.tar.gz
chromium_src-1fcb8b01fe855dde2485b82d620cdaecb580a3bd.tar.bz2
Adds a backend for the bookmarks bar that reads/writes to a JSON
file. These files aren't being built or used yet (though a test case exists that is ifdef'd out). BUG=1256202 TEST=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/bookmark_bar_model.h13
-rw-r--r--chrome/browser/bookmark_bar_model_unittest.cc70
-rw-r--r--chrome/browser/bookmark_codec.cc197
-rw-r--r--chrome/browser/bookmark_codec.h80
-rw-r--r--chrome/browser/bookmark_storage.cc185
-rw-r--r--chrome/browser/bookmark_storage.h95
6 files changed, 640 insertions, 0 deletions
diff --git a/chrome/browser/bookmark_bar_model.h b/chrome/browser/bookmark_bar_model.h
index edff1c7..b7e75f8 100644
--- a/chrome/browser/bookmark_bar_model.h
+++ b/chrome/browser/bookmark_bar_model.h
@@ -40,6 +40,7 @@
#include "skia/include/SkBitmap.h"
class BookmarkBarModel;
+class BookmarkCodec;
class Profile;
// BookmarkBarNode ------------------------------------------------------------
@@ -50,6 +51,7 @@ class Profile;
//
class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> {
friend class BookmarkBarModel;
+ friend class BookmarkCodec;
public:
explicit BookmarkBarNode(BookmarkBarModel* model);
@@ -64,6 +66,8 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> {
const GURL& GetURL() const { return url_; }
// Returns the start ID corresponding to this node.
+ // TODO(sky): bug 1256202, make this an ever increasing integer assigned on
+ // reading, but not archived. Best to set it automatically in the constructor.
history::StarID GetStarID() const { return star_id_; }
// Returns the type of this node.
@@ -73,6 +77,7 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> {
history::StarredEntry GetEntry();
// Returns the ID of group.
+ // TODO(sky): bug 1256202, nuke this.
history::UIStarID GetGroupID() { return group_id_; }
// Called when the favicon becomes invalid.
@@ -81,6 +86,13 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> {
favicon_ = SkBitmap();
}
+ // Returns the time the bookmark/group was added.
+ Time date_added() const { return date_added_; }
+
+ // Returns the last time the group was modified. This is only maintained
+ // for folders (including the bookmark and other folder).
+ Time date_group_modified() const { return date_group_modified_; }
+
private:
// Resets the properties of the node from the supplied entry.
void Reset(const history::StarredEntry& entry);
@@ -108,6 +120,7 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> {
GURL url_;
// Type of node.
+ // TODO(sky): bug 1256202, convert this into a type defined here.
history::StarredEntry::Type type_;
// Group ID.
diff --git a/chrome/browser/bookmark_bar_model_unittest.cc b/chrome/browser/bookmark_bar_model_unittest.cc
index efc94a7..5a95963 100644
--- a/chrome/browser/bookmark_bar_model_unittest.cc
+++ b/chrome/browser/bookmark_bar_model_unittest.cc
@@ -28,6 +28,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "base/string_util.h"
+#ifdef USE_BOOKMARK_CODEC
+#include "chrome/browser/bookmark_codec.h"
+#endif // USE_BOOKMARK_CODEC
#include "chrome/browser/bookmark_bar_model.h"
#include "chrome/test/testing_profile.h"
#include "chrome/views/tree_node_model.h"
@@ -131,6 +134,31 @@ class BookmarkBarModelTest : public testing::Test,
return GetMaxGroupID(model.root_node());
}
+ void AssertNodesEqual(BookmarkBarNode* expected, BookmarkBarNode* actual) {
+ ASSERT_TRUE(expected);
+ ASSERT_TRUE(actual);
+ EXPECT_EQ(expected->GetTitle(), actual->GetTitle());
+ EXPECT_EQ(expected->GetType(), actual->GetType());
+ EXPECT_TRUE(expected->date_added() == actual->date_added());
+ if (expected->GetType() == history::StarredEntry::URL) {
+ EXPECT_EQ(expected->GetURL(), actual->GetURL());
+ } else {
+ EXPECT_TRUE(expected->date_group_modified() ==
+ actual->date_group_modified());
+ ASSERT_EQ(expected->GetChildCount(), actual->GetChildCount());
+ for (int i = 0; i < expected->GetChildCount(); ++i)
+ AssertNodesEqual(expected->GetChild(i), actual->GetChild(i));
+ }
+ }
+
+ void AssertModelsEqual(BookmarkBarModel* expected,
+ BookmarkBarModel* actual) {
+ AssertNodesEqual(expected->GetBookmarkBarNode(),
+ actual->GetBookmarkBarNode());
+ AssertNodesEqual(expected->other_node(),
+ actual->other_node());
+ }
+
BookmarkBarModel model;
int moved_count;
@@ -572,3 +600,45 @@ TEST_F(BookmarkBarModelTestWithProfile, CreateAndRestore) {
VerifyModelMatchesNode(&other, bb_model_->other_node());
}
}
+
+#ifdef USE_BOOKMARK_CODEC
+// Creates a set of nodes in the bookmark bar model, then recreates the
+// bookmark bar model which triggers loading from the db and checks the loaded
+// structure to make sure it is what we first created.
+TEST_F(BookmarkBarModelTest, TestJSONCodec) {
+ struct TestData {
+ // Structure of the children of the bookmark bar model node.
+ const std::wstring bbn_contents;
+ // Structure of the children of the other node.
+ const std::wstring other_contents;
+ } data[] = {
+ // See PopulateNodeFromString for a description of these strings.
+ { L"", L"" },
+ { L"a", L"b" },
+ { L"a [ b ]", L"" },
+ { L"", L"[ b ] a [ c [ d e [ f ] ] ]" },
+ { L"a [ b ]", L"" },
+ { L"a b c [ d e [ f ] ]", L"g h i [ j k [ l ] ]"},
+ };
+ for (int i = 0; i < arraysize(data); ++i) {
+ BookmarkBarModel expected_model(NULL);
+
+ TestNode bbn;
+ PopulateNodeFromString(data[i].bbn_contents, &bbn);
+ PopulateBookmarkBarNode(&bbn, &expected_model,
+ expected_model.GetBookmarkBarNode());
+
+ TestNode other;
+ PopulateNodeFromString(data[i].other_contents, &other);
+ PopulateBookmarkBarNode(&other, &expected_model,
+ expected_model.other_node());
+
+ BookmarkBarModel actual_model(NULL);
+ BookmarkCodec codec;
+ scoped_ptr<Value> encoded_value(codec.Encode(&expected_model));
+ codec.Decode(&actual_model, *(encoded_value.get()));
+
+ AssertModelsEqual(&expected_model, &actual_model);
+ }
+}
+#endif // USE_BOOKMARK_CODEC
diff --git a/chrome/browser/bookmark_codec.cc b/chrome/browser/bookmark_codec.cc
new file mode 100644
index 0000000..37f0ed0
--- /dev/null
+++ b/chrome/browser/bookmark_codec.cc
@@ -0,0 +1,197 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/bookmark_codec.h"
+
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "googleurl/src/gurl.h"
+
+// Key names.
+static const wchar_t* kRootsKey = L"roots";
+static const wchar_t* kRootFolderNameKey = L"root";
+static const wchar_t* kOtherBookmarFolderNameKey = L"other";
+static const wchar_t* kVersionKey = L"version";
+static const wchar_t* kTypeKey = L"type";
+static const wchar_t* kNameKey = L"name";
+static const wchar_t* kDateAddedKey = L"date_added";
+static const wchar_t* kURLKey = L"url";
+static const wchar_t* kDateModifiedKey = L"date_modified";
+static const wchar_t* kChildrenKey = L"children";
+
+// Possible values for kTypeKey.
+static const wchar_t* kTypeURL = L"url";
+static const wchar_t* kTypeFolder = L"folder";
+
+// Current version of the file.
+static const int kCurrentVersion = 1;
+
+Value* BookmarkCodec::Encode(BookmarkBarModel* model) {
+ DictionaryValue* roots = new DictionaryValue();
+ roots->Set(kRootFolderNameKey, EncodeNode(model->GetBookmarkBarNode()));
+ roots->Set(kOtherBookmarFolderNameKey, EncodeNode(model->other_node()));
+
+ DictionaryValue* main = new DictionaryValue();
+ main->SetInteger(kVersionKey, kCurrentVersion);
+ main->Set(kRootsKey, roots);
+ return main;
+}
+
+bool BookmarkCodec::Decode(BookmarkBarModel* model, const Value& value) {
+ if (value.GetType() != Value::TYPE_DICTIONARY)
+ return false; // Unexpected type.
+
+ const DictionaryValue& d_value = static_cast<const DictionaryValue&>(value);
+
+ int version;
+ if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion)
+ return false; // Unknown version.
+
+ Value* roots;
+ if (!d_value.Get(kRootsKey, &roots))
+ return false; // No roots.
+
+ if (roots->GetType() != Value::TYPE_DICTIONARY)
+ return false; // Invalid type for roots.
+
+ DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
+ Value* root_folder_value;
+ Value* other_folder_value;
+ if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) ||
+ root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
+ !roots_d_value->Get(kOtherBookmarFolderNameKey, &other_folder_value) ||
+ other_folder_value->GetType() != Value::TYPE_DICTIONARY)
+ return false; // Invalid type for root folder and/or other folder.
+
+ DecodeNode(model, *static_cast<DictionaryValue*>(root_folder_value),
+ model->GetBookmarkBarNode());
+ DecodeNode(model, *static_cast<DictionaryValue*>(other_folder_value),
+ model->other_node());
+ // Need to reset these as Decode sets the type to FOLDER.
+ model->GetBookmarkBarNode()->type_ = history::StarredEntry::BOOKMARK_BAR;
+ model->other_node()->type_ = history::StarredEntry::OTHER;
+ return true;
+}
+
+Value* BookmarkCodec::EncodeNode(BookmarkBarNode* node) {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetString(kNameKey, node->GetTitle());
+ value->SetString(kDateAddedKey,
+ Int64ToWString(node->date_added().ToInternalValue()));
+ if (node->GetType() == history::StarredEntry::URL) {
+ value->SetString(kTypeKey, kTypeURL);
+ value->SetString(kURLKey,
+ UTF8ToWide(node->GetURL().possibly_invalid_spec()));
+ } else {
+ value->SetString(kTypeKey, kTypeFolder);
+ value->SetString(kDateModifiedKey,
+ Int64ToWString(node->date_group_modified().
+ ToInternalValue()));
+
+ ListValue* child_values = new ListValue();
+ value->Set(kChildrenKey, child_values);
+ for (int i = 0; i < node->GetChildCount(); ++i)
+ child_values->Append(EncodeNode(node->GetChild(i)));
+ }
+ return value;
+}
+
+bool BookmarkCodec::DecodeChildren(BookmarkBarModel* model,
+ const ListValue& child_value_list,
+ BookmarkBarNode* parent) {
+ for (size_t i = 0; i < child_value_list.GetSize(); ++i) {
+ Value* child_value;
+ if (!child_value_list.Get(i, &child_value))
+ return false;
+
+ if (child_value->GetType() != Value::TYPE_DICTIONARY)
+ return false;
+
+ BookmarkBarNode* child = new BookmarkBarNode(model);
+ parent->Add(static_cast<int>(i), child);
+ if (!DecodeNode(model, *static_cast<DictionaryValue*>(child_value), child))
+ return false;
+ }
+ return true;
+}
+
+bool BookmarkCodec::DecodeNode(BookmarkBarModel* model,
+ const DictionaryValue& value,
+ BookmarkBarNode* node) {
+ std::wstring title;
+ if (!value.GetString(kNameKey, &title))
+ return false;
+ node->SetTitle(title);
+
+ std::wstring date_added_string;
+ if (!value.GetString(kDateAddedKey, &date_added_string))
+ return false;
+ node->date_added_ =
+ Time::FromInternalValue(StringToInt64(date_added_string));
+
+ std::wstring type_string;
+ if (!value.GetString(kTypeKey, &type_string))
+ return false;
+
+ if (type_string != kTypeURL && type_string != kTypeFolder)
+ return false; // Unknown type.
+
+ if (type_string == kTypeURL) {
+ std::wstring url_string;
+ if (!value.GetString(kURLKey, &url_string))
+ return false;
+ node->SetURL(GURL(url_string));
+ node->type_ = history::StarredEntry::URL;
+ } else {
+ node->type_ = history::StarredEntry::USER_GROUP;
+
+ std::wstring last_modified_date;
+ if (!value.GetString(kDateModifiedKey, &last_modified_date))
+ return false;
+ node->date_group_modified_ =
+ Time::FromInternalValue(StringToInt64(last_modified_date));
+
+ Value* child_values;
+ if (!value.Get(kChildrenKey, &child_values))
+ return false;
+
+ if (child_values->GetType() != Value::TYPE_LIST)
+ return false;
+
+ if (!DecodeChildren(model, *static_cast<ListValue*>(child_values), node)) {
+ // There was an error in building the children. Delete all the children.
+ while (node->GetChildCount())
+ delete node->Remove(node->GetChildCount() - 1);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/chrome/browser/bookmark_codec.h b/chrome/browser/bookmark_codec.h
new file mode 100644
index 0000000..ffe51c2
--- /dev/null
+++ b/chrome/browser/bookmark_codec.h
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// BookmarkCodec is responsible for encoding and decoding the BookmarkBarModel
+// into JSON values. The encoded values are written to disk via the
+// BookmarkService.
+
+#ifndef CHROME_BROWSER_BOOKMARK_CODEC_H_
+
+#include "base/basictypes.h"
+
+class BookmarkBarModel;
+class BookmarkBarNode;
+class DictionaryValue;
+class ListValue;
+class Value;
+
+// BookmarkCodec is responsible for encoding/decoding bookmarks into JSON
+// values. BookmarkCodec is used by BookmarkService.
+
+class BookmarkCodec {
+ public:
+ BookmarkCodec() {}
+
+ // Encodes the model to a JSON value. It's up to the caller to delete the
+ // returned object.
+ Value* Encode(BookmarkBarModel* model);
+
+ // Decodes the previously encoded value to the specified model. Returns true
+ // on success, false otherwise. If there is an error (such as unexpected
+ // version) all children are removed from the bookmark bar and other folder
+ // nodes.
+ bool Decode(BookmarkBarModel* model, const Value& value);
+
+ private:
+ // Encodes node and all its children into a Value object and returns it.
+ // The caller takes ownership of the returned object.
+ Value* EncodeNode(BookmarkBarNode* node);
+
+ // Decodes the children of the specified node. Returns true on success.
+ bool DecodeChildren(BookmarkBarModel* model,
+ const ListValue& child_value_list,
+ BookmarkBarNode* parent);
+
+ // Decodes the supplied node from the supplied value. Child nodes are
+ // created appropriately by way of DecodeChildren.
+ bool DecodeNode(BookmarkBarModel* model,
+ const DictionaryValue& value,
+ BookmarkBarNode* node);
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkCodec);
+};
+
+#endif // CHROME_BROWSER_BOOKMARK_CODEC_H_
diff --git a/chrome/browser/bookmark_storage.cc b/chrome/browser/bookmark_storage.cc
new file mode 100644
index 0000000..e1fed29
--- /dev/null
+++ b/chrome/browser/bookmark_storage.cc
@@ -0,0 +1,185 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// NOTE: This class is currently unsed. The plan is to move bookmarks
+// out of the history db using this class and BookmarksCodec instead
+// (bug 1256202).
+
+#include "chrome/browser/bookmark_storage.h"
+
+#include "base/file_util.h"
+#include "base/json_writer.h"
+#include "base/message_loop.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/bookmark_codec.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/json_value_serializer.h"
+
+namespace {
+
+// Extension used for backup files (copy of main file created during startup).
+const wchar_t* const kBackupExtension = L"bak";
+
+// Extension for the temporary file. We write to the temp file than move to
+// kBookmarksFileName.
+const wchar_t* const kTmpExtension = L"tmp";
+
+// Name of file containing bookmarks.
+const wchar_t* const kBookmarksFileName = L"bookmarks.json";
+
+// How often we save.
+static const int kSaveDelayMS = 2500;
+
+class BookmarkStorageBackend :
+ public base::RefCountedThreadSafe<BookmarkStorageBackend> {
+ public:
+ explicit BookmarkStorageBackend(const std::wstring& path);
+
+ // Writes the specified value to disk. This takes ownership of |value| and
+ // deletes it when done.
+ void Write(Value* value);
+
+ // Reads the bookmarks from kBookmarksFileName. Notifies |service| with
+ // the results on the specified MessageLoop.
+ void Read(scoped_refptr<BookmarkStorage> service,
+ MessageLoop* message_loop);
+
+ private:
+ // Path we read/write to.
+ const std::wstring path_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BookmarkStorageBackend);
+};
+
+BookmarkStorageBackend::BookmarkStorageBackend(const std::wstring& path)
+ : path_(path) {
+ // Make a backup of the current file.
+ std::wstring backup_path = path;
+ file_util::ReplaceExtension(&backup_path, kBackupExtension);
+ file_util::CopyFile(path, backup_path);
+}
+
+void BookmarkStorageBackend::Write(Value* value) {
+ DCHECK(value);
+
+ // We own Value.
+ scoped_ptr<Value> value_ref(value);
+
+ std::string content;
+ JSONWriter::Write(value, true, &content);
+
+ // Write to a temp file, then rename.
+ std::wstring tmp_file = path_;
+ file_util::ReplaceExtension(&tmp_file, kTmpExtension);
+
+ int bytes_written = file_util::WriteFile(tmp_file, content.c_str(),
+ static_cast<int>(content.length()));
+ if (bytes_written != -1) {
+ MoveFileEx(tmp_file.c_str(), path_.c_str(),
+ MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
+ }
+}
+
+void BookmarkStorageBackend::Read(scoped_refptr<BookmarkStorage> service,
+ MessageLoop* message_loop) {
+ JSONFileValueSerializer serializer(path_);
+ Value* root = NULL;
+ serializer.Deserialize(&root);
+
+ // BookmarkStorage takes ownership of root.
+ message_loop->PostTask(FROM_HERE, NewRunnableMethod(
+ service.get(), &BookmarkStorage::LoadedBookmarks, root));
+}
+
+} // namespace
+
+BookmarkStorage::BookmarkStorage(Profile* profile, BookmarkBarModel* model)
+ : model_(model),
+#pragma warning(suppress: 4355) // Okay to pass "this" here.
+ save_factory_(this) {
+ std::wstring path = profile->GetPath();
+ file_util::AppendToPath(&path, kBookmarksFileName);
+ backend_ = new BookmarkStorageBackend(path);
+}
+
+void BookmarkStorage::LoadBookmarks() {
+ backend_thread()->message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Read,
+ scoped_refptr<BookmarkStorage>(this),
+ MessageLoop::current()));
+}
+
+void BookmarkStorage::ScheduleSave() {
+ if (save_factory_.empty()) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, save_factory_.NewRunnableMethod(&BookmarkStorage::SaveNow),
+ kSaveDelayMS);
+ }
+}
+
+void BookmarkStorage::BookmarkModelDeleted() {
+ if (!save_factory_.empty()) {
+ // There's a pending save. We need to save now as otherwise by the time
+ // SaveNow is invoked the model is gone.
+ save_factory_.RevokeAll();
+ SaveNow();
+ }
+ model_ = NULL;
+}
+
+void BookmarkStorage::LoadedBookmarks(Value* root_value) {
+ scoped_ptr<Value> value_ref(root_value);
+
+ if (model_) {
+ if (root_value) {
+ BookmarkCodec codec;
+ codec.Decode(model_, *root_value);
+ }
+ // TODO(sky): bug 1256202 need to invoke a method back on the model telling
+ // it all has loaded.
+ }
+}
+
+void BookmarkStorage::SaveNow() {
+ if (!model_ || !model_->IsLoaded()) {
+ // We should only get here if we have a valid model and it's finished
+ // loading.
+ NOTREACHED();
+ return;
+ }
+
+ BookmarkCodec codec;
+ Value* value = codec.Encode(model_);
+ // The backend deletes value in write.
+ backend_thread()->message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Write,
+ value));
+}
diff --git a/chrome/browser/bookmark_storage.h b/chrome/browser/bookmark_storage.h
new file mode 100644
index 0000000..8ba00d0
--- /dev/null
+++ b/chrome/browser/bookmark_storage.h
@@ -0,0 +1,95 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// BookmarkService handles reading/write the bookmark bar model. The
+// BookmarkBarModel uses the BookmarkService to load bookmarks from
+// disk, as well as notifying the BookmarkService every time the model
+// changes.
+//
+// Internally BookmarkService uses BookmarkCoded to do the actual read/write.
+
+// NOTE: This class is currently unsed. The plan is to move bookmarks
+// out of the history db using this class and BookmarksCodec instead
+// (bug 1256202).
+
+#ifndef CHROME_BROWSER_BOOKMARK_STORAGE_H_
+#define CHROME_BROWSER_BOOKMARK_STORAGE_H_
+
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "chrome/browser/browser_process.h"
+
+class BookmarkBarModel;
+class Profile;
+class Value;
+
+namespace {
+class BookmarkStorageBackend;
+}
+
+class BookmarkStorage : public base::RefCountedThreadSafe<BookmarkStorage> {
+ friend class BookmarkStorageBackend;
+
+ public:
+ // Creates a BookmarkStorage for the specified model
+ BookmarkStorage(Profile* profile, BookmarkBarModel* model);
+
+ // Loads the bookmarks into the model, notifying the model when done.
+ void LoadBookmarks();
+
+ // Schedules saving the bookmark bar model to disk.
+ void ScheduleSave();
+
+ // Notification the bookmark bar model is going to be deleted. If there is
+ // a pending save, it is saved immediately.
+ void BookmarkModelDeleted();
+
+ private:
+ // Callback from backend with the results of the bookmark file.
+ void LoadedBookmarks(Value* root_value);
+
+ // Schedules a save on the backend thread.
+ void SaveNow();
+
+ // Returns the thread the backend is run on.
+ Thread* backend_thread() { return g_browser_process->file_thread(); }
+
+ // The model. The model is NULL once BookmarkModelDeleted has been invoked.
+ BookmarkBarModel* model_;
+
+ // Used to delay saves.
+ ScopedRunnableMethodFactory<BookmarkStorage> save_factory_;
+
+ // The backend handles actual reading/writing to disk.
+ scoped_refptr<BookmarkStorageBackend> backend_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkStorage);
+};
+
+#endif // CHROME_BROWSER_BOOKMARK_STORAGE_H_