summaryrefslogtreecommitdiffstats
path: root/sync/internal_api/public
diff options
context:
space:
mode:
Diffstat (limited to 'sync/internal_api/public')
-rw-r--r--sync/internal_api/public/DEPS3
-rw-r--r--sync/internal_api/public/base_node.h255
-rw-r--r--sync/internal_api/public/base_transaction.h54
-rw-r--r--sync/internal_api/public/change_record.h67
-rw-r--r--sync/internal_api/public/change_record_unittest.cc137
-rw-r--r--sync/internal_api/public/configure_reason.h36
-rw-r--r--sync/internal_api/public/http_post_provider_factory.h34
-rw-r--r--sync/internal_api/public/http_post_provider_interface.h67
-rw-r--r--sync/internal_api/public/read_node.h66
-rw-r--r--sync/internal_api/public/read_transaction.h46
-rw-r--r--sync/internal_api/public/sync_manager.h580
-rw-r--r--sync/internal_api/public/test/test_user_share.h68
-rw-r--r--sync/internal_api/public/user_share.h36
-rw-r--r--sync/internal_api/public/util/experiments.h28
-rw-r--r--sync/internal_api/public/util/immutable.h262
-rw-r--r--sync/internal_api/public/util/immutable_unittest.cc250
-rw-r--r--sync/internal_api/public/util/report_unrecoverable_error_function.h19
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_handler.h30
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_info.cc44
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_info.h41
-rw-r--r--sync/internal_api/public/util/weak_handle.cc36
-rw-r--r--sync/internal_api/public/util/weak_handle.h379
-rw-r--r--sync/internal_api/public/util/weak_handle_unittest.cc326
-rw-r--r--sync/internal_api/public/write_node.h203
-rw-r--r--sync/internal_api/public/write_transaction.h57
25 files changed, 3124 insertions, 0 deletions
diff --git a/sync/internal_api/public/DEPS b/sync/internal_api/public/DEPS
index 2a51ace..a483090 100644
--- a/sync/internal_api/public/DEPS
+++ b/sync/internal_api/public/DEPS
@@ -2,4 +2,7 @@ include_rules = [
"-sync",
"+sync/internal_api/public",
"+sync/protocol",
+
+ # TODO(tim): Remove. Bug 131130
+ "+sync/util/cryptographer.h"
]
diff --git a/sync/internal_api/public/base_node.h b/sync/internal_api/public/base_node.h
new file mode 100644
index 0000000..87de41e
--- /dev/null
+++ b/sync/internal_api/public/base_node.h
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_BASE_NODE_H_
+#define SYNC_INTERNAL_API_PUBLIC_BASE_NODE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "sync/internal_api/public/syncable/model_type.h"
+#include "sync/protocol/sync.pb.h"
+
+// Forward declarations of internal class types so that sync API objects
+// may have opaque pointers to these types.
+namespace base {
+class DictionaryValue;
+}
+
+namespace syncable {
+class BaseTransaction;
+class Entry;
+}
+
+namespace sync_pb {
+class AppSpecifics;
+class AutofillSpecifics;
+class AutofillProfileSpecifics;
+class BookmarkSpecifics;
+class EntitySpecifics;
+class ExtensionSpecifics;
+class SessionSpecifics;
+class NigoriSpecifics;
+class PreferenceSpecifics;
+class PasswordSpecificsData;
+class ThemeSpecifics;
+class TypedUrlSpecifics;
+}
+
+namespace sync_api {
+
+class BaseTransaction;
+
+// A valid BaseNode will never have an ID of zero.
+static const int64 kInvalidId = 0;
+
+// BaseNode wraps syncable::Entry, and corresponds to a single object's state.
+// This, like syncable::Entry, is intended for use on the stack. A valid
+// transaction is necessary to create a BaseNode or any of its children.
+// Unlike syncable::Entry, a sync API BaseNode is identified primarily by its
+// int64 metahandle, which we call an ID here.
+class BaseNode {
+ public:
+ // Enumerates the possible outcomes of trying to initialize a sync node.
+ enum InitByLookupResult {
+ INIT_OK,
+ // Could not find an entry matching the lookup criteria.
+ INIT_FAILED_ENTRY_NOT_GOOD,
+ // Found an entry, but it is already deleted.
+ INIT_FAILED_ENTRY_IS_DEL,
+ // Found an entry, but was unable to decrypt.
+ INIT_FAILED_DECRYPT_IF_NECESSARY,
+ // A precondition was not met for calling init, such as legal input
+ // arguments.
+ INIT_FAILED_PRECONDITION,
+ };
+
+ // All subclasses of BaseNode must provide a way to initialize themselves by
+ // doing an ID lookup. Returns false on failure. An invalid or deleted
+ // ID will result in failure.
+ virtual InitByLookupResult InitByIdLookup(int64 id) = 0;
+
+ // All subclasses of BaseNode must also provide a way to initialize themselves
+ // by doing a client tag lookup. Returns false on failure. A deleted node
+ // will return FALSE.
+ virtual InitByLookupResult InitByClientTagLookup(
+ syncable::ModelType model_type,
+ const std::string& tag) = 0;
+
+ // Each object is identified by a 64-bit id (internally, the syncable
+ // metahandle). These ids are strictly local handles. They will persist
+ // on this client, but the same object on a different client may have a
+ // different ID value.
+ virtual int64 GetId() const;
+
+ // Returns the modification time of the object.
+ const base::Time& GetModificationTime() const;
+
+ // Nodes are hierarchically arranged into a single-rooted tree.
+ // InitByRootLookup on ReadNode allows access to the root. GetParentId is
+ // how you find a node's parent.
+ int64 GetParentId() const;
+
+ // Nodes are either folders or not. This corresponds to the IS_DIR property
+ // of syncable::Entry.
+ bool GetIsFolder() const;
+
+ // Returns the title of the object.
+ // Uniqueness of the title is not enforced on siblings -- it is not an error
+ // for two children to share a title.
+ std::string GetTitle() const;
+
+ // Returns the model type of this object. The model type is set at node
+ // creation time and is expected never to change.
+ syncable::ModelType GetModelType() const;
+
+ // Getter specific to the BOOKMARK datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == BOOKMARK.
+ const sync_pb::BookmarkSpecifics& GetBookmarkSpecifics() const;
+
+ // Legacy, bookmark-specific getter that wraps GetBookmarkSpecifics() above.
+ // Returns the URL of a bookmark object.
+ // TODO(ncarter): Remove this datatype-specific accessor.
+ GURL GetURL() const;
+
+ // Legacy, bookmark-specific getter that wraps GetBookmarkSpecifics() above.
+ // Fill in a vector with the byte data of this node's favicon. Assumes
+ // that the node is a bookmark.
+ // Favicons are expected to be PNG images, and though no verification is
+ // done on the syncapi client of this, the server may reject favicon updates
+ // that are invalid for whatever reason.
+ // TODO(ncarter): Remove this datatype-specific accessor.
+ void GetFaviconBytes(std::vector<unsigned char>* output) const;
+
+ // Getter specific to the APPS datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == APPS.
+ const sync_pb::AppSpecifics& GetAppSpecifics() const;
+
+ // Getter specific to the AUTOFILL datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == AUTOFILL.
+ const sync_pb::AutofillSpecifics& GetAutofillSpecifics() const;
+
+ virtual const sync_pb::AutofillProfileSpecifics&
+ GetAutofillProfileSpecifics() const;
+
+ // Getter specific to the NIGORI datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == NIGORI.
+ const sync_pb::NigoriSpecifics& GetNigoriSpecifics() const;
+
+ // Getter specific to the PASSWORD datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == PASSWORD.
+ const sync_pb::PasswordSpecificsData& GetPasswordSpecifics() const;
+
+ // Getter specific to the PREFERENCE datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == PREFERENCE.
+ const sync_pb::PreferenceSpecifics& GetPreferenceSpecifics() const;
+
+ // Getter specific to the THEME datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == THEME.
+ const sync_pb::ThemeSpecifics& GetThemeSpecifics() const;
+
+ // Getter specific to the TYPED_URLS datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == TYPED_URLS.
+ const sync_pb::TypedUrlSpecifics& GetTypedUrlSpecifics() const;
+
+ // Getter specific to the EXTENSIONS datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == EXTENSIONS.
+ const sync_pb::ExtensionSpecifics& GetExtensionSpecifics() const;
+
+ // Getter specific to the SESSIONS datatype. Returns protobuf
+ // data. Can only be called if GetModelType() == SESSIONS.
+ const sync_pb::SessionSpecifics& GetSessionSpecifics() const;
+
+ const sync_pb::EntitySpecifics& GetEntitySpecifics() const;
+
+ // Returns the local external ID associated with the node.
+ int64 GetExternalId() const;
+
+ // Returns true iff this node has children.
+ bool HasChildren() const;
+
+ // Return the ID of the node immediately before this in the sibling order.
+ // For the first node in the ordering, return 0.
+ int64 GetPredecessorId() const;
+
+ // Return the ID of the node immediately after this in the sibling order.
+ // For the last node in the ordering, return 0.
+ int64 GetSuccessorId() const;
+
+ // Return the ID of the first child of this node. If this node has no
+ // children, return 0.
+ int64 GetFirstChildId() const;
+
+ // These virtual accessors provide access to data members of derived classes.
+ virtual const syncable::Entry* GetEntry() const = 0;
+ virtual const BaseTransaction* GetTransaction() const = 0;
+
+ // Dumps a summary of node info into a DictionaryValue and returns it.
+ // Transfers ownership of the DictionaryValue to the caller.
+ base::DictionaryValue* GetSummaryAsValue() const;
+
+ // Dumps all node details into a DictionaryValue and returns it.
+ // Transfers ownership of the DictionaryValue to the caller.
+ base::DictionaryValue* GetDetailsAsValue() const;
+
+ protected:
+ BaseNode();
+ virtual ~BaseNode();
+ // The server has a size limit on client tags, so we generate a fixed length
+ // hash locally. This also ensures that ModelTypes have unique namespaces.
+ static std::string GenerateSyncableHash(syncable::ModelType model_type,
+ const std::string& client_tag);
+
+ // Determines whether part of the entry is encrypted, and if so attempts to
+ // decrypt it. Unless decryption is necessary and fails, this will always
+ // return |true|. If the contents are encrypted, the decrypted data will be
+ // stored in |unencrypted_data_|.
+ // This method is invoked once when the BaseNode is initialized.
+ bool DecryptIfNecessary();
+
+ // Returns the unencrypted specifics associated with |entry|. If |entry| was
+ // not encrypted, it directly returns |entry|'s EntitySpecifics. Otherwise,
+ // returns |unencrypted_data_|.
+ const sync_pb::EntitySpecifics& GetUnencryptedSpecifics(
+ const syncable::Entry* entry) const;
+
+ // Copy |specifics| into |unencrypted_data_|.
+ void SetUnencryptedSpecifics(const sync_pb::EntitySpecifics& specifics);
+
+ private:
+ // Have to friend the test class as well to allow member functions to access
+ // protected/private BaseNode methods.
+ friend class SyncManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(SyncApiTest, GenerateSyncableHash);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, UpdateEntryWithEncryption);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest,
+ UpdatePasswordSetEntitySpecificsNoChange);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, UpdatePasswordSetPasswordSpecifics);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, UpdatePasswordNewPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, UpdatePasswordReencryptEverything);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, SetBookmarkTitle);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, SetBookmarkTitleWithEncryption);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, SetNonBookmarkTitle);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, SetNonBookmarkTitleWithEncryption);
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, SetPreviouslyEncryptedSpecifics);
+
+ void* operator new(size_t size); // Node is meant for stack use only.
+
+ // A holder for the unencrypted data stored in an encrypted node.
+ sync_pb::EntitySpecifics unencrypted_data_;
+
+ // Same as |unencrypted_data_|, but for legacy password encryption.
+ scoped_ptr<sync_pb::PasswordSpecificsData> password_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseNode);
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_BASE_NODE_H_
diff --git a/sync/internal_api/public/base_transaction.h b/sync/internal_api/public/base_transaction.h
new file mode 100644
index 0000000..77e102a
--- /dev/null
+++ b/sync/internal_api/public/base_transaction.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_BASE_TRANSACTION_H_
+#define SYNC_INTERNAL_API_PUBLIC_BASE_TRANSACTION_H_
+#pragma once
+
+#include "sync/internal_api/public/user_share.h"
+
+#include "sync/util/cryptographer.h"
+
+namespace syncable {
+class BaseTransaction;
+class Directory;
+}
+
+namespace sync_api {
+
+// Sync API's BaseTransaction, ReadTransaction, and WriteTransaction allow for
+// batching of several read and/or write operations. The read and write
+// operations are performed by creating ReadNode and WriteNode instances using
+// the transaction. These transaction classes wrap identically named classes in
+// syncable, and are used in a similar way. Unlike syncable::BaseTransaction,
+// whose construction requires an explicit syncable::Directory, a sync
+// API BaseTransaction is created from a UserShare object.
+class BaseTransaction {
+ public:
+ // Provide access to the underlying syncable.h objects from BaseNode.
+ virtual syncable::BaseTransaction* GetWrappedTrans() const = 0;
+ browser_sync::Cryptographer* GetCryptographer() const;
+
+ syncable::Directory* GetDirectory() const {
+ return directory_;
+ }
+
+ protected:
+ explicit BaseTransaction(UserShare* share);
+ virtual ~BaseTransaction();
+
+ BaseTransaction() : directory_(NULL) { }
+
+ private:
+ syncable::Directory* directory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseTransaction);
+};
+
+syncable::ModelTypeSet GetEncryptedTypes(
+ const sync_api::BaseTransaction* trans);
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_BASE_TRANSACTION_H_
diff --git a/sync/internal_api/public/change_record.h b/sync/internal_api/public/change_record.h
new file mode 100644
index 0000000..f522a3b
--- /dev/null
+++ b/sync/internal_api/public/change_record.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_PUBLIC_API_CHANGE_RECORD_H_
+#define SYNC_INTERNAL_PUBLIC_API_CHANGE_RECORD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "sync/internal_api/public/util/immutable.h"
+#include "sync/protocol/password_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
+
+namespace base {
+class DictionaryValue;
+} // namespace base
+
+namespace sync_api {
+
+// TODO(zea): One day get passwords playing nicely with the rest of encryption
+// and get rid of this.
+class ExtraPasswordChangeRecordData {
+ public:
+ ExtraPasswordChangeRecordData();
+ explicit ExtraPasswordChangeRecordData(
+ const sync_pb::PasswordSpecificsData& data);
+ virtual ~ExtraPasswordChangeRecordData();
+
+ // Transfers ownership of the DictionaryValue to the caller.
+ virtual base::DictionaryValue* ToValue() const;
+
+ const sync_pb::PasswordSpecificsData& unencrypted() const;
+ private:
+ sync_pb::PasswordSpecificsData unencrypted_;
+};
+
+// ChangeRecord indicates a single item that changed as a result of a sync
+// operation. This gives the sync id of the node that changed, and the type
+// of change. To get the actual property values after an ADD or UPDATE, the
+// client should get the node with InitByIdLookup(), using the provided id.
+struct ChangeRecord {
+ enum Action {
+ ACTION_ADD,
+ ACTION_DELETE,
+ ACTION_UPDATE,
+ };
+ ChangeRecord();
+ ~ChangeRecord();
+
+ // Transfers ownership of the DictionaryValue to the caller.
+ base::DictionaryValue* ToValue() const;
+
+ int64 id;
+ Action action;
+ sync_pb::EntitySpecifics specifics;
+ linked_ptr<ExtraPasswordChangeRecordData> extra;
+};
+
+typedef std::vector<ChangeRecord> ChangeRecordList;
+
+typedef browser_sync::Immutable<ChangeRecordList> ImmutableChangeRecordList;
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_CHANGE_RECORD_H_
diff --git a/sync/internal_api/public/change_record_unittest.cc b/sync/internal_api/public/change_record_unittest.cc
new file mode 100644
index 0000000..4fb1f01
--- /dev/null
+++ b/sync/internal_api/public/change_record_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/internal_api/public/change_record.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/test/values_test_util.h"
+#include "base/values.h"
+#include "sync/protocol/extension_specifics.pb.h"
+#include "sync/protocol/proto_value_conversions.h"
+#include "sync/protocol/sync.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_api {
+namespace {
+
+using base::ExpectDictDictionaryValue;
+using base::ExpectDictStringValue;
+using testing::Invoke;
+using testing::StrictMock;
+
+class ChangeRecordTest : public testing::Test {};
+
+void ExpectChangeRecordActionValue(ChangeRecord::Action expected_value,
+ const base::DictionaryValue& value,
+ const std::string& key) {
+ std::string str_value;
+ EXPECT_TRUE(value.GetString(key, &str_value));
+ switch (expected_value) {
+ case ChangeRecord::ACTION_ADD:
+ EXPECT_EQ("Add", str_value);
+ break;
+ case ChangeRecord::ACTION_UPDATE:
+ EXPECT_EQ("Update", str_value);
+ break;
+ case ChangeRecord::ACTION_DELETE:
+ EXPECT_EQ("Delete", str_value);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void CheckChangeRecordValue(
+ const ChangeRecord& record,
+ const base::DictionaryValue& value) {
+ ExpectChangeRecordActionValue(record.action, value, "action");
+ ExpectDictStringValue(base::Int64ToString(record.id), value, "id");
+ if (record.action == ChangeRecord::ACTION_DELETE) {
+ scoped_ptr<base::DictionaryValue> expected_extra_value;
+ if (record.extra.get()) {
+ expected_extra_value.reset(record.extra->ToValue());
+ }
+ base::Value* extra_value = NULL;
+ EXPECT_EQ(record.extra.get() != NULL,
+ value.Get("extra", &extra_value));
+ EXPECT_TRUE(Value::Equals(extra_value, expected_extra_value.get()));
+
+ scoped_ptr<DictionaryValue> expected_specifics_value(
+ browser_sync::EntitySpecificsToValue(record.specifics));
+ ExpectDictDictionaryValue(*expected_specifics_value,
+ value, "specifics");
+ }
+}
+
+class MockExtraChangeRecordData
+ : public ExtraPasswordChangeRecordData {
+ public:
+ MOCK_CONST_METHOD0(ToValue, DictionaryValue*());
+};
+
+TEST_F(ChangeRecordTest, ChangeRecordToValue) {
+ sync_pb::EntitySpecifics old_specifics;
+ old_specifics.mutable_extension()->set_id("old");
+ sync_pb::EntitySpecifics new_specifics;
+ old_specifics.mutable_extension()->set_id("new");
+
+ const int64 kTestId = 5;
+
+ // Add
+ {
+ ChangeRecord record;
+ record.action = ChangeRecord::ACTION_ADD;
+ record.id = kTestId;
+ record.specifics = old_specifics;
+ record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
+ scoped_ptr<DictionaryValue> value(record.ToValue());
+ CheckChangeRecordValue(record, *value);
+ }
+
+ // Update
+ {
+ ChangeRecord record;
+ record.action = ChangeRecord::ACTION_UPDATE;
+ record.id = kTestId;
+ record.specifics = old_specifics;
+ record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
+ scoped_ptr<DictionaryValue> value(record.ToValue());
+ CheckChangeRecordValue(record, *value);
+ }
+
+ // Delete (no extra)
+ {
+ ChangeRecord record;
+ record.action = ChangeRecord::ACTION_DELETE;
+ record.id = kTestId;
+ record.specifics = old_specifics;
+ scoped_ptr<DictionaryValue> value(record.ToValue());
+ CheckChangeRecordValue(record, *value);
+ }
+
+ // Delete (with extra)
+ {
+ ChangeRecord record;
+ record.action = ChangeRecord::ACTION_DELETE;
+ record.id = kTestId;
+ record.specifics = old_specifics;
+
+ DictionaryValue extra_value;
+ extra_value.SetString("foo", "bar");
+ scoped_ptr<StrictMock<MockExtraChangeRecordData> > extra(
+ new StrictMock<MockExtraChangeRecordData>());
+ EXPECT_CALL(*extra, ToValue()).Times(2).WillRepeatedly(
+ Invoke(&extra_value, &DictionaryValue::DeepCopy));
+
+ record.extra.reset(extra.release());
+ scoped_ptr<DictionaryValue> value(record.ToValue());
+ CheckChangeRecordValue(record, *value);
+ }
+}
+
+} // namespace
+} // namespace sync_api
diff --git a/sync/internal_api/public/configure_reason.h b/sync/internal_api/public/configure_reason.h
new file mode 100644
index 0000000..37916ac
--- /dev/null
+++ b/sync/internal_api/public/configure_reason.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_CONFIGURE_REASON_H_
+#define SYNC_INTERNAL_API_PUBLIC_CONFIGURE_REASON_H_
+#pragma once
+
+namespace sync_api {
+
+// Note: This should confirm with the enums in sync.proto for
+// GetUpdatesCallerInfo. They will have 1:1 mapping but this will only map
+// to a subset of the GetUpdatesCallerInfo enum values.
+enum ConfigureReason {
+ // We should never be here during actual configure. This is for setting
+ // default values.
+ CONFIGURE_REASON_UNKNOWN,
+
+ // The client is configuring because the user opted to sync a different set
+ // of datatypes.
+ CONFIGURE_REASON_RECONFIGURATION,
+
+ // The client is configuring because the client is being asked to migrate.
+ CONFIGURE_REASON_MIGRATION,
+
+ // Setting up sync performs an initial config to download NIGORI data, and
+ // also a config to download initial data once the user selects types.
+ CONFIGURE_REASON_NEW_CLIENT,
+
+ // A new datatype is enabled for syncing due to a client upgrade.
+ CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE,
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_CONFIGURE_REASON_H_
diff --git a/sync/internal_api/public/http_post_provider_factory.h b/sync/internal_api/public/http_post_provider_factory.h
new file mode 100644
index 0000000..53d576b
--- /dev/null
+++ b/sync/internal_api/public/http_post_provider_factory.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_FACTORY_H_
+#define SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_FACTORY_H_
+#pragma once
+
+namespace sync_api {
+
+class HttpPostProviderInterface;
+
+// A factory to create HttpPostProviders to hide details about the
+// implementations and dependencies.
+// A factory instance itself should be owned by whomever uses it to create
+// HttpPostProviders.
+class HttpPostProviderFactory {
+ public:
+ virtual ~HttpPostProviderFactory() {}
+
+ // Obtain a new HttpPostProviderInterface instance, owned by caller.
+ virtual HttpPostProviderInterface* Create() = 0;
+
+ // When the interface is no longer needed (ready to be cleaned up), clients
+ // must call Destroy().
+ // This allows actual HttpPostProvider subclass implementations to be
+ // reference counted, which is useful if a particular implementation uses
+ // multiple threads to serve network requests.
+ virtual void Destroy(HttpPostProviderInterface* http) = 0;
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_FACTORY_H_
diff --git a/sync/internal_api/public/http_post_provider_interface.h b/sync/internal_api/public/http_post_provider_interface.h
new file mode 100644
index 0000000..5aa201a
--- /dev/null
+++ b/sync/internal_api/public/http_post_provider_interface.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_INTERFACE_H_
+#define SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_INTERFACE_H_
+#pragma once
+
+#include <string>
+
+namespace sync_api {
+
+// An interface the embedding application (e.g. Chromium) implements to provide
+// required HTTP POST functionality to the syncer backend. This interface is
+// designed for one-time use. You create one, use it, and create another if you
+// want to make a subsequent POST.
+class HttpPostProviderInterface {
+ public:
+ // Use specified user agent string when POSTing. If not called a default UA
+ // may be used.
+ virtual void SetUserAgent(const char* user_agent) = 0;
+
+ // Add additional headers to the request.
+ virtual void SetExtraRequestHeaders(const char* headers) = 0;
+
+ // Set the URL to POST to.
+ virtual void SetURL(const char* url, int port) = 0;
+
+ // Set the type, length and content of the POST payload.
+ // |content_type| is a null-terminated MIME type specifier.
+ // |content| is a data buffer; Do not interpret as a null-terminated string.
+ // |content_length| is the total number of chars in |content|. It is used to
+ // assign/copy |content| data.
+ virtual void SetPostPayload(const char* content_type,
+ int content_length,
+ const char* content) = 0;
+
+ // Returns true if the URL request succeeded. If the request failed,
+ // error() may be non-zero and hence contain more information.
+ virtual bool MakeSynchronousPost(int* error_code, int* response_code) = 0;
+
+ // Get the length of the content returned in the HTTP response.
+ // This does not count the trailing null-terminating character returned
+ // by GetResponseContent, so it is analogous to calling string.length.
+ virtual int GetResponseContentLength() const = 0;
+
+ // Get the content returned in the HTTP response.
+ // This is a null terminated string of characters.
+ // Value should be copied.
+ virtual const char* GetResponseContent() const = 0;
+
+ // Get the value of a header returned in the HTTP response.
+ // If the header is not present, returns the empty string.
+ virtual const std::string GetResponseHeaderValue(
+ const std::string& name) const = 0;
+
+ // Abandon any pending POST and unblock caller in MakeSynchronousPost.
+ // This must be safe to call from any thread.
+ virtual void Abort() = 0;
+
+ protected:
+ virtual ~HttpPostProviderInterface() {}
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_HTTP_POST_PROVIDER_INTERFACE_H_
diff --git a/sync/internal_api/public/read_node.h b/sync/internal_api/public/read_node.h
new file mode 100644
index 0000000..f57b577
--- /dev/null
+++ b/sync/internal_api/public/read_node.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_READ_NODE_H_
+#define SYNC_INTERNAL_API_PUBLIC_READ_NODE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "sync/internal_api/public/base_node.h"
+#include "sync/internal_api/public/syncable/model_type.h"
+
+namespace sync_api {
+
+// ReadNode wraps a syncable::Entry to provide the functionality of a
+// read-only BaseNode.
+class ReadNode : public BaseNode {
+ public:
+ // Create an unpopulated ReadNode on the given transaction. Call some flavor
+ // of Init to populate the ReadNode with a database entry.
+ explicit ReadNode(const BaseTransaction* transaction);
+ virtual ~ReadNode();
+
+ // A client must use one (and only one) of the following Init variants to
+ // populate the node.
+
+ // BaseNode implementation.
+ virtual InitByLookupResult InitByIdLookup(int64 id) OVERRIDE;
+ virtual InitByLookupResult InitByClientTagLookup(
+ syncable::ModelType model_type,
+ const std::string& tag) OVERRIDE;
+
+ // There is always a root node, so this can't fail. The root node is
+ // never mutable, so root lookup is only possible on a ReadNode.
+ void InitByRootLookup();
+
+ // Each server-created permanent node is tagged with a unique string.
+ // Look up the node with the particular tag. If it does not exist,
+ // return false.
+ InitByLookupResult InitByTagLookup(const std::string& tag);
+
+ // Implementation of BaseNode's abstract virtual accessors.
+ virtual const syncable::Entry* GetEntry() const OVERRIDE;
+ virtual const BaseTransaction* GetTransaction() const OVERRIDE;
+
+ protected:
+ ReadNode();
+
+ private:
+ void* operator new(size_t size); // Node is meant for stack use only.
+
+ // The underlying syncable object which this class wraps.
+ syncable::Entry* entry_;
+
+ // The sync API transaction that is the parent of this node.
+ const BaseTransaction* transaction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadNode);
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_READ_NODE_H_
diff --git a/sync/internal_api/public/read_transaction.h b/sync/internal_api/public/read_transaction.h
new file mode 100644
index 0000000..fd35373
--- /dev/null
+++ b/sync/internal_api/public/read_transaction.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_READ_TRANSACTION_H_
+#define SYNC_INTERNAL_API_PUBLIC_READ_TRANSACTION_H_
+
+#include "base/compiler_specific.h"
+#include "sync/internal_api/public/base_transaction.h"
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace sync_api {
+
+struct UserShare;
+
+// Sync API's ReadTransaction is a read-only BaseTransaction. It wraps
+// a syncable::ReadTransaction.
+class ReadTransaction : public BaseTransaction {
+ public:
+ // Start a new read-only transaction on the specified repository.
+ ReadTransaction(const tracked_objects::Location& from_here,
+ UserShare* share);
+
+ // Resume the middle of a transaction. Will not close transaction.
+ ReadTransaction(UserShare* share, syncable::BaseTransaction* trans);
+
+ virtual ~ReadTransaction();
+
+ // BaseTransaction override.
+ virtual syncable::BaseTransaction* GetWrappedTrans() const OVERRIDE;
+ private:
+ void* operator new(size_t size); // Transaction is meant for stack use only.
+
+ // The underlying syncable object which this class wraps.
+ syncable::BaseTransaction* transaction_;
+ bool close_transaction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadTransaction);
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_READ_TRANSACTION_H_
diff --git a/sync/internal_api/public/sync_manager.h b/sync/internal_api/public/sync_manager.h
new file mode 100644
index 0000000..f4a42e3
--- /dev/null
+++ b/sync/internal_api/public/sync_manager.h
@@ -0,0 +1,580 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_
+#define SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "sync/internal_api/public/change_record.h"
+#include "sync/internal_api/public/configure_reason.h"
+#include "sync/internal_api/public/engine/model_safe_worker.h"
+#include "sync/internal_api/public/engine/sync_status.h"
+#include "sync/internal_api/public/syncable/model_type.h"
+#include "sync/internal_api/public/util/report_unrecoverable_error_function.h"
+#include "sync/internal_api/public/util/unrecoverable_error_handler.h"
+#include "sync/internal_api/public/util/weak_handle.h"
+#include "sync/protocol/sync_protocol_error.h"
+
+namespace browser_sync {
+struct ConfigurationParams;
+class Encryptor;
+struct Experiments;
+class ExtensionsActivityMonitor;
+class JsBackend;
+class JsEventHandler;
+class SyncScheduler;
+
+namespace sessions {
+class SyncSessionSnapshot;
+} // namespace sessions
+} // namespace browser_sync
+
+namespace sync_notifier {
+class SyncNotifier;
+} // namespace sync_notifier
+
+namespace sync_pb {
+class EncryptedData;
+} // namespace sync_pb
+
+namespace sync_api {
+
+class BaseTransaction;
+class HttpPostProviderFactory;
+struct UserShare;
+
+// Used by SyncManager::OnConnectionStatusChange().
+enum ConnectionStatus {
+ CONNECTION_OK,
+ CONNECTION_AUTH_ERROR,
+ CONNECTION_SERVER_ERROR
+};
+
+// Reasons due to which browser_sync::Cryptographer might require a passphrase.
+enum PassphraseRequiredReason {
+ REASON_PASSPHRASE_NOT_REQUIRED = 0, // Initial value.
+ REASON_ENCRYPTION = 1, // The cryptographer requires a
+ // passphrase for its first attempt at
+ // encryption. Happens only during
+ // migration or upgrade.
+ REASON_DECRYPTION = 2, // The cryptographer requires a
+ // passphrase for its first attempt at
+ // decryption.
+};
+
+
+// Contains everything needed to talk to and identify a user account.
+struct SyncCredentials {
+ std::string email;
+ std::string sync_token;
+};
+
+// SyncManager encapsulates syncable::Directory and serves as the parent of all
+// other objects in the sync API. If multiple threads interact with the same
+// local sync repository (i.e. the same sqlite database), they should share a
+// single SyncManager instance. The caller should typically create one
+// SyncManager for the lifetime of a user session.
+//
+// Unless stated otherwise, all methods of SyncManager should be called on the
+// same thread.
+class SyncManager {
+ public:
+ // SyncInternal contains the implementation of SyncManager, while abstracting
+ // internal types from clients of the interface.
+ class SyncInternal;
+
+ // An interface the embedding application implements to be notified
+ // on change events. Note that these methods may be called on *any*
+ // thread.
+ class ChangeDelegate {
+ public:
+ // Notify the delegate that changes have been applied to the sync model.
+ //
+ // This will be invoked on the same thread as on which ApplyChanges was
+ // called. |changes| is an array of size |change_count|, and contains the
+ // ID of each individual item that was changed. |changes| exists only for
+ // the duration of the call. If items of multiple data types change at
+ // the same time, this method is invoked once per data type and |changes|
+ // is restricted to items of the ModelType indicated by |model_type|.
+ // Because the observer is passed a |trans|, the observer can assume a
+ // read lock on the sync model that will be released after the function
+ // returns.
+ //
+ // The SyncManager constructs |changes| in the following guaranteed order:
+ //
+ // 1. Deletions, from leaves up to parents.
+ // 2. Updates to existing items with synced parents & predecessors.
+ // 3. New items with synced parents & predecessors.
+ // 4. Items with parents & predecessors in |changes|.
+ // 5. Repeat #4 until all items are in |changes|.
+ //
+ // Thus, an implementation of OnChangesApplied should be able to
+ // process the change records in the order without having to worry about
+ // forward dependencies. But since deletions come before reparent
+ // operations, a delete may temporarily orphan a node that is
+ // updated later in the list.
+ virtual void OnChangesApplied(
+ syncable::ModelType model_type,
+ const BaseTransaction* trans,
+ const ImmutableChangeRecordList& changes) = 0;
+
+ // OnChangesComplete gets called when the TransactionComplete event is
+ // posted (after OnChangesApplied finishes), after the transaction lock
+ // and the change channel mutex are released.
+ //
+ // The purpose of this function is to support processors that require
+ // split-transactions changes. For example, if a model processor wants to
+ // perform blocking I/O due to a change, it should calculate the changes
+ // while holding the transaction lock (from within OnChangesApplied), buffer
+ // those changes, let the transaction fall out of scope, and then commit
+ // those changes from within OnChangesComplete (postponing the blocking
+ // I/O to when it no longer holds any lock).
+ virtual void OnChangesComplete(syncable::ModelType model_type) = 0;
+
+ protected:
+ virtual ~ChangeDelegate();
+ };
+
+ // Like ChangeDelegate, except called only on the sync thread and
+ // not while a transaction is held. For objects that want to know
+ // when changes happen, but don't need to process them.
+ class ChangeObserver {
+ public:
+ // Ids referred to in |changes| may or may not be in the write
+ // transaction specified by |write_transaction_id|. If they're
+ // not, that means that the node didn't actually change, but we
+ // marked them as changed for some other reason (e.g., siblings of
+ // re-ordered nodes).
+ //
+ // TODO(sync, long-term): Ideally, ChangeDelegate/Observer would
+ // be passed a transformed version of EntryKernelMutation instead
+ // of a transaction that would have to be used to look up the
+ // changed nodes. That is, ChangeDelegate::OnChangesApplied()
+ // would still be called under the transaction, but all the needed
+ // data will be passed down.
+ //
+ // Even more ideally, we would have sync semantics such that we'd
+ // be able to apply changes without being under a transaction.
+ // But that's a ways off...
+ virtual void OnChangesApplied(
+ syncable::ModelType model_type,
+ int64 write_transaction_id,
+ const ImmutableChangeRecordList& changes) = 0;
+
+ virtual void OnChangesComplete(syncable::ModelType model_type) = 0;
+
+ protected:
+ virtual ~ChangeObserver();
+ };
+
+ // An interface the embedding application implements to receive
+ // notifications from the SyncManager. Register an observer via
+ // SyncManager::AddObserver. All methods are called only on the
+ // sync thread.
+ class Observer {
+ public:
+ // A round-trip sync-cycle took place and the syncer has resolved any
+ // conflicts that may have arisen.
+ virtual void OnSyncCycleCompleted(
+ const browser_sync::sessions::SyncSessionSnapshot& snapshot) = 0;
+
+ // Called when the status of the connection to the sync server has
+ // changed.
+ virtual void OnConnectionStatusChange(ConnectionStatus status) = 0;
+
+ // Called when a new auth token is provided by the sync server.
+ virtual void OnUpdatedToken(const std::string& token) = 0;
+
+ // Called when user interaction is required to obtain a valid passphrase.
+ // - If the passphrase is required for encryption, |reason| will be
+ // REASON_ENCRYPTION.
+ // - If the passphrase is required for the decryption of data that has
+ // already been encrypted, |reason| will be REASON_DECRYPTION.
+ // - If the passphrase is required because decryption failed, and a new
+ // passphrase is required, |reason| will be REASON_SET_PASSPHRASE_FAILED.
+ //
+ // |pending_keys| is a copy of the cryptographer's pending keys, that may be
+ // cached by the frontend for subsequent use by the UI.
+ virtual void OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) = 0;
+
+ // Called when the passphrase provided by the user has been accepted and is
+ // now used to encrypt sync data.
+ virtual void OnPassphraseAccepted() = 0;
+
+ // |bootstrap_token| is an opaque base64 encoded representation of the key
+ // generated by the current passphrase, and is provided to the observer for
+ // persistence purposes and use in a future initialization of sync (e.g.
+ // after restart). The boostrap token will always be derived from the most
+ // recent GAIA password (for accounts with implicit passphrases), even if
+ // the data is still encrypted with an older GAIA password. For accounts
+ // with explicit passphrases, it will be the most recently seen custom
+ // passphrase.
+ virtual void OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) = 0;
+
+ // Called when initialization is complete to the point that SyncManager can
+ // process changes. This does not necessarily mean authentication succeeded
+ // or that the SyncManager is online.
+ // IMPORTANT: Creating any type of transaction before receiving this
+ // notification is illegal!
+ // WARNING: Calling methods on the SyncManager before receiving this
+ // message, unless otherwise specified, produces undefined behavior.
+ //
+ // |js_backend| is what about:sync interacts with. It can emit
+ // the following events:
+
+ /**
+ * @param {{ enabled: boolean }} details A dictionary containing:
+ * - enabled: whether or not notifications are enabled.
+ */
+ // function onNotificationStateChange(details);
+
+ /**
+ * @param {{ changedTypes: Array.<string> }} details A dictionary
+ * containing:
+ * - changedTypes: a list of types (as strings) for which there
+ are new updates.
+ */
+ // function onIncomingNotification(details);
+
+ // Also, it responds to the following messages (all other messages
+ // are ignored):
+
+ /**
+ * Gets the current notification state.
+ *
+ * @param {function(boolean)} callback Called with whether or not
+ * notifications are enabled.
+ */
+ // function getNotificationState(callback);
+
+ /**
+ * Gets details about the root node.
+ *
+ * @param {function(!Object)} callback Called with details about the
+ * root node.
+ */
+ // TODO(akalin): Change this to getRootNodeId or eliminate it
+ // entirely.
+ // function getRootNodeDetails(callback);
+
+ /**
+ * Gets summary information for a list of ids.
+ *
+ * @param {Array.<string>} idList List of 64-bit ids in decimal
+ * string form.
+ * @param {Array.<{id: string, title: string, isFolder: boolean}>}
+ * callback Called with summaries for the nodes in idList that
+ * exist.
+ */
+ // function getNodeSummariesById(idList, callback);
+
+ /**
+ * Gets detailed information for a list of ids.
+ *
+ * @param {Array.<string>} idList List of 64-bit ids in decimal
+ * string form.
+ * @param {Array.<!Object>} callback Called with detailed
+ * information for the nodes in idList that exist.
+ */
+ // function getNodeDetailsById(idList, callback);
+
+ /**
+ * Gets child ids for a given id.
+ *
+ * @param {string} id 64-bit id in decimal string form of the parent
+ * node.
+ * @param {Array.<string>} callback Called with the (possibly empty)
+ * list of child ids.
+ */
+ // function getChildNodeIds(id);
+
+ virtual void OnInitializationComplete(
+ const browser_sync::WeakHandle<browser_sync::JsBackend>&
+ js_backend, bool success) = 0;
+
+ // We are no longer permitted to communicate with the server. Sync should
+ // be disabled and state cleaned up at once. This can happen for a number
+ // of reasons, e.g. swapping from a test instance to production, or a
+ // global stop syncing operation has wiped the store.
+ virtual void OnStopSyncingPermanently() = 0;
+
+ // After a request to clear server data, these callbacks are invoked to
+ // indicate success or failure.
+ virtual void OnClearServerDataSucceeded() = 0;
+ virtual void OnClearServerDataFailed() = 0;
+
+ // Called when the set of encrypted types or the encrypt
+ // everything flag has been changed. Note that encryption isn't
+ // complete until the OnEncryptionComplete() notification has been
+ // sent (see below).
+ //
+ // |encrypted_types| will always be a superset of
+ // Cryptographer::SensitiveTypes(). If |encrypt_everything| is
+ // true, |encrypted_types| will be the set of all known types.
+ //
+ // Until this function is called, observers can assume that the
+ // set of encrypted types is Cryptographer::SensitiveTypes() and
+ // that the encrypt everything flag is false.
+ //
+ // Called from within a transaction.
+ virtual void OnEncryptedTypesChanged(
+ syncable::ModelTypeSet encrypted_types,
+ bool encrypt_everything) = 0;
+
+ // Called after we finish encrypting the current set of encrypted
+ // types.
+ //
+ // Called from within a transaction.
+ virtual void OnEncryptionComplete() = 0;
+
+ virtual void OnActionableError(
+ const browser_sync::SyncProtocolError& sync_protocol_error) = 0;
+
+ protected:
+ virtual ~Observer();
+ };
+
+ enum TestingMode {
+ NON_TEST,
+ TEST_ON_DISK,
+ TEST_IN_MEMORY,
+ };
+
+ // Create an uninitialized SyncManager. Callers must Init() before using.
+ explicit SyncManager(const std::string& name);
+ virtual ~SyncManager();
+
+ // Initialize the sync manager. |database_location| specifies the path of
+ // the directory in which to locate a sqlite repository storing the syncer
+ // backend state. Initialization will open the database, or create it if it
+ // does not already exist. Returns false on failure.
+ // |event_handler| is the JsEventHandler used to propagate events to
+ // chrome://sync-internals. |event_handler| may be uninitialized.
+ // |sync_server_and_path| and |sync_server_port| represent the Chrome sync
+ // server to use, and |use_ssl| specifies whether to communicate securely;
+ // the default is false.
+ // |blocking_task_runner| is a TaskRunner to be used for tasks that
+ // may block on disk I/O.
+ // |post_factory| will be owned internally and used to create
+ // instances of an HttpPostProvider.
+ // |model_safe_worker| ownership is given to the SyncManager.
+ // |user_agent| is a 7-bit ASCII string suitable for use as the User-Agent
+ // HTTP header. Used internally when collecting stats to classify clients.
+ // |sync_notifier| is owned and used to listen for notifications.
+ // |report_unrecoverable_error_function| may be NULL.
+ bool Init(const FilePath& database_location,
+ const browser_sync::WeakHandle<browser_sync::JsEventHandler>&
+ event_handler,
+ const std::string& sync_server_and_path,
+ int sync_server_port,
+ bool use_ssl,
+ const scoped_refptr<base::TaskRunner>& blocking_task_runner,
+ HttpPostProviderFactory* post_factory,
+ const browser_sync::ModelSafeRoutingInfo& model_safe_routing_info,
+ const std::vector<browser_sync::ModelSafeWorker*>& workers,
+ browser_sync::ExtensionsActivityMonitor*
+ extensions_activity_monitor,
+ ChangeDelegate* change_delegate,
+ const std::string& user_agent,
+ const SyncCredentials& credentials,
+ sync_notifier::SyncNotifier* sync_notifier,
+ const std::string& restored_key_for_bootstrapping,
+ TestingMode testing_mode,
+ browser_sync::Encryptor* encryptor,
+ browser_sync::UnrecoverableErrorHandler*
+ unrecoverable_error_handler,
+ browser_sync::ReportUnrecoverableErrorFunction
+ report_unrecoverable_error_function);
+
+ // Throw an unrecoverable error from a transaction (mostly used for
+ // testing).
+ void ThrowUnrecoverableError();
+
+ // Returns the set of types for which we have stored some sync data.
+ syncable::ModelTypeSet InitialSyncEndedTypes();
+
+ // Update tokens that we're using in Sync. Email must stay the same.
+ void UpdateCredentials(const SyncCredentials& credentials);
+
+ // Called when the user disables or enables a sync type.
+ void UpdateEnabledTypes(const syncable::ModelTypeSet& enabled_types);
+
+ // Put the syncer in normal mode ready to perform nudges and polls.
+ void StartSyncingNormally(
+ const browser_sync::ModelSafeRoutingInfo& routing_info);
+
+ // Attempts to re-encrypt encrypted data types using the passphrase provided.
+ // Notifies observers of the result of the operation via OnPassphraseAccepted
+ // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
+ // appropriate. If an explicit password has been set previously, we drop
+ // subsequent requests to set a passphrase. If the cryptographer has pending
+ // keys, and a new implicit passphrase is provided, we try decrypting the
+ // pending keys with it, and if that fails, we cache the passphrase for
+ // re-encryption once the pending keys are decrypted.
+ void SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit);
+
+ // Provides a passphrase for decrypting the user's existing sync data.
+ // Notifies observers of the result of the operation via OnPassphraseAccepted
+ // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
+ // appropriate if there is a previously cached encryption passphrase. It is an
+ // error to call this when we don't have pending keys.
+ void SetDecryptionPassphrase(const std::string& passphrase);
+
+ // Request a clearing of all data on the server
+ void RequestClearServerData();
+
+ // Switches the mode of operation to CONFIGURATION_MODE and performs
+ // any configuration tasks needed as determined by the params. Once complete,
+ // syncer will remain in CONFIGURATION_MODE until StartSyncingNormally is
+ // called.
+ // |ready_task| is invoked when the configuration completes.
+ // |retry_task| is invoked if the configuration job could not immediately
+ // execute. |ready_task| will still be called when it eventually
+ // does finish.
+ void ConfigureSyncer(
+ ConfigureReason reason,
+ const syncable::ModelTypeSet& types_to_config,
+ const browser_sync::ModelSafeRoutingInfo& new_routing_info,
+ const base::Closure& ready_task,
+ const base::Closure& retry_task);
+
+ // Adds a listener to be notified of sync events.
+ // NOTE: It is OK (in fact, it's probably a good idea) to call this before
+ // having received OnInitializationCompleted.
+ void AddObserver(Observer* observer);
+
+ // Remove the given observer. Make sure to call this if the
+ // Observer is being destroyed so the SyncManager doesn't
+ // potentially dereference garbage.
+ void RemoveObserver(Observer* observer);
+
+ // Status-related getter. May be called on any thread.
+ SyncStatus GetDetailedStatus() const;
+
+ // Whether or not the Nigori node is encrypted using an explicit passphrase.
+ // May be called on any thread.
+ bool IsUsingExplicitPassphrase();
+
+ // Call periodically from a database-safe thread to persist recent changes
+ // to the syncapi model.
+ void SaveChanges();
+
+ // Initiates shutdown of various components in the sync engine. Must be
+ // called from the main thread to allow preempting ongoing tasks on the sync
+ // loop (that may be blocked on I/O). The semantics of |callback| are the
+ // same as with StartConfigurationMode. If provided and a scheduler / sync
+ // loop exists, it will be invoked from the sync loop by the scheduler to
+ // notify that all work has been flushed + cancelled, and it is idle.
+ // If no scheduler exists, the callback is run immediately (from the loop
+ // this was created on, which is the sync loop), as sync is effectively
+ // stopped.
+ void StopSyncingForShutdown(const base::Closure& callback);
+
+ // Issue a final SaveChanges, and close sqlite handles.
+ void ShutdownOnSyncThread();
+
+ // May be called from any thread.
+ UserShare* GetUserShare() const;
+
+ // Inform the cryptographer of the most recent passphrase and set of
+ // encrypted types (from nigori node), then ensure all data that
+ // needs encryption is encrypted with the appropriate passphrase.
+ //
+ // May trigger OnPassphraseRequired(). Otherwise, it will trigger
+ // OnEncryptedTypesChanged() if necessary (see comments for
+ // OnEncryptedTypesChanged()), and then OnEncryptionComplete().
+ //
+ // Also updates or adds device information to the nigori node.
+ //
+ // Note: opens a transaction, so must only be called after syncapi
+ // has been initialized.
+ void RefreshNigori(const std::string& chrome_version,
+ const base::Closure& done_callback);
+
+ // Enable encryption of all sync data. Once enabled, it can never be
+ // disabled without clearing the server data.
+ //
+ // This will trigger OnEncryptedTypesChanged() if necessary (see
+ // comments for OnEncryptedTypesChanged()). It then may trigger
+ // OnPassphraseRequired(), but otherwise it will trigger
+ // OnEncryptionComplete().
+ void EnableEncryptEverything();
+
+ // Returns true if we are currently encrypting all sync data. May
+ // be called on any thread.
+ bool EncryptEverythingEnabledForTest() const;
+
+ // Gets the set of encrypted types from the cryptographer
+ // Note: opens a transaction. May be called from any thread.
+ syncable::ModelTypeSet GetEncryptedDataTypesForTest() const;
+
+ // Reads the nigori node to determine if any experimental features should
+ // be enabled.
+ // Note: opens a transaction. May be called on any thread.
+ bool ReceivedExperiment(browser_sync::Experiments* experiments) const;
+
+ // Uses a read-only transaction to determine if the directory being synced has
+ // any remaining unsynced items. May be called on any thread.
+ bool HasUnsyncedItems() const;
+
+ // Functions used for testing.
+
+ void TriggerOnNotificationStateChangeForTest(
+ bool notifications_enabled);
+
+ void TriggerOnIncomingNotificationForTest(
+ syncable::ModelTypeSet model_types);
+
+ static const int kDefaultNudgeDelayMilliseconds;
+ static const int kPreferencesNudgeDelayMilliseconds;
+ static const int kPiggybackNudgeDelay;
+
+ static const FilePath::CharType kSyncDatabaseFilename[];
+
+ private:
+ friend class SyncManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, NudgeDelayTest);
+
+ // For unit tests.
+ base::TimeDelta GetNudgeDelayTimeDelta(const syncable::ModelType& model_type);
+
+ // Set the internal scheduler for testing purposes.
+ // TODO(sync): Use dependency injection instead. crbug.com/133061
+ void SetSyncSchedulerForTest(
+ scoped_ptr<browser_sync::SyncScheduler> scheduler);
+
+ base::ThreadChecker thread_checker_;
+
+ // An opaque pointer to the nested private class.
+ SyncInternal* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncManager);
+};
+
+bool InitialSyncEndedForTypes(syncable::ModelTypeSet types, UserShare* share);
+
+syncable::ModelTypeSet GetTypesWithEmptyProgressMarkerToken(
+ syncable::ModelTypeSet types,
+ sync_api::UserShare* share);
+
+const char* ConnectionStatusToString(ConnectionStatus status);
+
+// Returns the string representation of a PassphraseRequiredReason value.
+const char* PassphraseRequiredReasonToString(PassphraseRequiredReason reason);
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_
diff --git a/sync/internal_api/public/test/test_user_share.h b/sync/internal_api/public/test/test_user_share.h
new file mode 100644
index 0000000..7386b18
--- /dev/null
+++ b/sync/internal_api/public/test/test_user_share.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A handy class that takes care of setting up and destroying a
+// sync_api::UserShare instance for unit tests that require one.
+//
+// The expected usage is to make this a component of your test fixture:
+//
+// class AwesomenessTest : public testing::Test {
+// public:
+// virtual void SetUp() {
+// test_user_share_.SetUp();
+// }
+// virtual void TearDown() {
+// test_user_share_.TearDown();
+// }
+// protected:
+// TestUserShare test_user_share_;
+// };
+//
+// Then, in your tests:
+//
+// TEST_F(AwesomenessTest, IsMaximal) {
+// sync_api::ReadTransaction trans(test_user_share_.user_share());
+// ...
+// }
+//
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_TEST_TEST_USER_SHARE_H_
+#define SYNC_INTERNAL_API_PUBLIC_TEST_TEST_USER_SHARE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "sync/internal_api/public/user_share.h"
+
+namespace browser_sync {
+
+class TestDirectorySetterUpper;
+
+class TestUserShare {
+ public:
+ TestUserShare();
+ ~TestUserShare();
+
+ // Sets up the UserShare instance. Clears any existing database
+ // backing files that might exist on disk.
+ void SetUp();
+
+ // Undo everything done by SetUp(): closes the UserShare and deletes
+ // the backing files. Before closing the directory, this will run
+ // the directory invariant checks and perform the SaveChanges action
+ // on the user share's directory.
+ void TearDown();
+
+ // Non-NULL iff called between a call to SetUp() and TearDown().
+ sync_api::UserShare* user_share();
+
+ private:
+ scoped_ptr<TestDirectorySetterUpper> dir_maker_;
+ scoped_ptr<sync_api::UserShare> user_share_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUserShare);
+};
+
+} // namespace browser_sync
+
+#endif // SYNC_INTERNAL_API_PUBLIC_TEST_TEST_USER_SHARE_H_
diff --git a/sync/internal_api/public/user_share.h b/sync/internal_api/public/user_share.h
new file mode 100644
index 0000000..0fa6a1d
--- /dev/null
+++ b/sync/internal_api/public/user_share.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_USER_SHARE_H_
+#define SYNC_INTERNAL_API_PUBLIC_USER_SHARE_H_
+#pragma once
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace syncable {
+class Directory;
+}
+
+namespace sync_api {
+
+// A UserShare encapsulates the syncable pieces that represent an authenticated
+// user and their data (share).
+// This encompasses all pieces required to build transaction objects on the
+// syncable share.
+struct UserShare {
+ UserShare();
+ ~UserShare();
+
+ // The Directory itself, which is the parent of Transactions.
+ scoped_ptr<syncable::Directory> directory;
+
+ // The username of the sync user.
+ std::string name;
+};
+
+}
+
+#endif // SYNC_INTERNAL_API_PUBLIC_USER_SHARE_H_
diff --git a/sync/internal_api/public/util/experiments.h b/sync/internal_api/public/util/experiments.h
new file mode 100644
index 0000000..1439e2b
--- /dev/null
+++ b/sync/internal_api/public/util/experiments.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_UTIL_EXPERIMENTS_
+#define SYNC_UTIL_EXPERIMENTS_
+#pragma once
+
+#include "sync/internal_api/public/syncable/model_type.h"
+
+namespace browser_sync {
+
+// A structure to hold the enable status of experimental sync features.
+struct Experiments {
+ Experiments() : sync_tab_favicons(false) {}
+
+ bool Matches(const Experiments& rhs) {
+ return (sync_tab_favicons == rhs.sync_tab_favicons);
+ }
+
+ // Enable syncing of favicons within tab sync (only has an effect if tab sync
+ // is already enabled). This takes effect on the next restart.
+ bool sync_tab_favicons;
+};
+
+}
+
+#endif // SYNC_UTIL_EXPERIMENTS_
diff --git a/sync/internal_api/public/util/immutable.h b/sync/internal_api/public/util/immutable.h
new file mode 100644
index 0000000..6624b90
--- /dev/null
+++ b/sync/internal_api/public/util/immutable.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Immutable<T> provides an easy, cheap, and thread-safe way to pass
+// large immutable data around.
+//
+// For example, consider the following code:
+//
+// typedef std::vector<LargeObject> LargeObjectList;
+//
+// void ProcessStuff(const LargeObjectList& stuff) {
+// for (LargeObjectList::const_iterator it = stuff.begin();
+// it != stuff.end(); ++it) {
+// ... process it ...
+// }
+// }
+//
+// ...
+//
+// LargeObjectList my_stuff;
+// ... fill my_stuff with lots of LargeObjects ...
+// some_loop->PostTask(FROM_HERE, base::Bind(&ProcessStuff, my_stuff));
+//
+// The last line incurs the cost of copying my_stuff, which is
+// undesirable. Here's the above code re-written using Immutable<T>:
+//
+// void ProcessStuff(
+// const browser_sync::Immutable<LargeObjectList>& stuff) {
+// for (LargeObjectList::const_iterator it = stuff.Get().begin();
+// it != stuff.Get().end(); ++it) {
+// ... process it ...
+// }
+// }
+//
+// ...
+//
+// LargeObjectList my_stuff;
+// ... fill my_stuff with lots of LargeObjects ...
+// some_loop->PostTask(
+// FROM_HERE, base::Bind(&ProcessStuff, MakeImmutable(&my_stuff)));
+//
+// The last line, which resets my_stuff to a default-initialized
+// state, incurs only the cost of a swap of LargeObjectLists, which is
+// O(1) for most STL container implementations. The data in my_stuff
+// is ref-counted (thread-safely), so it is freed as soon as
+// ProcessStuff is finished.
+//
+// NOTE: By default, Immutable<T> relies on ADL
+// (http://en.wikipedia.org/wiki/Argument-dependent_name_lookup) to
+// find a swap() function for T, falling back to std::swap() when
+// necessary. If you overload swap() for your type in its namespace,
+// or if you specialize std::swap() for your type, (see
+// http://stackoverflow.com/questions/11562/how-to-overload-stdswap
+// for discussion) Immutable<T> should be able to find it.
+//
+// Alternatively, you could explicitly control which swap function is
+// used by providing your own traits class or using one of the
+// pre-defined ones below. See comments on traits below for details.
+//
+// NOTE: Some complexity is necessary in order to use Immutable<T>
+// with forward-declared types. See comments on traits below for
+// details.
+
+#ifndef SYNC_UTIL_IMMUTABLE_H_
+#define SYNC_UTIL_IMMUTABLE_H_
+#pragma once
+
+// For std::swap().
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+
+namespace browser_sync {
+
+namespace internal {
+// This class is part of the Immutable implementation. DO NOT USE
+// THIS CLASS DIRECTLY YOURSELF.
+
+template <typename T, typename Traits>
+class ImmutableCore
+ : public base::RefCountedThreadSafe<ImmutableCore<T, Traits> > {
+ public:
+ // wrapper_ is always explicitly default-initialized to handle
+ // primitive types and the case where Traits::Wrapper == T.
+
+ ImmutableCore() : wrapper_() {
+ Traits::InitializeWrapper(&wrapper_);
+ }
+
+ explicit ImmutableCore(T* t) : wrapper_() {
+ Traits::InitializeWrapper(&wrapper_);
+ Traits::Swap(Traits::UnwrapMutable(&wrapper_), t);
+ }
+
+ const T& Get() const {
+ return Traits::Unwrap(wrapper_);
+ }
+
+ private:
+ ~ImmutableCore() {
+ Traits::DestroyWrapper(&wrapper_);
+ }
+ friend class base::RefCountedThreadSafe<ImmutableCore<T, Traits> >;
+
+ // This is semantically const, but we can't mark it a such as we
+ // modify it in the constructor.
+ typename Traits::Wrapper wrapper_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImmutableCore);
+};
+
+} // namespace internal
+
+// Traits usage notes
+// ------------------
+// The most common reason to use your own traits class is to provide
+// your own swap method. First, consider the pre-defined traits
+// classes HasSwapMemFn{ByRef,ByPtr} below. If neither of those work,
+// then define your own traits class inheriting from
+// DefaultImmutableTraits<YourType> (to pick up the defaults for
+// everything else) and provide your own Swap() method.
+//
+// Another reason to use your own traits class is to be able to use
+// Immutable<T> with a forward-declared type (important for protobuf
+// classes, when you want to avoid headers pulling in generated
+// headers). (This is why the Traits::Wrapper type exists; normally,
+// Traits::Wrapper is just T itself, but that needs to be changed for
+// forward-declared types.)
+//
+// For example, if you want to do this:
+//
+// my_class.h
+// ----------
+// #include ".../immutable.h"
+//
+// // Forward declaration.
+// class SomeOtherType;
+//
+// class MyClass {
+// ...
+// private:
+// // Doesn't work, as defaults traits class needs SomeOtherType's
+// // definition to be visible.
+// Immutable<SomeOtherType> foo_;
+// };
+//
+// You'll have to do this:
+//
+// my_class.h
+// ----------
+// #include ".../immutable.h"
+//
+// // Forward declaration.
+// class SomeOtherType;
+//
+// class MyClass {
+// ...
+// private:
+// struct ImmutableSomeOtherTypeTraits {
+// // scoped_ptr<SomeOtherType> won't work here, either.
+// typedef SomeOtherType* Wrapper;
+//
+// static void InitializeWrapper(Wrapper* wrapper);
+//
+// static void DestroyWrapper(Wrapper* wrapper);
+// ...
+// };
+//
+// typedef Immutable<SomeOtherType, ImmutableSomeOtherTypeTraits>
+// ImmutableSomeOtherType;
+//
+// ImmutableSomeOtherType foo_;
+// };
+//
+// my_class.cc
+// -----------
+// #include ".../some_other_type.h"
+//
+// void MyClass::ImmutableSomeOtherTypeTraits::InitializeWrapper(
+// Wrapper* wrapper) {
+// *wrapper = new SomeOtherType();
+// }
+//
+// void MyClass::ImmutableSomeOtherTypeTraits::DestroyWrapper(
+// Wrapper* wrapper) {
+// delete *wrapper;
+// }
+//
+// ...
+//
+// Also note that this incurs an additional memory allocation when you
+// create an Immutable<SomeOtherType>.
+
+template <typename T>
+struct DefaultImmutableTraits {
+ typedef T Wrapper;
+
+ static void InitializeWrapper(Wrapper* wrapper) {}
+
+ static void DestroyWrapper(Wrapper* wrapper) {}
+
+ static const T& Unwrap(const Wrapper& wrapper) { return wrapper; }
+
+ static T* UnwrapMutable(Wrapper* wrapper) { return wrapper; }
+
+ static void Swap(T* t1, T* t2) {
+ // Uses ADL (see
+ // http://en.wikipedia.org/wiki/Argument-dependent_name_lookup).
+ using std::swap;
+ swap(*t1, *t2);
+ }
+};
+
+// Most STL containers have by-reference swap() member functions,
+// although they usually already overload std::swap() to use those.
+template <typename T>
+struct HasSwapMemFnByRef : public DefaultImmutableTraits<T> {
+ static void Swap(T* t1, T* t2) {
+ t1->swap(*t2);
+ }
+};
+
+// Most Google-style objects have by-pointer Swap() member functions
+// (for example, generated protocol buffer classes).
+template <typename T>
+struct HasSwapMemFnByPtr : public DefaultImmutableTraits<T> {
+ static void Swap(T* t1, T* t2) {
+ t1->Swap(t2);
+ }
+};
+
+template <typename T, typename Traits = DefaultImmutableTraits<T> >
+class Immutable {
+ public:
+ // Puts the underlying object in a default-initialized state.
+ Immutable() : core_(new internal::ImmutableCore<T, Traits>()) {}
+
+ // Copy constructor and assignment welcome.
+
+ // Resets |t| to a default-initialized state.
+ explicit Immutable(T* t)
+ : core_(new internal::ImmutableCore<T, Traits>(t)) {}
+
+ const T& Get() const {
+ return core_->Get();
+ }
+
+ private:
+ scoped_refptr<const internal::ImmutableCore<T, Traits> > core_;
+};
+
+// Helper function to avoid having to write out template arguments.
+template <typename T>
+Immutable<T> MakeImmutable(T* t) {
+ return Immutable<T>(t);
+}
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_IMMUTABLE_H_
diff --git a/sync/internal_api/public/util/immutable_unittest.cc b/sync/internal_api/public/util/immutable_unittest.cc
new file mode 100644
index 0000000..ea0b29b
--- /dev/null
+++ b/sync/internal_api/public/util/immutable_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/internal_api/public/util/immutable.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+// Helper class that keeps track of the token passed in at
+// construction and how many times that token is copied.
+class TokenCore : public base::RefCounted<TokenCore> {
+ public:
+ explicit TokenCore(const char* token) : token_(token), copy_count_(0) {}
+
+ const char* GetToken() const { return token_; }
+
+ void RecordCopy() { ++copy_count_; }
+
+ int GetCopyCount() const { return copy_count_; }
+
+ private:
+ friend class base::RefCounted<TokenCore>;
+
+ ~TokenCore() {}
+
+ const char* const token_;
+ int copy_count_;
+};
+
+enum SwapBehavior {
+ USE_DEFAULT_SWAP,
+ USE_FAST_SWAP_VIA_ADL,
+ USE_FAST_SWAP_VIA_SPECIALIZATION
+};
+
+const char kEmptyToken[] = "<empty token>";
+
+// Base class for various token classes, differing in swap behavior.
+template <SwapBehavior>
+class TokenBase {
+ public:
+ TokenBase() : core_(new TokenCore(kEmptyToken)) {}
+
+ explicit TokenBase(const char* token) : core_(new TokenCore(token)) {}
+
+ TokenBase(const TokenBase& other) : core_(other.core_) {
+ core_->RecordCopy();
+ }
+
+ TokenBase& operator=(const TokenBase& other) {
+ core_ = other.core_;
+ core_->RecordCopy();
+ return *this;
+ }
+
+ const char* GetToken() const {
+ return core_->GetToken();
+ }
+
+ int GetCopyCount() const {
+ return core_->GetCopyCount();
+ }
+
+ // For associative containers.
+ bool operator<(const TokenBase& other) const {
+ return std::string(GetToken()) < std::string(other.GetToken());
+ }
+
+ // STL-style swap.
+ void swap(TokenBase& other) {
+ using std::swap;
+ swap(other.core_, core_);
+ }
+
+ // Google-style swap.
+ void Swap(TokenBase* other) {
+ using std::swap;
+ swap(other->core_, core_);
+ }
+
+ private:
+ scoped_refptr<TokenCore> core_;
+};
+
+typedef TokenBase<USE_DEFAULT_SWAP> Token;
+typedef TokenBase<USE_FAST_SWAP_VIA_ADL> ADLToken;
+typedef TokenBase<USE_FAST_SWAP_VIA_SPECIALIZATION> SpecializationToken;
+
+void swap(ADLToken& t1, ADLToken& t2) {
+ t1.Swap(&t2);
+}
+
+} // namespace browser_sync
+
+// Allowed by the standard (17.4.3.1/1).
+namespace std {
+
+template <>
+void swap(browser_sync::SpecializationToken& t1,
+ browser_sync::SpecializationToken& t2) {
+ t1.Swap(&t2);
+}
+
+} // namespace
+
+namespace browser_sync {
+namespace {
+
+class ImmutableTest : public ::testing::Test {};
+
+TEST_F(ImmutableTest, Int) {
+ int x = 5;
+ Immutable<int> ix(&x);
+ EXPECT_EQ(5, ix.Get());
+ EXPECT_EQ(0, x);
+}
+
+TEST_F(ImmutableTest, IntCopy) {
+ int x = 5;
+ Immutable<int> ix = Immutable<int>(&x);
+ EXPECT_EQ(5, ix.Get());
+ EXPECT_EQ(0, x);
+}
+
+TEST_F(ImmutableTest, IntAssign) {
+ int x = 5;
+ Immutable<int> ix;
+ EXPECT_EQ(0, ix.Get());
+ ix = Immutable<int>(&x);
+ EXPECT_EQ(5, ix.Get());
+ EXPECT_EQ(0, x);
+}
+
+TEST_F(ImmutableTest, IntMakeImmutable) {
+ int x = 5;
+ Immutable<int> ix = MakeImmutable(&x);
+ EXPECT_EQ(5, ix.Get());
+ EXPECT_EQ(0, x);
+}
+
+template <typename T, typename ImmutableT>
+void RunTokenTest(const char* token, bool expect_copies) {
+ SCOPED_TRACE(token);
+ T t(token);
+ EXPECT_EQ(token, t.GetToken());
+ EXPECT_EQ(0, t.GetCopyCount());
+
+ ImmutableT immutable_t(&t);
+ EXPECT_EQ(token, immutable_t.Get().GetToken());
+ EXPECT_EQ(kEmptyToken, t.GetToken());
+ EXPECT_EQ(expect_copies, immutable_t.Get().GetCopyCount() > 0);
+ EXPECT_EQ(expect_copies, t.GetCopyCount() > 0);
+}
+
+TEST_F(ImmutableTest, Token) {
+ RunTokenTest<Token, Immutable<Token> >("Token", true /* expect_copies */);
+}
+
+TEST_F(ImmutableTest, TokenSwapMemFnByRef) {
+ RunTokenTest<Token, Immutable<Token, HasSwapMemFnByRef<Token> > >(
+ "TokenSwapMemFnByRef", false /* expect_copies */);
+}
+
+TEST_F(ImmutableTest, TokenSwapMemFnByPtr) {
+ RunTokenTest<Token, Immutable<Token, HasSwapMemFnByPtr<Token> > >(
+ "TokenSwapMemFnByPtr", false /* expect_copies */);
+}
+
+TEST_F(ImmutableTest, ADLToken) {
+ RunTokenTest<ADLToken, Immutable<ADLToken> >(
+ "ADLToken", false /* expect_copies */);
+}
+
+TEST_F(ImmutableTest, SpecializationToken) {
+ RunTokenTest<SpecializationToken, Immutable<SpecializationToken> >(
+ "SpecializationToken", false /* expect_copies */);
+}
+
+template <typename C, typename ImmutableC>
+void RunTokenContainerTest(const char* token) {
+ SCOPED_TRACE(token);
+ const Token tokens[] = { Token(), Token(token) };
+ const size_t token_count = arraysize(tokens);
+ C c(tokens, tokens + token_count);
+ const int copy_count = c.begin()->GetCopyCount();
+ EXPECT_GT(copy_count, 0);
+ for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) {
+ EXPECT_EQ(copy_count, it->GetCopyCount());
+ }
+
+ // Make sure that making the container immutable doesn't incur any
+ // copies of the tokens.
+ ImmutableC immutable_c(&c);
+ EXPECT_TRUE(c.empty());
+ ASSERT_EQ(token_count, immutable_c.Get().size());
+ int i = 0;
+ for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) {
+ EXPECT_EQ(tokens[i].GetToken(), it->GetToken());
+ EXPECT_EQ(copy_count, it->GetCopyCount());
+ ++i;
+ }
+}
+
+TEST_F(ImmutableTest, Vector) {
+ RunTokenContainerTest<std::vector<Token>, Immutable<std::vector<Token> > >(
+ "Vector");
+}
+
+TEST_F(ImmutableTest, VectorSwapMemFnByRef) {
+ RunTokenContainerTest<
+ std::vector<Token>,
+ Immutable<std::vector<Token>, HasSwapMemFnByRef<std::vector<Token> > > >(
+ "VectorSwapMemFnByRef");
+}
+
+// http://crbug.com/129128
+#if defined(OS_WIN)
+#define MAYBE_Deque DISABLED_Deque
+#else
+#define MAYBE_Deque Deque
+#endif
+TEST_F(ImmutableTest, MAYBE_Deque) {
+ RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >(
+ "Deque");
+}
+
+TEST_F(ImmutableTest, List) {
+ RunTokenContainerTest<std::list<Token>, Immutable<std::list<Token> > >(
+ "List");
+}
+
+TEST_F(ImmutableTest, Set) {
+ RunTokenContainerTest<std::set<Token>, Immutable<std::set<Token> > >(
+ "Set");
+}
+
+} // namespace
+} // namespace browser_sync
diff --git a/sync/internal_api/public/util/report_unrecoverable_error_function.h b/sync/internal_api/public/util/report_unrecoverable_error_function.h
new file mode 100644
index 0000000..ead73f0
--- /dev/null
+++ b/sync/internal_api/public/util/report_unrecoverable_error_function.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
+#define SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
+#pragma once
+
+namespace browser_sync {
+
+// A ReportUnrecoverableErrorFunction is a function that is called
+// immediately when an unrecoverable error is encountered. Unlike
+// UnrecoverableErrorHandler, it should just log the error and any
+// context surrounding it.
+typedef void (*ReportUnrecoverableErrorFunction)(void);
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
diff --git a/sync/internal_api/public/util/unrecoverable_error_handler.h b/sync/internal_api/public/util/unrecoverable_error_handler.h
new file mode 100644
index 0000000..aaca1e9
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_handler.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+#define SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "base/location.h"
+
+namespace browser_sync {
+
+class UnrecoverableErrorHandler {
+ public:
+ // Call this when normal operation detects that the chrome model and the
+ // syncer model are inconsistent, or similar. The ProfileSyncService will
+ // try to avoid doing any work to avoid crashing or corrupting things
+ // further, and will report an error status if queried.
+ virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
+ const std::string& message) = 0;
+ protected:
+ virtual ~UnrecoverableErrorHandler() { }
+};
+
+}
+
+#endif // SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+
diff --git a/sync/internal_api/public/util/unrecoverable_error_info.cc b/sync/internal_api/public/util/unrecoverable_error_info.cc
new file mode 100644
index 0000000..369fa3e
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_info.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/internal_api/public/util/unrecoverable_error_info.h"
+
+namespace browser_sync {
+
+UnrecoverableErrorInfo::UnrecoverableErrorInfo()
+ : is_set_(false) {
+}
+
+UnrecoverableErrorInfo::UnrecoverableErrorInfo(
+ const tracked_objects::Location& location,
+ const std::string& message)
+ : location_(location),
+ message_(message),
+ is_set_(true) {
+}
+
+UnrecoverableErrorInfo::~UnrecoverableErrorInfo() {
+}
+
+void UnrecoverableErrorInfo::Reset(
+ const tracked_objects::Location& location,
+ const std::string& message) {
+ location_ = location;
+ message_ = message;
+ is_set_ = true;
+}
+
+bool UnrecoverableErrorInfo::IsSet() const {
+ return is_set_;
+}
+
+const tracked_objects::Location& UnrecoverableErrorInfo::location() const {
+ return location_;
+}
+
+const std::string& UnrecoverableErrorInfo::message() const {
+ return message_;
+}
+
+} // namespace browser_sync
diff --git a/sync/internal_api/public/util/unrecoverable_error_info.h b/sync/internal_api/public/util/unrecoverable_error_info.h
new file mode 100644
index 0000000..64b780a
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_info.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
+#define SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
+// TODO(lipalani): Figure out the right location for this class so it is
+// accessible outside of sync engine as well.
+#pragma once
+
+#include <string>
+
+#include "base/location.h"
+
+namespace browser_sync {
+
+class UnrecoverableErrorInfo {
+ public:
+ UnrecoverableErrorInfo();
+ UnrecoverableErrorInfo(
+ const tracked_objects::Location& location,
+ const std::string& message);
+ ~UnrecoverableErrorInfo();
+
+ void Reset(const tracked_objects::Location& location,
+ const std::string& message);
+
+ bool IsSet() const;
+
+ const tracked_objects::Location& location() const;
+ const std::string& message() const;
+
+ private:
+ tracked_objects::Location location_;
+ std::string message_;
+ bool is_set_;
+};
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
diff --git a/sync/internal_api/public/util/weak_handle.cc b/sync/internal_api/public/util/weak_handle.cc
new file mode 100644
index 0000000..136fc58
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/internal_api/public/util/weak_handle.h"
+
+#include <sstream>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+
+namespace browser_sync {
+
+namespace internal {
+
+WeakHandleCoreBase::WeakHandleCoreBase()
+ : owner_loop_proxy_(base::MessageLoopProxy::current()) {}
+
+bool WeakHandleCoreBase::IsOnOwnerThread() const {
+ return owner_loop_proxy_->BelongsToCurrentThread();
+}
+
+WeakHandleCoreBase::~WeakHandleCoreBase() {}
+
+void WeakHandleCoreBase::PostToOwnerThread(
+ const tracked_objects::Location& from_here,
+ const base::Closure& fn) const {
+ if (!owner_loop_proxy_->PostTask(from_here, fn)) {
+ DVLOG(1) << "Could not post task from " << from_here.ToString();
+ }
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/sync/internal_api/public/util/weak_handle.h b/sync/internal_api/public/util/weak_handle.h
new file mode 100644
index 0000000..653da60
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle.h
@@ -0,0 +1,379 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Weak handles provides a way to refer to weak pointers from another
+// thread. This is useful because it is not safe to reference a weak
+// pointer from a thread other than the thread on which it was
+// created.
+//
+// Weak handles can be passed across threads, so for example, you can
+// use them to do the "real" work on one thread and get notified on
+// another thread:
+//
+// class FooIOWorker {
+// public:
+// FooIOWorker(const WeakHandle<Foo>& foo) : foo_(foo) {}
+//
+// void OnIOStart() {
+// foo_.Call(FROM_HERE, &Foo::OnIOStart);
+// }
+//
+// void OnIOEvent(IOEvent e) {
+// foo_.Call(FROM_HERE, &Foo::OnIOEvent, e);
+// }
+//
+// void OnIOError(IOError err) {
+// foo_.Call(FROM_HERE, &Foo::OnIOError, err);
+// }
+//
+// private:
+// const WeakHandle<Foo> foo_;
+// };
+//
+// class Foo : public SupportsWeakPtr<Foo>, public NonThreadSafe {
+// public:
+// Foo() {
+// SpawnFooIOWorkerOnIOThread(base::MakeWeakHandle(AsWeakPtr()));
+// }
+//
+// /* Will always be called on the correct thread, and only if this
+// object hasn't been destroyed. */
+// void OnIOStart() { DCHECK(CalledOnValidThread(); ... }
+// void OnIOEvent(IOEvent e) { DCHECK(CalledOnValidThread(); ... }
+// void OnIOError(IOError err) { DCHECK(CalledOnValidThread(); ... }
+// };
+
+#ifndef SYNC_UTIL_WEAK_HANDLE_H_
+#define SYNC_UTIL_WEAK_HANDLE_H_
+#pragma once
+
+#include <cstddef>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+class MessageLoopProxy;
+} // namespace base
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace browser_sync {
+
+template <typename T> class WeakHandle;
+
+namespace internal {
+// These classes are part of the WeakHandle implementation. DO NOT
+// USE THESE CLASSES DIRECTLY YOURSELF.
+
+// Adapted from base/callback_internal.h.
+
+template <typename T>
+struct ParamTraits {
+ typedef const T& ForwardType;
+};
+
+template <typename T>
+struct ParamTraits<T&> {
+ typedef T& ForwardType;
+};
+
+template <typename T, size_t n>
+struct ParamTraits<T[n]> {
+ typedef const T* ForwardType;
+};
+
+template <typename T>
+struct ParamTraits<T[]> {
+ typedef const T* ForwardType;
+};
+
+// Base class for WeakHandleCore<T> to avoid template bloat. Handles
+// the interaction with the owner thread and its message loop.
+class WeakHandleCoreBase {
+ public:
+ // Assumes the current thread is the owner thread.
+ WeakHandleCoreBase();
+
+ // May be called on any thread.
+ bool IsOnOwnerThread() const;
+
+ protected:
+ // May be destroyed on any thread.
+ ~WeakHandleCoreBase();
+
+ // May be called on any thread.
+ void PostToOwnerThread(const tracked_objects::Location& from_here,
+ const base::Closure& fn) const;
+
+ private:
+ // May be used on any thread.
+ const scoped_refptr<base::MessageLoopProxy> owner_loop_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakHandleCoreBase);
+};
+
+// WeakHandleCore<T> contains all the logic for WeakHandle<T>.
+template <typename T>
+class WeakHandleCore
+ : public WeakHandleCoreBase,
+ public base::RefCountedThreadSafe<WeakHandleCore<T> > {
+ public:
+ // Must be called on |ptr|'s owner thread, which is assumed to be
+ // the current thread.
+ explicit WeakHandleCore(const base::WeakPtr<T>& ptr) : ptr_(ptr) {}
+
+ // Must be called on |ptr_|'s owner thread.
+ base::WeakPtr<T> Get() const {
+ CHECK(IsOnOwnerThread());
+ return ptr_;
+ }
+
+ // Call(...) may be called on any thread, but all its arguments
+ // should be safe to be bound and copied across threads.
+
+ template <typename U>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(void)) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall0<U>, this, fn));
+ }
+
+ template <typename U, typename A1>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1),
+ typename ParamTraits<A1>::ForwardType a1) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall1<U, A1>,
+ this, fn, a1));
+ }
+
+ template <typename U, typename A1, typename A2>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall2<U, A1, A2>,
+ this, fn, a1, a2));
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall3<U, A1, A2, A3>,
+ this, fn, a1, a2, a3));
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3, A4),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3,
+ typename ParamTraits<A4>::ForwardType a4) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall4<U, A1, A2, A3, A4>,
+ this, fn, a1, a2, a3, a4));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<WeakHandleCore<T> >;
+
+ // May be destroyed on any thread.
+ ~WeakHandleCore() {}
+
+ // GCC 4.2.1 on OS X gets confused if all the DoCall functions are
+ // named the same, so we distinguish them.
+
+ template <typename U>
+ void DoCall0(void (U::*fn)(void)) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)();
+ }
+
+ template <typename U, typename A1>
+ void DoCall1(void (U::*fn)(A1),
+ typename ParamTraits<A1>::ForwardType a1) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1);
+ }
+
+ template <typename U, typename A1, typename A2>
+ void DoCall2(void (U::*fn)(A1, A2),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void DoCall3(void (U::*fn)(A1, A2, A3),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2, a3);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void DoCall4(void (U::*fn)(A1, A2, A3, A4),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3,
+ typename ParamTraits<A4>::ForwardType a4) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2, a3, a4);
+ }
+
+ // Must be dereferenced only on the owner thread. May be destroyed
+ // from any thread.
+ base::WeakPtr<T> ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakHandleCore);
+};
+
+} // namespace internal
+
+// May be destroyed on any thread.
+// Copying and assignment are welcome.
+template <typename T>
+class WeakHandle {
+ public:
+ // Creates an uninitialized WeakHandle.
+ WeakHandle() {}
+
+ // Creates an initialized WeakHandle from |ptr|.
+ explicit WeakHandle(const base::WeakPtr<T>& ptr)
+ : core_(new internal::WeakHandleCore<T>(ptr)) {}
+
+ // Allow conversion from WeakHandle<U> to WeakHandle<T> if U is
+ // convertible to T, but we *must* be on |other|'s owner thread.
+ // Note that this doesn't override the regular copy constructor, so
+ // that one can be called on any thread.
+ template <typename U>
+ WeakHandle(const browser_sync::WeakHandle<U>& other) // NOLINT
+ : core_(
+ other.IsInitialized() ?
+ new internal::WeakHandleCore<T>(other.Get()) :
+ NULL) {}
+
+ // Returns true iff this WeakHandle is initialized. Note that being
+ // initialized isn't a guarantee that the underlying object is still
+ // alive.
+ bool IsInitialized() const {
+ return core_.get() != NULL;
+ }
+
+ // Resets to an uninitialized WeakHandle.
+ void Reset() {
+ core_ = NULL;
+ }
+
+ // Must be called only on the underlying object's owner thread.
+ base::WeakPtr<T> Get() const {
+ CHECK(IsInitialized());
+ CHECK(core_->IsOnOwnerThread());
+ return core_->Get();
+ }
+
+ // Call(...) may be called on any thread, but all its arguments
+ // should be safe to be bound and copied across threads.
+
+ template <typename U>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(void)) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn);
+ }
+
+ template <typename U, typename A1>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1),
+ typename internal::ParamTraits<A1>::ForwardType a1) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1);
+ }
+
+ template <typename U, typename A1, typename A2>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2,
+ typename internal::ParamTraits<A3>::ForwardType a3) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2, a3);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3, A4),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2,
+ typename internal::ParamTraits<A3>::ForwardType a3,
+ typename internal::ParamTraits<A4>::ForwardType a4) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2, a3, a4);
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WeakHandleTest,
+ TypeConversionConstructor);
+ FRIEND_TEST_ALL_PREFIXES(WeakHandleTest,
+ TypeConversionConstructorAssignment);
+
+ scoped_refptr<internal::WeakHandleCore<T> > core_;
+};
+
+// Makes a WeakHandle from a WeakPtr.
+template <typename T>
+WeakHandle<T> MakeWeakHandle(const base::WeakPtr<T>& ptr) {
+ return WeakHandle<T>(ptr);
+}
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_WEAK_HANDLE_H_
diff --git a/sync/internal_api/public/util/weak_handle_unittest.cc b/sync/internal_api/public/util/weak_handle_unittest.cc
new file mode 100644
index 0000000..af919ad
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle_unittest.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/internal_api/public/util/weak_handle.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+using ::testing::_;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+class Base {
+ public:
+ Base() : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+ WeakHandle<Base> AsWeakHandle() {
+ return MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
+ }
+
+ void Kill() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ }
+
+ MOCK_METHOD0(Test, void());
+ MOCK_METHOD1(Test1, void(const int&));
+ MOCK_METHOD2(Test2, void(const int&, Base*));
+ MOCK_METHOD3(Test3, void(const int&, Base*, float));
+ MOCK_METHOD4(Test4, void(const int&, Base*, float, const char*));
+
+ MOCK_METHOD1(TestWithSelf, void(const WeakHandle<Base>&));
+
+ private:
+ base::WeakPtrFactory<Base> weak_ptr_factory_;
+};
+
+class Derived : public Base, public base::SupportsWeakPtr<Derived> {};
+
+class WeakHandleTest : public ::testing::Test {
+ protected:
+ virtual void TearDown() {
+ // Process any last-minute posted tasks.
+ PumpLoop();
+ }
+
+ void PumpLoop() {
+ message_loop_.RunAllPending();
+ }
+
+ static void CallTestFromOtherThread(tracked_objects::Location from_here,
+ const WeakHandle<Base>& h) {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(
+ from_here, base::Bind(&WeakHandleTest::CallTest, from_here, h));
+ }
+
+ private:
+ static void CallTest(tracked_objects::Location from_here,
+ const WeakHandle<Base>& h) {
+ h.Call(from_here, &Base::Test);
+ }
+
+ MessageLoop message_loop_;
+};
+
+TEST_F(WeakHandleTest, Uninitialized) {
+ // Default.
+ WeakHandle<int> h;
+ EXPECT_FALSE(h.IsInitialized());
+ // Copy.
+ {
+ WeakHandle<int> h2(h);
+ EXPECT_FALSE(h2.IsInitialized());
+ }
+ // Assign.
+ {
+ WeakHandle<int> h2;
+ h2 = h;
+ EXPECT_FALSE(h.IsInitialized());
+ }
+}
+
+TEST_F(WeakHandleTest, InitializedAfterDestroy) {
+ WeakHandle<Base> h;
+ {
+ StrictMock<Base> b;
+ h = b.AsWeakHandle();
+ }
+ EXPECT_TRUE(h.IsInitialized());
+ EXPECT_FALSE(h.Get());
+}
+
+TEST_F(WeakHandleTest, InitializedAfterInvalidate) {
+ StrictMock<Base> b;
+ WeakHandle<Base> h = b.AsWeakHandle();
+ b.Kill();
+ EXPECT_TRUE(h.IsInitialized());
+ EXPECT_FALSE(h.Get());
+}
+
+TEST_F(WeakHandleTest, Call) {
+ StrictMock<Base> b;
+ const char test_str[] = "test";
+ EXPECT_CALL(b, Test());
+ EXPECT_CALL(b, Test1(5));
+ EXPECT_CALL(b, Test2(5, &b));
+ EXPECT_CALL(b, Test3(5, &b, 5));
+ EXPECT_CALL(b, Test4(5, &b, 5, test_str));
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should run.
+ h.Call(FROM_HERE, &Base::Test);
+ h.Call(FROM_HERE, &Base::Test1, 5);
+ h.Call(FROM_HERE, &Base::Test2, 5, &b);
+ h.Call(FROM_HERE, &Base::Test3, 5, &b, 5);
+ h.Call(FROM_HERE, &Base::Test4, 5, &b, 5, test_str);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterDestroy) {
+ {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should not run.
+ h.Call(FROM_HERE, &Base::Test);
+ }
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterInvalidate) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should not run.
+ h.Call(FROM_HERE, &Base::Test);
+
+ b.Kill();
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallThreaded) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test());
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ // Should run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterDestroyThreaded) {
+ WeakHandle<Base> h;
+ {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+ h = b.AsWeakHandle();
+ }
+
+ // Should not run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterInvalidateThreaded) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ b.Kill();
+ // Should not run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, DeleteOnOtherThread) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base>* h = new WeakHandle<Base>(b.AsWeakHandle());
+
+ {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->DeleteSoon(FROM_HERE, h);
+ }
+
+ PumpLoop();
+}
+
+void CallTestWithSelf(const WeakHandle<Base>& b1) {
+ StrictMock<Base> b2;
+ b1.Call(FROM_HERE, &Base::TestWithSelf, b2.AsWeakHandle());
+}
+
+TEST_F(WeakHandleTest, WithDestroyedThread) {
+ StrictMock<Base> b1;
+ WeakHandle<Base> b2;
+ EXPECT_CALL(b1, TestWithSelf(_)).WillOnce(SaveArg<0>(&b2));
+
+ {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&CallTestWithSelf,
+ b1.AsWeakHandle()));
+ }
+
+ // Calls b1.TestWithSelf().
+ PumpLoop();
+
+ // Shouldn't do anything, since the thread is gone.
+ b2.Call(FROM_HERE, &Base::Test);
+
+ // |b2| shouldn't leak when it's destroyed, even if the original
+ // thread is gone.
+}
+
+TEST_F(WeakHandleTest, InitializedAcrossCopyAssign) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(3);
+
+ EXPECT_TRUE(b.AsWeakHandle().IsInitialized());
+ b.AsWeakHandle().Call(FROM_HERE, &Base::Test);
+
+ {
+ WeakHandle<Base> h(b.AsWeakHandle());
+ EXPECT_TRUE(h.IsInitialized());
+ h.Call(FROM_HERE, &Base::Test);
+ h.Reset();
+ EXPECT_FALSE(h.IsInitialized());
+ }
+
+ {
+ WeakHandle<Base> h;
+ h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+ h.Call(FROM_HERE, &Base::Test);
+ h.Reset();
+ EXPECT_FALSE(h.IsInitialized());
+ }
+
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructor) {
+ StrictMock<Derived> d;
+ EXPECT_CALL(d, Test()).Times(2);
+
+ const WeakHandle<Derived> weak_handle = MakeWeakHandle(d.AsWeakPtr());
+
+ // Should trigger type conversion constructor.
+ const WeakHandle<Base> base_weak_handle(weak_handle);
+ // Should trigger regular copy constructor.
+ const WeakHandle<Derived> derived_weak_handle(weak_handle);
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ base_weak_handle.Call(FROM_HERE, &Base::Test);
+
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+ // Copy constructor shouldn't construct a new |core_|.
+ EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get());
+ derived_weak_handle.Call(FROM_HERE, &Base::Test);
+
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorMakeWeakHandle) {
+ const base::WeakPtr<Derived> weak_ptr;
+
+ // Should trigger type conversion constructor after MakeWeakHandle.
+ WeakHandle<Base> base_weak_handle(MakeWeakHandle(weak_ptr));
+ // Should trigger regular copy constructor after MakeWeakHandle.
+ const WeakHandle<Derived> derived_weak_handle(MakeWeakHandle(weak_ptr));
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorAssignment) {
+ const WeakHandle<Derived> weak_handle =
+ MakeWeakHandle(Derived().AsWeakPtr());
+
+ // Should trigger type conversion constructor before the assignment.
+ WeakHandle<Base> base_weak_handle;
+ base_weak_handle = weak_handle;
+ // Should trigger regular copy constructor before the assignment.
+ WeakHandle<Derived> derived_weak_handle;
+ derived_weak_handle = weak_handle;
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+ // Copy constructor shouldn't construct a new |core_|.
+ EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorUninitialized) {
+ const WeakHandle<Base> base_weak_handle = WeakHandle<Derived>();
+ EXPECT_FALSE(base_weak_handle.IsInitialized());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorUninitializedAssignment) {
+ WeakHandle<Base> base_weak_handle;
+ base_weak_handle = WeakHandle<Derived>();
+ EXPECT_FALSE(base_weak_handle.IsInitialized());
+}
+
+} // namespace browser_sync
diff --git a/sync/internal_api/public/write_node.h b/sync/internal_api/public/write_node.h
new file mode 100644
index 0000000..ba61122
--- /dev/null
+++ b/sync/internal_api/public/write_node.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_WRITE_NODE_H_
+#define SYNC_INTERNAL_API_PUBLIC_WRITE_NODE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "sync/internal_api/public/base_node.h"
+#include "sync/internal_api/public/syncable/model_type.h"
+
+namespace browser_sync {
+class Cryptographer;
+class TestBookmarkModelAssociator;
+}
+
+namespace syncable {
+class Entry;
+class MutableEntry;
+}
+
+namespace sync_pb {
+class AppSpecifics;
+class AutofillSpecifics;
+class AutofillProfileSpecifics;
+class BookmarkSpecifics;
+class EntitySpecifics;
+class ExtensionSpecifics;
+class SessionSpecifics;
+class NigoriSpecifics;
+class PasswordSpecificsData;
+class ThemeSpecifics;
+class TypedUrlSpecifics;
+}
+
+namespace sync_api {
+
+class WriteTransaction;
+
+// WriteNode extends BaseNode to add mutation, and wraps
+// syncable::MutableEntry. A WriteTransaction is needed to create a WriteNode.
+class WriteNode : public BaseNode {
+ public:
+ enum InitUniqueByCreationResult {
+ INIT_SUCCESS,
+ // The tag passed into this method was empty.
+ INIT_FAILED_EMPTY_TAG,
+ // An entry with this tag already exists.
+ INIT_FAILED_ENTRY_ALREADY_EXISTS,
+ // The constructor for a new MutableEntry with the specified data failed.
+ INIT_FAILED_COULD_NOT_CREATE_ENTRY,
+ // Setting the predecessor failed
+ INIT_FAILED_SET_PREDECESSOR,
+ };
+
+ // Create a WriteNode using the given transaction.
+ explicit WriteNode(WriteTransaction* transaction);
+ virtual ~WriteNode();
+
+ // A client must use one (and only one) of the following Init variants to
+ // populate the node.
+
+ // BaseNode implementation.
+ virtual InitByLookupResult InitByIdLookup(int64 id) OVERRIDE;
+ virtual InitByLookupResult InitByClientTagLookup(
+ syncable::ModelType model_type,
+ const std::string& tag) OVERRIDE;
+
+ // Create a new node with the specified parent and predecessor. |model_type|
+ // dictates the type of the item, and controls which EntitySpecifics proto
+ // extension can be used with this item. Use a NULL |predecessor|
+ // to indicate that this is to be the first child.
+ // |predecessor| must be a child of |new_parent| or NULL. Returns false on
+ // failure.
+ bool InitByCreation(syncable::ModelType model_type,
+ const BaseNode& parent,
+ const BaseNode* predecessor);
+
+ // Create nodes using this function if they're unique items that
+ // you want to fetch using client_tag. Note that the behavior of these
+ // items is slightly different than that of normal items.
+ // Most importantly, if it exists locally, this function will
+ // actually undelete it
+ // Client unique tagged nodes must NOT be folders.
+ InitUniqueByCreationResult InitUniqueByCreation(
+ syncable::ModelType model_type,
+ const BaseNode& parent,
+ const std::string& client_tag);
+
+ // Each server-created permanent node is tagged with a unique string.
+ // Look up the node with the particular tag. If it does not exist,
+ // return false.
+ InitByLookupResult InitByTagLookup(const std::string& tag);
+
+ // These Set() functions correspond to the Get() functions of BaseNode.
+ void SetIsFolder(bool folder);
+ void SetTitle(const std::wstring& title);
+
+ // External ID is a client-only field, so setting it doesn't cause the item to
+ // be synced again.
+ void SetExternalId(int64 external_id);
+
+ // Remove this node and its children.
+ void Remove();
+
+ // Set a new parent and position. Position is specified by |predecessor|; if
+ // it is NULL, the node is moved to the first position. |predecessor| must
+ // be a child of |new_parent| or NULL. Returns false on failure..
+ bool SetPosition(const BaseNode& new_parent, const BaseNode* predecessor);
+
+ // Set the bookmark specifics (url and favicon).
+ // Should only be called if GetModelType() == BOOKMARK.
+ void SetBookmarkSpecifics(const sync_pb::BookmarkSpecifics& specifics);
+
+ // Legacy, bookmark-specific setters that wrap SetBookmarkSpecifics() above.
+ // Should only be called if GetModelType() == BOOKMARK.
+ // TODO(ncarter): Remove these two datatype-specific accessors.
+ void SetURL(const GURL& url);
+ void SetFaviconBytes(const std::vector<unsigned char>& bytes);
+
+ // Generic set specifics method. Will extract the model type from |specifics|.
+ void SetEntitySpecifics(const sync_pb::EntitySpecifics& specifics);
+
+ // Resets the EntitySpecifics for this node based on the unencrypted data.
+ // Will encrypt if necessary.
+ void ResetFromSpecifics();
+
+ // TODO(sync): Remove the setters below when the corresponding data
+ // types are ported to the new sync service API.
+
+ // Set the app specifics (id, update url, enabled state, etc).
+ // Should only be called if GetModelType() == APPS.
+ void SetAppSpecifics(const sync_pb::AppSpecifics& specifics);
+
+ // Set the autofill specifics (name and value).
+ // Should only be called if GetModelType() == AUTOFILL.
+ void SetAutofillSpecifics(const sync_pb::AutofillSpecifics& specifics);
+
+ void SetAutofillProfileSpecifics(
+ const sync_pb::AutofillProfileSpecifics& specifics);
+
+ // Set the nigori specifics.
+ // Should only be called if GetModelType() == NIGORI.
+ void SetNigoriSpecifics(const sync_pb::NigoriSpecifics& specifics);
+
+ // Set the password specifics.
+ // Should only be called if GetModelType() == PASSWORD.
+ void SetPasswordSpecifics(const sync_pb::PasswordSpecificsData& specifics);
+
+ // Set the theme specifics (name and value).
+ // Should only be called if GetModelType() == THEME.
+ void SetThemeSpecifics(const sync_pb::ThemeSpecifics& specifics);
+
+ // Set the typed_url specifics (url, title, typed_count, etc).
+ // Should only be called if GetModelType() == TYPED_URLS.
+ void SetTypedUrlSpecifics(const sync_pb::TypedUrlSpecifics& specifics);
+
+ // Set the extension specifics (id, update url, enabled state, etc).
+ // Should only be called if GetModelType() == EXTENSIONS.
+ void SetExtensionSpecifics(const sync_pb::ExtensionSpecifics& specifics);
+
+ // Set the session specifics (windows, tabs, navigations etc.).
+ // Should only be called if GetModelType() == SESSIONS.
+ void SetSessionSpecifics(const sync_pb::SessionSpecifics& specifics);
+
+ // Implementation of BaseNode's abstract virtual accessors.
+ virtual const syncable::Entry* GetEntry() const OVERRIDE;
+
+ virtual const BaseTransaction* GetTransaction() const OVERRIDE;
+
+ private:
+ friend class browser_sync::TestBookmarkModelAssociator;
+ FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, EncryptBookmarksWithLegacyData);
+
+ void* operator new(size_t size); // Node is meant for stack use only.
+
+ // Helper to set model type. This will clear any specifics data.
+ void PutModelType(syncable::ModelType model_type);
+
+ // Helper to set the previous node.
+ bool PutPredecessor(const BaseNode* predecessor) WARN_UNUSED_RESULT;
+
+ // Sets IS_UNSYNCED and SYNCING to ensure this entry is considered in an
+ // upcoming commit pass.
+ void MarkForSyncing();
+
+ // The underlying syncable object which this class wraps.
+ syncable::MutableEntry* entry_;
+
+ // The sync API transaction that is the parent of this node.
+ WriteTransaction* transaction_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriteNode);
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_WRITE_NODE_H_
diff --git a/sync/internal_api/public/write_transaction.h b/sync/internal_api/public/write_transaction.h
new file mode 100644
index 0000000..1321ed1
--- /dev/null
+++ b/sync/internal_api/public/write_transaction.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_WRITE_TRANSACTION_H_
+#define SYNC_INTERNAL_API_PUBLIC_WRITE_TRANSACTION_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "sync/internal_api/public/base_transaction.h"
+
+namespace syncable {
+class BaseTransaction;
+class WriteTransaction;
+} // namespace syncable
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace sync_api {
+
+// Sync API's WriteTransaction is a read/write BaseTransaction. It wraps
+// a syncable::WriteTransaction.
+//
+// NOTE: Only a single model type can be mutated for a given
+// WriteTransaction.
+class WriteTransaction : public BaseTransaction {
+ public:
+ // Start a new read/write transaction.
+ WriteTransaction(const tracked_objects::Location& from_here,
+ UserShare* share);
+ virtual ~WriteTransaction();
+
+ // Provide access to the syncable.h transaction from the API WriteNode.
+ virtual syncable::BaseTransaction* GetWrappedTrans() const OVERRIDE;
+ syncable::WriteTransaction* GetWrappedWriteTrans() { return transaction_; }
+
+ protected:
+ WriteTransaction() {}
+
+ void SetTransaction(syncable::WriteTransaction* trans) {
+ transaction_ = trans;
+ }
+
+ private:
+ void* operator new(size_t size); // Transaction is meant for stack use only.
+
+ // The underlying syncable object which this class wraps.
+ syncable::WriteTransaction* transaction_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriteTransaction);
+};
+
+} // namespace sync_api
+
+#endif // SYNC_INTERNAL_API_PUBLIC_WRITE_TRANSACTION_H_