summaryrefslogtreecommitdiffstats
path: root/sync/protocol
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:35:42 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:35:42 +0000
commitc1c32c85357f14756247b04b8b5ae41b05bf2e16 (patch)
tree58f25f64e1fa592e8daf276ef69901cd2218f929 /sync/protocol
parent63ee33bde2ec8471a70f0f0ec6a1962dd07fc8ab (diff)
downloadchromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.zip
chromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.tar.gz
chromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.tar.bz2
[Sync] Move 'sync' target to sync/
Also move related test files. Move WriteNode::UpdateEntryWithEncryption to nigori_util.h. Clean up defines and dependencies. In particular, get rid of SYNC_ENGINE_VERSION_STRING and hard-code the string in the single place it's used. Rename data_encryption.* to data_encryption_win.* and add a pragma for crypt32.lib. Clean up exit-time constructor warnings in sync{able,er}_unittest.cc. Remove some unused files. BUG=117585 TEST= TBR=jhawkins@chromium.org Review URL: https://chromiumcodereview.appspot.com/9699057 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126872 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/protocol')
-rw-r--r--sync/protocol/DEPS3
-rw-r--r--sync/protocol/proto_enum_conversions.cc112
-rw-r--r--sync/protocol/proto_enum_conversions.h40
-rw-r--r--sync/protocol/proto_enum_conversions_unittest.cc62
-rw-r--r--sync/protocol/proto_value_conversions.cc413
-rw-r--r--sync/protocol/proto_value_conversions.h142
-rw-r--r--sync/protocol/proto_value_conversions_unittest.cc191
-rw-r--r--sync/protocol/service_constants.h23
-rw-r--r--sync/protocol/sync_protocol_error.cc63
-rw-r--r--sync/protocol/sync_protocol_error.h81
10 files changed, 1130 insertions, 0 deletions
diff --git a/sync/protocol/DEPS b/sync/protocol/DEPS
new file mode 100644
index 0000000..a21ff1a
--- /dev/null
+++ b/sync/protocol/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sync/syncable/model_type.h",
+]
diff --git a/sync/protocol/proto_enum_conversions.cc b/sync/protocol/proto_enum_conversions.cc
new file mode 100644
index 0000000..47a0016
--- /dev/null
+++ b/sync/protocol/proto_enum_conversions.cc
@@ -0,0 +1,112 @@
+// 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.
+
+// Keep this file in sync with the .proto files in this directory.
+
+#include "sync/protocol/proto_enum_conversions.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace browser_sync {
+
+#define ASSERT_ENUM_BOUNDS(enum_parent, enum_type, enum_min, enum_max) \
+ COMPILE_ASSERT(enum_parent::enum_type##_MIN == enum_parent::enum_min, \
+ enum_type##_MIN_not_##enum_min); \
+ COMPILE_ASSERT(enum_parent::enum_type##_MAX == enum_parent::enum_max, \
+ enum_type##_MAX_not_##enum_max);
+
+#define ENUM_CASE(enum_parent, enum_value) \
+ case enum_parent::enum_value: return #enum_value
+
+const char* GetBrowserTypeString(
+ sync_pb::SessionWindow::BrowserType browser_type) {
+ ASSERT_ENUM_BOUNDS(sync_pb::SessionWindow, BrowserType,
+ TYPE_TABBED, TYPE_POPUP);
+ switch (browser_type) {
+ ENUM_CASE(sync_pb::SessionWindow, TYPE_TABBED);
+ ENUM_CASE(sync_pb::SessionWindow, TYPE_POPUP);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetPageTransitionString(
+ sync_pb::TabNavigation::PageTransition page_transition) {
+ ASSERT_ENUM_BOUNDS(sync_pb::TabNavigation, PageTransition,
+ LINK, CHAIN_END);
+ switch (page_transition) {
+ ENUM_CASE(sync_pb::TabNavigation, LINK);
+ ENUM_CASE(sync_pb::TabNavigation, TYPED);
+ ENUM_CASE(sync_pb::TabNavigation, AUTO_BOOKMARK);
+ ENUM_CASE(sync_pb::TabNavigation, AUTO_SUBFRAME);
+ ENUM_CASE(sync_pb::TabNavigation, MANUAL_SUBFRAME);
+ ENUM_CASE(sync_pb::TabNavigation, GENERATED);
+ ENUM_CASE(sync_pb::TabNavigation, START_PAGE);
+ ENUM_CASE(sync_pb::TabNavigation, FORM_SUBMIT);
+ ENUM_CASE(sync_pb::TabNavigation, RELOAD);
+ ENUM_CASE(sync_pb::TabNavigation, KEYWORD);
+ ENUM_CASE(sync_pb::TabNavigation, KEYWORD_GENERATED);
+ ENUM_CASE(sync_pb::TabNavigation, CHAIN_START);
+ ENUM_CASE(sync_pb::TabNavigation, CHAIN_END);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetPageTransitionQualifierString(
+ sync_pb::TabNavigation::PageTransitionQualifier
+ page_transition_qualifier) {
+ ASSERT_ENUM_BOUNDS(sync_pb::TabNavigation, PageTransitionQualifier,
+ CLIENT_REDIRECT, SERVER_REDIRECT);
+ switch (page_transition_qualifier) {
+ ENUM_CASE(sync_pb::TabNavigation, CLIENT_REDIRECT);
+ ENUM_CASE(sync_pb::TabNavigation, SERVER_REDIRECT);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetUpdatesSourceString(
+ sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source) {
+ ASSERT_ENUM_BOUNDS(sync_pb::GetUpdatesCallerInfo, GetUpdatesSource,
+ UNKNOWN, DATATYPE_REFRESH);
+ switch (updates_source) {
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, UNKNOWN);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, FIRST_UPDATE);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, LOCAL);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, NOTIFICATION);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, PERIODIC);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, SYNC_CYCLE_CONTINUATION);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, CLEAR_PRIVATE_DATA);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, NEWLY_SUPPORTED_DATATYPE);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, MIGRATION);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, NEW_CLIENT);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, RECONFIGURATION);
+ ENUM_CASE(sync_pb::GetUpdatesCallerInfo, DATATYPE_REFRESH);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetDeviceTypeString(
+ sync_pb::SessionHeader::DeviceType device_type) {
+ ASSERT_ENUM_BOUNDS(sync_pb::SessionHeader, DeviceType, TYPE_WIN, TYPE_TABLET);
+ switch (device_type) {
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_WIN);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_MAC);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_LINUX);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_CROS);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_OTHER);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_PHONE);
+ ENUM_CASE(sync_pb::SessionHeader, TYPE_TABLET);
+ }
+ NOTREACHED();
+ return "";
+}
+
+#undef ASSERT_ENUM_BOUNDS
+#undef ENUM_CASE
+
+} // namespace
diff --git a/sync/protocol/proto_enum_conversions.h b/sync/protocol/proto_enum_conversions.h
new file mode 100644
index 0000000..fb8d44b
--- /dev/null
+++ b/sync/protocol/proto_enum_conversions.h
@@ -0,0 +1,40 @@
+// 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_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
+#define SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
+#pragma once
+
+// Keep this file in sync with the .proto files in this directory.
+
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
+
+// Utility functions to get the string equivalent for some sync proto
+// enums.
+
+namespace browser_sync {
+
+// The returned strings (which don't have to be freed) are in ASCII.
+// The result of passing in an invalid enum value is undefined.
+
+const char* GetBrowserTypeString(
+ sync_pb::SessionWindow::BrowserType browser_type);
+
+const char* GetPageTransitionString(
+ sync_pb::TabNavigation::PageTransition page_transition);
+
+const char* GetPageTransitionQualifierString(
+ sync_pb::TabNavigation::PageTransitionQualifier
+ page_transition_qualifier);
+
+const char* GetUpdatesSourceString(
+ sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source);
+
+const char* GetDeviceTypeString(
+ sync_pb::SessionHeader::DeviceType device_type);
+
+} // namespace browser_sync
+
+#endif // SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
diff --git a/sync/protocol/proto_enum_conversions_unittest.cc b/sync/protocol/proto_enum_conversions_unittest.cc
new file mode 100644
index 0000000..2445a30
--- /dev/null
+++ b/sync/protocol/proto_enum_conversions_unittest.cc
@@ -0,0 +1,62 @@
+// 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.
+
+// Keep this file in sync with the .proto files in this directory.
+
+#include "sync/protocol/proto_enum_conversions.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+namespace {
+
+class ProtoEnumConversionsTest : public testing::Test {
+};
+
+template <class T>
+void TestEnumStringFunction(const char* (*enum_string_fn)(T),
+ int enum_min, int enum_max) {
+ for (int i = enum_min; i <= enum_max; ++i) {
+ const std::string& str = enum_string_fn(static_cast<T>(i));
+ EXPECT_FALSE(str.empty());
+ }
+}
+
+TEST_F(ProtoEnumConversionsTest, GetBrowserTypeString) {
+ TestEnumStringFunction(
+ GetBrowserTypeString,
+ sync_pb::SessionWindow::BrowserType_MIN,
+ sync_pb::SessionWindow::BrowserType_MAX);
+}
+
+TEST_F(ProtoEnumConversionsTest, GetPageTransitionString) {
+ // We have a gap, so we need to do two ranges.
+ TestEnumStringFunction(
+ GetPageTransitionString,
+ sync_pb::TabNavigation::PageTransition_MIN,
+ sync_pb::TabNavigation::KEYWORD_GENERATED);
+ TestEnumStringFunction(
+ GetPageTransitionString,
+ sync_pb::TabNavigation::CHAIN_START,
+ sync_pb::TabNavigation::PageTransition_MAX);
+}
+
+TEST_F(ProtoEnumConversionsTest, GetPageTransitionQualifierString) {
+ TestEnumStringFunction(
+ GetPageTransitionQualifierString,
+ sync_pb::TabNavigation::PageTransitionQualifier_MIN,
+ sync_pb::TabNavigation::PageTransitionQualifier_MAX);
+}
+
+TEST_F(ProtoEnumConversionsTest, GetUpdatesSourceString) {
+ TestEnumStringFunction(
+ GetUpdatesSourceString,
+ sync_pb::GetUpdatesCallerInfo::GetUpdatesSource_MIN,
+ sync_pb::GetUpdatesCallerInfo::GetUpdatesSource_MAX);
+}
+
+} // namespace
+} // namespace browser_sync
diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc
new file mode 100644
index 0000000..009c030
--- /dev/null
+++ b/sync/protocol/proto_value_conversions.cc
@@ -0,0 +1,413 @@
+// 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.
+
+// Keep this file in sync with the .proto files in this directory.
+
+#include "sync/protocol/proto_value_conversions.h"
+
+#include "base/base64.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "sync/protocol/app_notification_specifics.pb.h"
+#include "sync/protocol/app_setting_specifics.pb.h"
+#include "sync/protocol/app_specifics.pb.h"
+#include "sync/protocol/autofill_specifics.pb.h"
+#include "sync/protocol/bookmark_specifics.pb.h"
+#include "sync/protocol/encryption.pb.h"
+#include "sync/protocol/extension_setting_specifics.pb.h"
+#include "sync/protocol/extension_specifics.pb.h"
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/protocol/password_specifics.pb.h"
+#include "sync/protocol/preference_specifics.pb.h"
+#include "sync/protocol/proto_enum_conversions.h"
+#include "sync/protocol/search_engine_specifics.pb.h"
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
+#include "sync/protocol/theme_specifics.pb.h"
+#include "sync/protocol/typed_url_specifics.pb.h"
+
+namespace browser_sync {
+
+namespace {
+
+// Basic Type -> Value functions.
+
+StringValue* MakeInt64Value(int64 x) {
+ return Value::CreateStringValue(base::Int64ToString(x));
+}
+
+// TODO(akalin): Perhaps make JSONWriter support BinaryValue and use
+// that instead of a StringValue.
+StringValue* MakeBytesValue(const std::string& bytes) {
+ std::string bytes_base64;
+ if (!base::Base64Encode(bytes, &bytes_base64)) {
+ NOTREACHED();
+ }
+ return Value::CreateStringValue(bytes_base64);
+}
+
+// T is the enum type.
+template <class T>
+StringValue* MakeEnumValue(T t, const char* (*converter_fn)(T)) {
+ return Value::CreateStringValue(converter_fn(t));
+}
+
+// T is the field type, F is either RepeatedField or RepeatedPtrField,
+// and V is a subclass of Value.
+template <class T, class F, class V>
+ListValue* MakeRepeatedValue(const F& fields, V* (*converter_fn)(T)) {
+ ListValue* list = new ListValue();
+ for (typename F::const_iterator it = fields.begin(); it != fields.end();
+ ++it) {
+ list->Append(converter_fn(*it));
+ }
+ return list;
+}
+
+} // namespace
+
+// Helper macros to reduce the amount of boilerplate.
+
+#define SET(field, fn) value->Set(#field, fn(proto.field()))
+#define SET_REP(field, fn) \
+ value->Set(#field, MakeRepeatedValue(proto.field(), fn))
+#define SET_ENUM(field, fn) \
+ value->Set(#field, MakeEnumValue(proto.field(), fn))
+
+#define SET_BOOL(field) SET(field, Value::CreateBooleanValue)
+#define SET_BYTES(field) SET(field, MakeBytesValue)
+#define SET_INT32(field) SET(field, MakeInt64Value)
+#define SET_INT32_REP(field) SET_REP(field, MakeInt64Value)
+#define SET_INT64(field) SET(field, MakeInt64Value)
+#define SET_INT64_REP(field) SET_REP(field, MakeInt64Value)
+#define SET_STR(field) SET(field, Value::CreateStringValue)
+#define SET_STR_REP(field) \
+ value->Set(#field, \
+ MakeRepeatedValue<const std::string&, \
+ google::protobuf::RepeatedPtrField< \
+ std::string >, \
+ StringValue>(proto.field(), \
+ Value::CreateStringValue))
+
+#define SET_FIELD(field, fn) \
+ do { \
+ if (specifics.has_##field()) { \
+ value->Set(#field, fn(specifics.field())); \
+ } \
+ } while (0)
+
+// If you add another macro, don't forget to add an #undef at the end
+// of this file, too.
+
+DictionaryValue* EncryptedDataToValue(const sync_pb::EncryptedData& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(key_name);
+ // TODO(akalin): Shouldn't blob be of type bytes instead of string?
+ SET_BYTES(blob);
+ return value;
+}
+
+DictionaryValue* AppSettingsToValue(
+ const sync_pb::AppNotificationSettings& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_BOOL(initial_setup_done);
+ SET_BOOL(disabled);
+ SET_STR(oauth_client_id);
+ return value;
+}
+
+DictionaryValue* SessionHeaderToValue(
+ const sync_pb::SessionHeader& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_REP(window, SessionWindowToValue);
+ SET_STR(client_name);
+ SET_ENUM(device_type, GetDeviceTypeString);
+ return value;
+}
+
+DictionaryValue* SessionTabToValue(
+ const sync_pb::SessionTab& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_INT32(tab_id);
+ SET_INT32(window_id);
+ SET_INT32(tab_visual_index);
+ SET_INT32(current_navigation_index);
+ SET_BOOL(pinned);
+ SET_STR(extension_app_id);
+ SET_REP(navigation, TabNavigationToValue);
+ return value;
+}
+
+DictionaryValue* SessionWindowToValue(
+ const sync_pb::SessionWindow& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_INT32(window_id);
+ SET_INT32(selected_tab_index);
+ SET_INT32_REP(tab);
+ SET_ENUM(browser_type, GetBrowserTypeString);
+ return value;
+}
+
+DictionaryValue* TabNavigationToValue(
+ const sync_pb::TabNavigation& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_INT32(index);
+ SET_STR(virtual_url);
+ SET_STR(referrer);
+ SET_STR(title);
+ SET_STR(state);
+ SET_ENUM(page_transition, GetPageTransitionString);
+ SET_ENUM(navigation_qualifier, GetPageTransitionQualifierString);
+ return value;
+}
+
+DictionaryValue* PasswordSpecificsDataToValue(
+ const sync_pb::PasswordSpecificsData& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_INT32(scheme);
+ SET_STR(signon_realm);
+ SET_STR(origin);
+ SET_STR(action);
+ SET_STR(username_element);
+ SET_STR(username_value);
+ SET_STR(password_element);
+ value->SetString("password_value", "<redacted>");
+ SET_BOOL(ssl_valid);
+ SET_BOOL(preferred);
+ SET_INT64(date_created);
+ SET_BOOL(blacklisted);
+ return value;
+}
+
+DictionaryValue* DeviceInformationToValue(
+ const sync_pb::DeviceInformation& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(cache_guid);
+ SET_STR(name);
+ SET_STR(platform);
+ SET_STR(chrome_version);
+ return value;
+}
+
+DictionaryValue* AppNotificationToValue(
+ const sync_pb::AppNotification& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(guid);
+ SET_STR(app_id);
+ SET_INT64(creation_timestamp_ms);
+ SET_STR(title);
+ SET_STR(body_text);
+ SET_STR(link_url);
+ SET_STR(link_text);
+ return value;
+}
+
+DictionaryValue* AppSettingSpecificsToValue(
+ const sync_pb::AppSettingSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET(extension_setting, ExtensionSettingSpecificsToValue);
+ return value;
+}
+
+DictionaryValue* AppSpecificsToValue(
+ const sync_pb::AppSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET(extension, ExtensionSpecificsToValue);
+ SET(notification_settings, AppSettingsToValue);
+ SET_STR(app_launch_ordinal);
+ SET_STR(page_ordinal);
+
+ return value;
+}
+
+DictionaryValue* AutofillSpecificsToValue(
+ const sync_pb::AutofillSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(name);
+ SET_STR(value);
+ SET_INT64_REP(usage_timestamp);
+ SET(profile, AutofillProfileSpecificsToValue);
+ return value;
+}
+
+DictionaryValue* AutofillProfileSpecificsToValue(
+ const sync_pb::AutofillProfileSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(label);
+ SET_STR(guid);
+
+ SET_STR_REP(name_first);
+ SET_STR_REP(name_middle);
+ SET_STR_REP(name_last);
+ SET_STR_REP(email_address);
+ SET_STR(company_name);
+
+ SET_STR(address_home_line1);
+ SET_STR(address_home_line2);
+ SET_STR(address_home_city);
+ SET_STR(address_home_state);
+ SET_STR(address_home_zip);
+ SET_STR(address_home_country);
+
+ SET_STR_REP(phone_home_whole_number);
+ return value;
+}
+
+DictionaryValue* BookmarkSpecificsToValue(
+ const sync_pb::BookmarkSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(url);
+ SET_BYTES(favicon);
+ SET_STR(title);
+ return value;
+}
+
+DictionaryValue* ExtensionSettingSpecificsToValue(
+ const sync_pb::ExtensionSettingSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(extension_id);
+ SET_STR(key);
+ SET_STR(value);
+ return value;
+}
+
+DictionaryValue* ExtensionSpecificsToValue(
+ const sync_pb::ExtensionSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(id);
+ SET_STR(version);
+ SET_STR(update_url);
+ SET_BOOL(enabled);
+ SET_BOOL(incognito_enabled);
+ SET_STR(name);
+ return value;
+}
+
+DictionaryValue* NigoriSpecificsToValue(
+ const sync_pb::NigoriSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET(encrypted, EncryptedDataToValue);
+ SET_BOOL(using_explicit_passphrase);
+ SET_BOOL(encrypt_bookmarks);
+ SET_BOOL(encrypt_preferences);
+ SET_BOOL(encrypt_autofill_profile);
+ SET_BOOL(encrypt_autofill);
+ SET_BOOL(encrypt_themes);
+ SET_BOOL(encrypt_typed_urls);
+ SET_BOOL(encrypt_extension_settings);
+ SET_BOOL(encrypt_extensions);
+ SET_BOOL(encrypt_sessions);
+ SET_BOOL(encrypt_app_settings);
+ SET_BOOL(encrypt_apps);
+ SET_BOOL(encrypt_search_engines);
+ SET_BOOL(sync_tabs);
+ SET_BOOL(encrypt_everything);
+ SET_REP(device_information, DeviceInformationToValue);
+ return value;
+}
+
+DictionaryValue* PasswordSpecificsToValue(
+ const sync_pb::PasswordSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET(encrypted, EncryptedDataToValue);
+ return value;
+}
+
+DictionaryValue* PreferenceSpecificsToValue(
+ const sync_pb::PreferenceSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(name);
+ SET_STR(value);
+ return value;
+}
+
+DictionaryValue* SearchEngineSpecificsToValue(
+ const sync_pb::SearchEngineSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(short_name);
+ SET_STR(keyword);
+ SET_STR(favicon_url);
+ SET_STR(url);
+ SET_BOOL(safe_for_autoreplace);
+ SET_STR(originating_url);
+ SET_INT64(date_created);
+ SET_STR(input_encodings);
+ SET_BOOL(show_in_default_list);
+ SET_STR(suggestions_url);
+ SET_INT32(prepopulate_id);
+ SET_BOOL(autogenerate_keyword);
+ SET_STR(instant_url);
+ SET_INT64(last_modified);
+ SET_STR(sync_guid);
+ return value;
+}
+
+DictionaryValue* SessionSpecificsToValue(
+ const sync_pb::SessionSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(session_tag);
+ SET(header, SessionHeaderToValue);
+ SET(tab, SessionTabToValue);
+ return value;
+}
+
+DictionaryValue* ThemeSpecificsToValue(
+ const sync_pb::ThemeSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_BOOL(use_custom_theme);
+ SET_BOOL(use_system_theme_by_default);
+ SET_STR(custom_theme_name);
+ SET_STR(custom_theme_id);
+ SET_STR(custom_theme_update_url);
+ return value;
+}
+
+DictionaryValue* TypedUrlSpecificsToValue(
+ const sync_pb::TypedUrlSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(url);
+ SET_STR(title);
+ SET_BOOL(hidden);
+ SET_INT64_REP(visits);
+ SET_INT32_REP(visit_transitions);
+ return value;
+}
+
+DictionaryValue* EntitySpecificsToValue(
+ const sync_pb::EntitySpecifics& specifics) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_FIELD(app, AppSpecificsToValue);
+ SET_FIELD(app_notification, AppNotificationToValue);
+ SET_FIELD(app_setting, AppSettingSpecificsToValue);
+ SET_FIELD(autofill, AutofillSpecificsToValue);
+ SET_FIELD(autofill_profile, AutofillProfileSpecificsToValue);
+ SET_FIELD(bookmark, BookmarkSpecificsToValue);
+ SET_FIELD(extension, ExtensionSpecificsToValue);
+ SET_FIELD(extension_setting, ExtensionSettingSpecificsToValue);
+ SET_FIELD(nigori, NigoriSpecificsToValue);
+ SET_FIELD(password, PasswordSpecificsToValue);
+ SET_FIELD(preference, PreferenceSpecificsToValue);
+ SET_FIELD(search_engine, SearchEngineSpecificsToValue);
+ SET_FIELD(session, SessionSpecificsToValue);
+ SET_FIELD(theme, ThemeSpecificsToValue);
+ SET_FIELD(typed_url, TypedUrlSpecificsToValue);
+ return value;
+}
+
+#undef SET
+#undef SET_REP
+
+#undef SET_BOOL
+#undef SET_BYTES
+#undef SET_INT32
+#undef SET_INT64
+#undef SET_INT64_REP
+#undef SET_STR
+#undef SET_STR_REP
+
+#undef SET_FIELD
+
+} // namespace browser_sync
diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h
new file mode 100644
index 0000000..79bf1b1
--- /dev/null
+++ b/sync/protocol/proto_value_conversions.h
@@ -0,0 +1,142 @@
+// 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.
+
+// Keep this file in sync with the .proto files in this directory.
+
+#ifndef SYNC_PROTOCOL_PROTO_VALUE_CONVERSIONS_H_
+#define SYNC_PROTOCOL_PROTO_VALUE_CONVERSIONS_H_
+#pragma once
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace sync_pb {
+class AppNotification;
+class AppNotificationSettings;
+class AppSettingSpecifics;
+class AppSpecifics;
+class AutofillProfileSpecifics;
+class AutofillSpecifics;
+class BookmarkSpecifics;
+class DeviceInformation;
+class EncryptedData;
+class EntitySpecifics;
+class ExtensionSettingSpecifics;
+class ExtensionSpecifics;
+class NigoriSpecifics;
+class PasswordSpecifics;
+class PasswordSpecificsData;
+class PreferenceSpecifics;
+class SearchEngineSpecifics;
+class SessionHeader;
+class SessionSpecifics;
+class SessionTab;
+class SessionWindow;
+class TabNavigation;
+class ThemeSpecifics;
+class TypedUrlSpecifics;
+} // namespace sync_pb
+
+// Utility functions to convert sync protocol buffers to dictionaries.
+// Each protocol field is mapped to a key of the same name. Repeated
+// fields are mapped to array values and sub-messages are mapped to
+// sub-dictionary values.
+//
+// TODO(akalin): Add has_* information.
+//
+// TODO(akalin): Improve enum support.
+
+namespace browser_sync {
+
+// Ownership of all returned DictionaryValues are transferred to the
+// caller.
+
+// TODO(akalin): Perhaps extend this to decrypt?
+base::DictionaryValue* EncryptedDataToValue(
+ const sync_pb::EncryptedData& encrypted_data);
+
+// Sub-protocol of AppSpecifics.
+base::DictionaryValue* AppSettingsToValue(
+ const sync_pb::AppNotificationSettings& app_notification_settings);
+
+// Sub-protocols of SessionSpecifics.
+
+base::DictionaryValue* SessionHeaderToValue(
+ const sync_pb::SessionHeader& session_header);
+
+base::DictionaryValue* SessionTabToValue(
+ const sync_pb::SessionTab& session_tab);
+
+base::DictionaryValue* SessionWindowToValue(
+ const sync_pb::SessionWindow& session_window);
+
+base::DictionaryValue* TabNavigationToValue(
+ const sync_pb::TabNavigation& tab_navigation);
+
+// Sub-protocol of PasswordSpecifics.
+
+base::DictionaryValue* PasswordSpecificsDataToValue(
+ const sync_pb::PasswordSpecificsData& password_specifics_data);
+
+// Sub-protocol of NigoriSpecifics.
+
+base::DictionaryValue* DeviceInformationToValue(
+ const sync_pb::DeviceInformation& device_information);
+
+// Main *SpecificsToValue functions.
+
+base::DictionaryValue* AppNotificationToValue(
+ const sync_pb::AppNotification& app_notification_specifics);
+
+base::DictionaryValue* AppSettingSpecificsToValue(
+ const sync_pb::AppSettingSpecifics& app_setting_specifics);
+
+base::DictionaryValue* AppSpecificsToValue(
+ const sync_pb::AppSpecifics& app_specifics);
+
+base::DictionaryValue* AutofillSpecificsToValue(
+ const sync_pb::AutofillSpecifics& autofill_specifics);
+
+base::DictionaryValue* AutofillProfileSpecificsToValue(
+ const sync_pb::AutofillProfileSpecifics& autofill_profile_specifics);
+
+base::DictionaryValue* BookmarkSpecificsToValue(
+ const sync_pb::BookmarkSpecifics& bookmark_specifics);
+
+base::DictionaryValue* ExtensionSettingSpecificsToValue(
+ const sync_pb::ExtensionSettingSpecifics& extension_setting_specifics);
+
+base::DictionaryValue* ExtensionSpecificsToValue(
+ const sync_pb::ExtensionSpecifics& extension_specifics);
+
+base::DictionaryValue* NigoriSpecificsToValue(
+ const sync_pb::NigoriSpecifics& nigori_specifics);
+
+base::DictionaryValue* PasswordSpecificsToValue(
+ const sync_pb::PasswordSpecifics& password_specifics);
+
+base::DictionaryValue* PreferenceSpecificsToValue(
+ const sync_pb::PreferenceSpecifics& password_specifics);
+
+base::DictionaryValue* SearchEngineSpecificsToValue(
+ const sync_pb::SearchEngineSpecifics& search_engine_specifics);
+
+base::DictionaryValue* SessionSpecificsToValue(
+ const sync_pb::SessionSpecifics& session_specifics);
+
+base::DictionaryValue* ThemeSpecificsToValue(
+ const sync_pb::ThemeSpecifics& theme_specifics);
+
+base::DictionaryValue* TypedUrlSpecificsToValue(
+ const sync_pb::TypedUrlSpecifics& typed_url_specifics);
+
+// Any present extensions are mapped to sub-dictionary values with the
+// key equal to the extension name.
+base::DictionaryValue* EntitySpecificsToValue(
+ const sync_pb::EntitySpecifics& specifics);
+
+} // namespace browser_sync
+
+#endif // SYNC_PROTOCOL_PROTO_VALUE_CONVERSIONS_H_
diff --git a/sync/protocol/proto_value_conversions_unittest.cc b/sync/protocol/proto_value_conversions_unittest.cc
new file mode 100644
index 0000000..3d96378
--- /dev/null
+++ b/sync/protocol/proto_value_conversions_unittest.cc
@@ -0,0 +1,191 @@
+// 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.
+
+// Keep this file in sync with the .proto files in this directory.
+
+#include "sync/protocol/proto_value_conversions.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "sync/protocol/app_notification_specifics.pb.h"
+#include "sync/protocol/app_setting_specifics.pb.h"
+#include "sync/protocol/app_specifics.pb.h"
+#include "sync/protocol/autofill_specifics.pb.h"
+#include "sync/protocol/bookmark_specifics.pb.h"
+#include "sync/protocol/encryption.pb.h"
+#include "sync/protocol/extension_setting_specifics.pb.h"
+#include "sync/protocol/extension_specifics.pb.h"
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/protocol/password_specifics.pb.h"
+#include "sync/protocol/preference_specifics.pb.h"
+#include "sync/protocol/search_engine_specifics.pb.h"
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
+#include "sync/protocol/theme_specifics.pb.h"
+#include "sync/protocol/typed_url_specifics.pb.h"
+#include "sync/syncable/model_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+namespace {
+
+class ProtoValueConversionsTest : public testing::Test {
+ protected:
+ template <class T>
+ void TestSpecificsToValue(
+ DictionaryValue* (*specifics_to_value)(const T&)) {
+ const T& specifics(T::default_instance());
+ scoped_ptr<DictionaryValue> value(specifics_to_value(specifics));
+ // We can't do much but make sure that the returned value has
+ // something in it.
+ EXPECT_FALSE(value->empty());
+ }
+};
+
+TEST_F(ProtoValueConversionsTest, ProtoChangeCheck) {
+ // If this number changes, that means we added or removed a data
+ // type. Don't forget to add a unit test for {New
+ // type}SpecificsToValue below.
+ EXPECT_EQ(17, syncable::MODEL_TYPE_COUNT);
+
+ // We'd also like to check if we changed any field in our messages.
+ // However, that's hard to do: sizeof could work, but it's
+ // platform-dependent. default_instance().ByteSize() won't change
+ // for most changes, since most of our fields are optional. So we
+ // just settle for comments in the proto files.
+}
+
+TEST_F(ProtoValueConversionsTest, EncryptedDataToValue) {
+ TestSpecificsToValue(EncryptedDataToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, SessionHeaderToValue) {
+ TestSpecificsToValue(SessionHeaderToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, SessionTabToValue) {
+ TestSpecificsToValue(SessionTabToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, SessionWindowToValue) {
+ TestSpecificsToValue(SessionWindowToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, TabNavigationToValue) {
+ TestSpecificsToValue(TabNavigationToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, PasswordSpecificsData) {
+ sync_pb::PasswordSpecificsData specifics;
+ specifics.set_password_value("secret");
+ scoped_ptr<DictionaryValue> value(PasswordSpecificsDataToValue(specifics));
+ EXPECT_FALSE(value->empty());
+ std::string password_value;
+ EXPECT_TRUE(value->GetString("password_value", &password_value));
+ EXPECT_EQ("<redacted>", password_value);
+}
+
+TEST_F(ProtoValueConversionsTest, AppNotificationToValue) {
+ TestSpecificsToValue(AppNotificationToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, AppSettingSpecificsToValue) {
+ sync_pb::AppNotificationSettings specifics;
+ specifics.set_disabled(true);
+ specifics.set_oauth_client_id("some_id_value");
+ scoped_ptr<DictionaryValue> value(AppSettingsToValue(specifics));
+ EXPECT_FALSE(value->empty());
+ bool disabled_value = false;
+ std::string oauth_client_id_value;
+ EXPECT_TRUE(value->GetBoolean("disabled", &disabled_value));
+ EXPECT_EQ(true, disabled_value);
+ EXPECT_TRUE(value->GetString("oauth_client_id", &oauth_client_id_value));
+ EXPECT_EQ("some_id_value", oauth_client_id_value);
+}
+
+TEST_F(ProtoValueConversionsTest, AppSpecificsToValue) {
+ TestSpecificsToValue(AppSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, AutofillSpecificsToValue) {
+ TestSpecificsToValue(AutofillSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, AutofillProfileSpecificsToValue) {
+ TestSpecificsToValue(AutofillProfileSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, BookmarkSpecificsToValue) {
+ TestSpecificsToValue(BookmarkSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, ExtensionSettingSpecificsToValue) {
+ TestSpecificsToValue(ExtensionSettingSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, ExtensionSpecificsToValue) {
+ TestSpecificsToValue(ExtensionSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, NigoriSpecificsToValue) {
+ TestSpecificsToValue(NigoriSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, PasswordSpecificsToValue) {
+ TestSpecificsToValue(PasswordSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, PreferenceSpecificsToValue) {
+ TestSpecificsToValue(PreferenceSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, SearchEngineSpecificsToValue) {
+ TestSpecificsToValue(SearchEngineSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, SessionSpecificsToValue) {
+ TestSpecificsToValue(SessionSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, ThemeSpecificsToValue) {
+ TestSpecificsToValue(ThemeSpecificsToValue);
+}
+
+TEST_F(ProtoValueConversionsTest, TypedUrlSpecificsToValue) {
+ TestSpecificsToValue(TypedUrlSpecificsToValue);
+}
+
+// TODO(akalin): Figure out how to better test EntitySpecificsToValue.
+
+TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) {
+ sync_pb::EntitySpecifics specifics;
+ // Touch the extensions to make sure it shows up in the generated
+ // value.
+#define SET_FIELD(key) (void)specifics.mutable_##key()
+
+ SET_FIELD(app);
+ SET_FIELD(app_notification);
+ SET_FIELD(app_setting);
+ SET_FIELD(autofill);
+ SET_FIELD(autofill_profile);
+ SET_FIELD(bookmark);
+ SET_FIELD(extension);
+ SET_FIELD(extension_setting);
+ SET_FIELD(nigori);
+ SET_FIELD(password);
+ SET_FIELD(preference);
+ SET_FIELD(search_engine);
+ SET_FIELD(session);
+ SET_FIELD(theme);
+ SET_FIELD(typed_url);
+
+#undef SET_FIELD
+
+ scoped_ptr<DictionaryValue> value(EntitySpecificsToValue(specifics));
+ EXPECT_EQ(syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE,
+ static_cast<int>(value->size()));
+}
+
+} // namespace
+} // namespace browser_sync
diff --git a/sync/protocol/service_constants.h b/sync/protocol/service_constants.h
new file mode 100644
index 0000000..83a65b1
--- /dev/null
+++ b/sync/protocol/service_constants.h
@@ -0,0 +1,23 @@
+// 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.
+//
+// Product-specific constants.
+
+#ifndef SYNC_PROTOCOL_SERVICE_CONSTANTS_H_
+#define SYNC_PROTOCOL_SERVICE_CONSTANTS_H_
+#pragma once
+
+// These fixed service names are used to obtain auth cookies for the
+// corresponding services. It might be interesting to make these updateable
+// as well as have the ability to add new ones.
+#define SYNC_SERVICE_NAME "chromiumsync"
+
+#define DEFAULT_SIGNIN_DOMAIN "gmail.com"
+
+#define PRODUCT_NAME_STRING_NARROW "Chromium Browser Sync"
+
+#define PRODUCT_NAME_STRING PRODUCT_NAME_STRING_NARROW
+#define PRODUCT_NAME_STRING_WIDE L##PRODUCT_NAME_STRING
+
+#endif // SYNC_PROTOCOL_SERVICE_CONSTANTS_H_
diff --git a/sync/protocol/sync_protocol_error.cc b/sync/protocol/sync_protocol_error.cc
new file mode 100644
index 0000000..544d98a
--- /dev/null
+++ b/sync/protocol/sync_protocol_error.cc
@@ -0,0 +1,63 @@
+// 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/protocol/sync_protocol_error.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/values.h"
+
+namespace browser_sync {
+#define ENUM_CASE(x) case x: return #x; break;
+
+const char* GetSyncErrorTypeString(SyncProtocolErrorType type) {
+ switch (type) {
+ ENUM_CASE(SYNC_SUCCESS);
+ ENUM_CASE(NOT_MY_BIRTHDAY);
+ ENUM_CASE(THROTTLED);
+ ENUM_CASE(CLEAR_PENDING);
+ ENUM_CASE(TRANSIENT_ERROR);
+ ENUM_CASE(NON_RETRIABLE_ERROR);
+ ENUM_CASE(MIGRATION_DONE);
+ ENUM_CASE(INVALID_CREDENTIAL);
+ ENUM_CASE(UNKNOWN_ERROR);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetClientActionString(ClientAction action) {
+ switch (action) {
+ ENUM_CASE(UPGRADE_CLIENT);
+ ENUM_CASE(CLEAR_USER_DATA_AND_RESYNC);
+ ENUM_CASE(ENABLE_SYNC_ON_ACCOUNT);
+ ENUM_CASE(STOP_AND_RESTART_SYNC);
+ ENUM_CASE(DISABLE_SYNC_ON_CLIENT);
+ ENUM_CASE(UNKNOWN_ACTION);
+ }
+ NOTREACHED();
+ return "";
+}
+
+SyncProtocolError::SyncProtocolError()
+ : error_type(UNKNOWN_ERROR),
+ action(UNKNOWN_ACTION) {
+}
+
+SyncProtocolError::~SyncProtocolError() {
+}
+
+DictionaryValue* SyncProtocolError::ToValue() const {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetString("ErrorType",
+ GetSyncErrorTypeString(error_type));
+ value->SetString("ErrorDescription", error_description);
+ value->SetString("url", url);
+ value->SetString("action", GetClientActionString(action));
+ return value;
+}
+
+} // namespace browser_sync
+
diff --git a/sync/protocol/sync_protocol_error.h b/sync/protocol/sync_protocol_error.h
new file mode 100644
index 0000000..9ae5317
--- /dev/null
+++ b/sync/protocol/sync_protocol_error.h
@@ -0,0 +1,81 @@
+// 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_PROTOCOL_SYNC_PROTOCOL_ERROR_H_
+#define SYNC_PROTOCOL_SYNC_PROTOCOL_ERROR_H_
+#pragma once
+
+#include <string>
+
+#include "base/values.h"
+#include "sync/syncable/model_type.h"
+
+namespace browser_sync{
+
+enum SyncProtocolErrorType {
+ // Success case.
+ SYNC_SUCCESS,
+
+ // Birthday does not match that of the server.
+ NOT_MY_BIRTHDAY,
+
+ // Server is busy. Try later.
+ THROTTLED,
+
+ // Clear user data is being currently executed by the server.
+ CLEAR_PENDING,
+
+ // Server cannot service the request now.
+ TRANSIENT_ERROR,
+
+ // Server does not wish the client to retry any more until the action has
+ // been taken.
+ NON_RETRIABLE_ERROR,
+
+ // Indicates the datatypes have been migrated and the client should resync
+ // them to get the latest progress markers.
+ MIGRATION_DONE,
+
+ // Invalid Credential.
+ INVALID_CREDENTIAL,
+
+ // The default value.
+ UNKNOWN_ERROR
+};
+
+enum ClientAction {
+ // Upgrade the client to latest version.
+ UPGRADE_CLIENT,
+
+ // Clear user data and setup sync again.
+ CLEAR_USER_DATA_AND_RESYNC,
+
+ // Set the bit on the account to enable sync.
+ ENABLE_SYNC_ON_ACCOUNT,
+
+ // Stop sync and restart sync.
+ STOP_AND_RESTART_SYNC,
+
+ // Wipe this client of any sync data.
+ DISABLE_SYNC_ON_CLIENT,
+
+ // The default. No action.
+ UNKNOWN_ACTION
+};
+
+struct SyncProtocolError {
+ SyncProtocolErrorType error_type;
+ std::string error_description;
+ std::string url;
+ ClientAction action;
+ syncable::ModelTypeSet error_data_types;
+ SyncProtocolError();
+ ~SyncProtocolError();
+ DictionaryValue* ToValue() const;
+};
+
+const char* GetSyncErrorTypeString(SyncProtocolErrorType type);
+const char* GetClientActionString(ClientAction action);
+} // namespace browser_sync
+#endif // SYNC_PROTOCOL_SYNC_PROTOCOL_ERROR_H_
+