diff options
Diffstat (limited to 'sync/internal_api/public')
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_ |