summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd3
-rw-r--r--chrome/browser/resources/sync_setup_overlay.html7
-rw-r--r--chrome/browser/resources/sync_setup_overlay.js15
-rw-r--r--chrome/browser/sync/profile_sync_service.cc5
-rw-r--r--chrome/browser/ui/webui/sync_setup_handler.cc40
-rw-r--r--chrome/browser/ui/webui/sync_setup_handler_unittest.cc7
-rw-r--r--components/sync_driver/model_association_manager.cc1
-rw-r--r--components/sync_driver/pref_names.cc1
-rw-r--r--components/sync_driver/pref_names.h1
-rw-r--r--components/sync_driver/sync_prefs.cc3
-rw-r--r--components/sync_driver/user_selectable_sync_type.h3
-rw-r--r--sync/internal_api/public/base/model_type.h6
-rw-r--r--sync/internal_api/public/sync_encryption_handler.cc4
-rw-r--r--sync/internal_api/sync_encryption_handler_impl_unittest.cc12
-rw-r--r--sync/protocol/BUILD.gn1
-rw-r--r--sync/protocol/proto_enum_conversions.cc13
-rw-r--r--sync/protocol/proto_enum_conversions.h3
-rw-r--r--sync/protocol/proto_enum_conversions_unittest.cc7
-rw-r--r--sync/protocol/proto_value_conversions.cc10
-rw-r--r--sync/protocol/proto_value_conversions.h4
-rw-r--r--sync/protocol/proto_value_conversions_unittest.cc8
-rw-r--r--sync/protocol/sync.proto2
-rw-r--r--sync/protocol/wifi_credential_specifics.proto58
-rw-r--r--sync/sync.gyp1
-rw-r--r--sync/syncable/model_type.cc51
-rw-r--r--sync/syncable/nigori_util.cc4
-rw-r--r--sync/tools/testserver/chromiumsync.py7
-rw-r--r--sync/util/data_type_histogram.h3
-rw-r--r--tools/metrics/histograms/histograms.xml56
29 files changed, 271 insertions, 65 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index e1c960a..2be1cb7 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12376,6 +12376,9 @@ Some features may be unavailable. Please check that the profile exists and you
<message name="IDS_SYNC_DATATYPE_TABS" desc="Open Tabs, one of the data types that we allow syncing.">
Open Tabs
</message>
+ <message name="IDS_SYNC_DATATYPE_WIFI_CREDENTIALS" desc="WiFi network names and passwords, one of the data types that we allow syncing.">
+ WiFi Credentials
+ </message>
<!-- Encryption tab of the configure sync dialog -->
<message name="IDS_SYNC_ENCRYPTION_INSTRUCTIONS" desc="Instructions for the encryption settings tab.">
diff --git a/chrome/browser/resources/sync_setup_overlay.html b/chrome/browser/resources/sync_setup_overlay.html
index 9e76025..06e330f 100644
--- a/chrome/browser/resources/sync_setup_overlay.html
+++ b/chrome/browser/resources/sync_setup_overlay.html
@@ -102,6 +102,13 @@
</span>
</label>
</div>
+ <div id="wifi-credentials-item" class="sync-type-checkbox checkbox">
+ <label>
+ <input id="wifi-credentials-checkbox" type="checkbox">
+ <span i18n-content="wifiCredentials" il8n-values="title:wifiCredentials">
+ </span>
+ </label>
+ </div>
</div>
</div>
<div id="customize-sync-encryption-new">
diff --git a/chrome/browser/resources/sync_setup_overlay.js b/chrome/browser/resources/sync_setup_overlay.js
index 976d7a3..ca326f2 100644
--- a/chrome/browser/resources/sync_setup_overlay.js
+++ b/chrome/browser/resources/sync_setup_overlay.js
@@ -306,7 +306,7 @@ cr.define('options', function() {
$('use-default-link').onclick = null;
// These values need to be kept in sync with where they are read in
- // SyncSetupFlow::GetDataTypeChoiceData().
+ // sync_setup_handler.cc:GetConfiguration().
var syncAll = $('sync-select-datatypes').selectedIndex ==
options.DataTypeSelection.SYNC_EVERYTHING;
var syncNothing = $('sync-select-datatypes').selectedIndex ==
@@ -323,6 +323,8 @@ cr.define('options', function() {
'typedUrlsSynced': syncAll || $('typed-urls-checkbox').checked,
'appsSynced': syncAll || $('apps-checkbox').checked,
'tabsSynced': syncAll || $('tabs-checkbox').checked,
+ 'wifiCredentialsSynced': syncAll ||
+ $('wifi-credentials-checkbox').checked,
'encryptAllData': encryptAllData,
'usePassphrase': usePassphrase,
'isGooglePassphrase': googlePassphrase,
@@ -439,6 +441,17 @@ cr.define('options', function() {
} else {
$('tabs-item').hidden = true;
}
+ if (args.wifiCredentialsRegistered) {
+ $('wifi-credentials-checkbox').checked = args.wifiCredentialsSynced;
+ dataTypeBoxesChecked_['wifi-credentials-checkbox'] =
+ args.wifiCredentialsSynced;
+ dataTypeBoxesDisabled_['wifi-credentials-checkbox'] =
+ args.wifiCredentialsEnforced;
+ $('wifi-credentials-checkbox').onclick = this.handleDataTypeClick_;
+ $('wifi-credentials-item').hidden = false;
+ } else {
+ $('wifi-credentials-item').hidden = true;
+ }
this.setDataTypeCheckboxes_(datatypeSelect.selectedIndex);
},
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 2effb5d..9fb4b92 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -1738,10 +1738,11 @@ void ProfileSyncService::UpdateSelectedTypesHistogram(
sync_driver::user_selectable_type::TYPED_URLS,
sync_driver::user_selectable_type::EXTENSIONS,
sync_driver::user_selectable_type::APPS,
- sync_driver::user_selectable_type::PROXY_TABS
+ sync_driver::user_selectable_type::WIFI_CREDENTIAL,
+ sync_driver::user_selectable_type::PROXY_TABS,
};
- COMPILE_ASSERT(32 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram);
+ COMPILE_ASSERT(33 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram);
if (!sync_everything) {
const syncer::ModelTypeSet current_types = GetPreferredDataTypes();
diff --git a/chrome/browser/ui/webui/sync_setup_handler.cc b/chrome/browser/ui/webui/sync_setup_handler.cc
index 090372a..0b25173 100644
--- a/chrome/browser/ui/webui/sync_setup_handler.cc
+++ b/chrome/browser/ui/webui/sync_setup_handler.cc
@@ -91,37 +91,6 @@ SyncConfigInfo::SyncConfigInfo()
SyncConfigInfo::~SyncConfigInfo() {}
-// Note: The order of these types must match the ordering of
-// the respective types in ModelType
-const char* kDataTypeNames[] = {
- "bookmarks",
- "preferences",
- "passwords",
- "autofill",
- "themes",
- "typedUrls",
- "extensions",
- "apps",
- "tabs"
-};
-
-COMPILE_ASSERT(32 == syncer::MODEL_TYPE_COUNT,
- update_kDataTypeNames_to_match_UserSelectableTypes);
-
-typedef std::map<syncer::ModelType, const char*> ModelTypeNameMap;
-
-ModelTypeNameMap GetSelectableTypeNameMap() {
- ModelTypeNameMap type_names;
- syncer::ModelTypeSet type_set = syncer::UserSelectableTypes();
- syncer::ModelTypeSet::Iterator it = type_set.First();
- DCHECK_EQ(arraysize(kDataTypeNames), type_set.Size());
- for (size_t i = 0; i < arraysize(kDataTypeNames) && it.Good();
- ++i, it.Inc()) {
- type_names[it.Get()] = kDataTypeNames[i];
- }
- return type_names;
-}
-
bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
scoped_ptr<base::Value> parsed_value(base::JSONReader::Read(json));
base::DictionaryValue* result;
@@ -143,9 +112,9 @@ bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
DCHECK(!(config->sync_everything && config->sync_nothing))
<< "syncAllDataTypes and syncNothing cannot both be true";
- ModelTypeNameMap type_names = GetSelectableTypeNameMap();
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
- for (ModelTypeNameMap::const_iterator it = type_names.begin();
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
it != type_names.end(); ++it) {
std::string key_name = it->second + std::string("Synced");
bool sync_value;
@@ -271,6 +240,7 @@ void SyncSetupHandler::GetStaticLocalizedValues(
{ "extensions", IDS_SYNC_DATATYPE_EXTENSIONS },
{ "typedURLs", IDS_SYNC_DATATYPE_TYPED_URLS },
{ "apps", IDS_SYNC_DATATYPE_APPS },
+ { "wifiCredentials", IDS_SYNC_DATATYPE_WIFI_CREDENTIALS },
{ "openTabs", IDS_SYNC_DATATYPE_TABS },
{ "serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR },
{ "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL },
@@ -357,8 +327,8 @@ void SyncSetupHandler::DisplayConfigureSync(bool show_advanced,
service->GetRegisteredDataTypes();
const syncer::ModelTypeSet preferred_types = service->GetPreferredDataTypes();
const syncer::ModelTypeSet enforced_types = service->GetForcedDataTypes();
- ModelTypeNameMap type_names = GetSelectableTypeNameMap();
- for (ModelTypeNameMap::const_iterator it = type_names.begin();
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
it != type_names.end(); ++it) {
syncer::ModelType sync_type = it->first;
const std::string key_name = it->second;
diff --git a/chrome/browser/ui/webui/sync_setup_handler_unittest.cc b/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
index 432de3e..69ec5e7 100644
--- a/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
+++ b/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
@@ -91,6 +91,8 @@ std::string GetConfiguration(const base::DictionaryValue* extra_values,
result.SetBoolean("tabsSynced", types.Has(syncer::PROXY_TABS));
result.SetBoolean("themesSynced", types.Has(syncer::THEMES));
result.SetBoolean("typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+ result.SetBoolean("wifiCredentialsSynced",
+ types.Has(syncer::WIFI_CREDENTIALS));
std::string args;
base::JSONWriter::Write(&result, &args);
return args;
@@ -110,7 +112,7 @@ void CheckBool(const base::DictionaryValue* dictionary,
bool actual_value;
EXPECT_TRUE(dictionary->GetBoolean(key, &actual_value)) <<
"No value found for " << key;
- EXPECT_EQ(actual_value, expected_value) <<
+ EXPECT_EQ(expected_value, actual_value) <<
"Mismatch found for " << key;
}
}
@@ -138,6 +140,8 @@ void CheckConfigDataTypeArguments(base::DictionaryValue* dictionary,
CheckBool(dictionary, "tabsSynced", types.Has(syncer::PROXY_TABS));
CheckBool(dictionary, "themesSynced", types.Has(syncer::THEMES));
CheckBool(dictionary, "typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+ CheckBool(dictionary, "wifiCredentialsSynced",
+ types.Has(syncer::WIFI_CREDENTIALS));
}
@@ -923,6 +927,7 @@ TEST_F(SyncSetupHandlerTest, ShowSetupSyncEverything) {
CheckBool(dictionary, "extensionsRegistered", true);
CheckBool(dictionary, "passwordsRegistered", true);
CheckBool(dictionary, "preferencesRegistered", true);
+ CheckBool(dictionary, "wifiCredentialsRegistered", true);
CheckBool(dictionary, "tabsRegistered", true);
CheckBool(dictionary, "themesRegistered", true);
CheckBool(dictionary, "typedUrlsRegistered", true);
diff --git a/components/sync_driver/model_association_manager.cc b/components/sync_driver/model_association_manager.cc
index 6706c4c..426e6e3 100644
--- a/components/sync_driver/model_association_manager.cc
+++ b/components/sync_driver/model_association_manager.cc
@@ -57,6 +57,7 @@ static const syncer::ModelType kStartOrder[] = {
syncer::SUPERVISED_USER_SETTINGS,
syncer::SUPERVISED_USER_SHARED_SETTINGS,
syncer::ARTICLES,
+ syncer::WIFI_CREDENTIALS,
};
COMPILE_ASSERT(arraysize(kStartOrder) ==
diff --git a/components/sync_driver/pref_names.cc b/components/sync_driver/pref_names.cc
index 5ecf5c8..49f0fcd 100644
--- a/components/sync_driver/pref_names.cc
+++ b/components/sync_driver/pref_names.cc
@@ -60,6 +60,7 @@ const char kSyncSyncedNotifications[] = "sync.synced_notifications";
const char kSyncTabs[] = "sync.tabs";
const char kSyncThemes[] = "sync.themes";
const char kSyncTypedUrls[] = "sync.typed_urls";
+const char kSyncWifiCredentials[] = "sync.wifi_credentials";
// Boolean used by enterprise configuration management in order to lock down
// sync.
diff --git a/components/sync_driver/pref_names.h b/components/sync_driver/pref_names.h
index 54710fb..4cc7f81 100644
--- a/components/sync_driver/pref_names.h
+++ b/components/sync_driver/pref_names.h
@@ -46,6 +46,7 @@ extern const char kSyncSyncedNotifications[];
extern const char kSyncTabs[];
extern const char kSyncThemes[];
extern const char kSyncTypedUrls[];
+extern const char kSyncWifiCredentials[];
extern const char kSyncManaged[];
extern const char kSyncSuppressStart[];
diff --git a/components/sync_driver/sync_prefs.cc b/components/sync_driver/sync_prefs.cc
index cc7914d..5773f0f 100644
--- a/components/sync_driver/sync_prefs.cc
+++ b/components/sync_driver/sync_prefs.cc
@@ -137,6 +137,7 @@ void SyncPrefs::RegisterProfilePrefs(
model_set.Put(syncer::TYPED_URLS);
model_set.Put(syncer::SESSIONS);
model_set.Put(syncer::ARTICLES);
+ model_set.Put(syncer::WIFI_CREDENTIALS);
registry->RegisterListPref(prefs::kSyncAcknowledgedSyncTypes,
syncer::ModelTypeSetToValue(model_set),
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
@@ -343,6 +344,8 @@ const char* SyncPrefs::GetPrefNameForDataType(syncer::ModelType data_type) {
return prefs::kSyncSupervisedUserSharedSettings;
case syncer::DEVICE_INFO:
return prefs::kSyncDeviceInfo;
+ case syncer::WIFI_CREDENTIALS:
+ return prefs::kSyncWifiCredentials;
default:
break;
}
diff --git a/components/sync_driver/user_selectable_sync_type.h b/components/sync_driver/user_selectable_sync_type.h
index 59c1b18..42fd6e4 100644
--- a/components/sync_driver/user_selectable_sync_type.h
+++ b/components/sync_driver/user_selectable_sync_type.h
@@ -34,6 +34,7 @@ enum UserSelectableSyncType {
// TODO(petewil): There was talk of removing this from user selectable sync
// types. Should we?
SYNCED_NOTIFICATIONS = 9,
+ WIFI_CREDENTIAL = 10,
// The datatypes below are implicitly synced, and are not exposed via user
// selectable checkboxes.
@@ -57,7 +58,7 @@ enum UserSelectableSyncType {
// SYNCED_NOTIFICATION_APP_INFO
// Number of sync datatypes exposed to the user via checboxes in the UI.
- SELECTABLE_DATATYPE_COUNT = 10,
+ SELECTABLE_DATATYPE_COUNT = 11,
};
} // namespace user_selectable_type
diff --git a/sync/internal_api/public/base/model_type.h b/sync/internal_api/public/base/model_type.h
index 61bcbd0..716bece 100644
--- a/sync/internal_api/public/base/model_type.h
+++ b/sync/internal_api/public/base/model_type.h
@@ -9,6 +9,7 @@
#ifndef SYNC_INTERNAL_API_PUBLIC_BASE_MODEL_TYPE_H_
#define SYNC_INTERNAL_API_PUBLIC_BASE_MODEL_TYPE_H_
+#include <map>
#include <set>
#include <string>
@@ -110,6 +111,9 @@ enum ModelType {
ARTICLES,
// App List items
APP_LIST,
+ // WiFi credentials. Each item contains the information for connecting to one
+ // WiFi network. This includes, e.g., network name and password.
+ WIFI_CREDENTIALS,
// ---- Proxy types ----
// Proxy types are excluded from the sync protocol, but are still considered
@@ -145,6 +149,7 @@ typedef EnumSet<ModelType, FIRST_REAL_MODEL_TYPE, LAST_REAL_MODEL_TYPE>
ModelTypeSet;
typedef EnumSet<ModelType, UNSPECIFIED, LAST_REAL_MODEL_TYPE>
FullModelTypeSet;
+typedef std::map<syncer::ModelType, const char*> ModelTypeNameMap;
inline ModelType ModelTypeFromInt(int i) {
DCHECK_GE(i, 0);
@@ -182,6 +187,7 @@ SYNC_EXPORT ModelTypeSet UserTypes();
// These are the user-selectable data types.
SYNC_EXPORT ModelTypeSet UserSelectableTypes();
SYNC_EXPORT bool IsUserSelectableType(ModelType model_type);
+SYNC_EXPORT ModelTypeNameMap GetUserSelectableTypeNameMap();
// This is the subset of UserTypes() that can be encrypted.
SYNC_EXPORT_PRIVATE ModelTypeSet EncryptableUserTypes();
diff --git a/sync/internal_api/public/sync_encryption_handler.cc b/sync/internal_api/public/sync_encryption_handler.cc
index e967600..f8eeeef 100644
--- a/sync/internal_api/public/sync_encryption_handler.cc
+++ b/sync/internal_api/public/sync_encryption_handler.cc
@@ -14,9 +14,9 @@ SyncEncryptionHandler::~SyncEncryptionHandler() {}
// Static.
ModelTypeSet SyncEncryptionHandler::SensitiveTypes() {
- // It has its own encryption scheme, but we include it anyway.
ModelTypeSet types;
- types.Put(PASSWORDS);
+ types.Put(PASSWORDS); // Has its own encryption, but include it anyway.
+ types.Put(WIFI_CREDENTIALS);
return types;
}
diff --git a/sync/internal_api/sync_encryption_handler_impl_unittest.cc b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
index 5aad1b0..d878fda 100644
--- a/sync/internal_api/sync_encryption_handler_impl_unittest.cc
+++ b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
@@ -423,7 +423,8 @@ TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingExplicit) {
EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
- EXPECT_TRUE(encrypted_types.Equals(ModelTypeSet(PASSWORDS)));
+ EXPECT_TRUE(encrypted_types.Equals(
+ ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS)));
{
WriteTransaction trans(FROM_HERE, user_share());
@@ -459,7 +460,8 @@ TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingImplicit) {
EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
- EXPECT_TRUE(encrypted_types.Equals(ModelTypeSet(PASSWORDS)));
+ EXPECT_TRUE(encrypted_types.Equals(
+ ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS)));
{
WriteTransaction trans(FROM_HERE, user_share());
@@ -503,7 +505,8 @@ TEST_F(SyncEncryptionHandlerImplTest, UnknownSensitiveTypes) {
EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
- EXPECT_TRUE(encrypted_types.Equals(ModelTypeSet(PASSWORDS)));
+ EXPECT_TRUE(encrypted_types.Equals(
+ ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS)));
{
WriteTransaction trans(FROM_HERE, user_share());
@@ -514,7 +517,8 @@ TEST_F(SyncEncryptionHandlerImplTest, UnknownSensitiveTypes) {
EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe();
- EXPECT_TRUE(encrypted_types.Equals(ModelTypeSet(BOOKMARKS, PASSWORDS)));
+ EXPECT_TRUE(encrypted_types.Equals(
+ ModelTypeSet(BOOKMARKS, PASSWORDS, WIFI_CREDENTIALS)));
}
// Receive an old nigori with old encryption keys and encrypted types. We should
diff --git a/sync/protocol/BUILD.gn b/sync/protocol/BUILD.gn
index d44846a..5bdca9d 100644
--- a/sync/protocol/BUILD.gn
+++ b/sync/protocol/BUILD.gn
@@ -46,6 +46,7 @@ proto_library("protocol") {
"theme_specifics.proto",
"typed_url_specifics.proto",
"unique_position.proto",
+ "wifi_credential_specifics.proto",
]
cc_generator_options = "dllexport_decl=SYNC_PROTO_EXPORT:"
diff --git a/sync/protocol/proto_enum_conversions.cc b/sync/protocol/proto_enum_conversions.cc
index 4c43983..eff952f 100644
--- a/sync/protocol/proto_enum_conversions.cc
+++ b/sync/protocol/proto_enum_conversions.cc
@@ -80,6 +80,19 @@ const char* GetPageTransitionRedirectTypeString(
return "";
}
+const char* GetWifiCredentialSecurityClassString(
+ sync_pb::WifiCredentialSpecifics::SecurityClass security_class) {
+ ASSERT_ENUM_BOUNDS(sync_pb::WifiCredentialSpecifics, SecurityClass,
+ SECURITY_CLASS_INVALID, SECURITY_CLASS_PSK);
+ switch (security_class) {
+ ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_INVALID);
+ ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_NONE);
+ ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_WEP);
+ ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_PSK);
+ }
+ NOTREACHED();
+ return "";
+}
const char* GetUpdatesSourceString(
sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source) {
ASSERT_ENUM_BOUNDS(sync_pb::GetUpdatesCallerInfo, GetUpdatesSource,
diff --git a/sync/protocol/proto_enum_conversions.h b/sync/protocol/proto_enum_conversions.h
index 57a7b5c..6a6bbdc 100644
--- a/sync/protocol/proto_enum_conversions.h
+++ b/sync/protocol/proto_enum_conversions.h
@@ -35,6 +35,9 @@ SYNC_EXPORT_PRIVATE const char* GetPageTransitionRedirectTypeString(
sync_pb::SyncEnums::PageTransitionRedirectType
redirect_type);
+SYNC_EXPORT_PRIVATE const char* GetWifiCredentialSecurityClassString(
+ sync_pb::WifiCredentialSpecifics::SecurityClass security_class);
+
SYNC_EXPORT const char* GetUpdatesSourceString(
sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source);
diff --git a/sync/protocol/proto_enum_conversions_unittest.cc b/sync/protocol/proto_enum_conversions_unittest.cc
index d566436..db88a8f 100644
--- a/sync/protocol/proto_enum_conversions_unittest.cc
+++ b/sync/protocol/proto_enum_conversions_unittest.cc
@@ -53,6 +53,13 @@ TEST_F(ProtoEnumConversionsTest, GetPageTransitionQualifierString) {
sync_pb::SyncEnums::PageTransitionRedirectType_MAX);
}
+TEST_F(ProtoEnumConversionsTest, GetWifiCredentialSecurityClassString) {
+ TestEnumStringFunction(
+ GetWifiCredentialSecurityClassString,
+ sync_pb::WifiCredentialSpecifics::SecurityClass_MIN,
+ sync_pb::WifiCredentialSpecifics::SecurityClass_MAX);
+}
+
TEST_F(ProtoEnumConversionsTest, GetUpdatesSourceString) {
TestEnumStringFunction(
GetUpdatesSourceString,
diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc
index 56a2704..cc022c4 100644
--- a/sync/protocol/proto_value_conversions.cc
+++ b/sync/protocol/proto_value_conversions.cc
@@ -783,6 +783,15 @@ base::DictionaryValue* TypedUrlSpecificsToValue(
return value;
}
+base::DictionaryValue* WifiCredentialSpecificsToValue(
+ const sync_pb::WifiCredentialSpecifics& proto) {
+ base::DictionaryValue* value = new base::DictionaryValue();
+ SET_BYTES(ssid);
+ SET_ENUM(security_class, GetWifiCredentialSecurityClassString);
+ SET_BYTES(passphrase);
+ return value;
+}
+
base::DictionaryValue* EntitySpecificsToValue(
const sync_pb::EntitySpecifics& specifics) {
base::DictionaryValue* value = new base::DictionaryValue();
@@ -817,6 +826,7 @@ base::DictionaryValue* EntitySpecificsToValue(
SyncedNotificationAppInfoSpecificsToValue);
SET_FIELD(theme, ThemeSpecificsToValue);
SET_FIELD(typed_url, TypedUrlSpecificsToValue);
+ SET_FIELD(wifi_credential, WifiCredentialSpecificsToValue);
return value;
}
diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h
index 2a3c1ae..79e4ca2 100644
--- a/sync/protocol/proto_value_conversions.h
+++ b/sync/protocol/proto_value_conversions.h
@@ -78,6 +78,7 @@ class Target;
class ThemeSpecifics;
class TimeRangeDirective;
class TypedUrlSpecifics;
+class WifiCredentialSpecifics;
} // namespace sync_pb
// Utility functions to convert sync protocol buffers to dictionaries.
@@ -274,6 +275,9 @@ SYNC_EXPORT_PRIVATE base::DictionaryValue* ThemeSpecificsToValue(
SYNC_EXPORT_PRIVATE base::DictionaryValue* TypedUrlSpecificsToValue(
const sync_pb::TypedUrlSpecifics& typed_url_specifics);
+SYNC_EXPORT_PRIVATE base::DictionaryValue* WifiCredentialSpecificsToValue(
+ const sync_pb::WifiCredentialSpecifics& wifi_credential_specifics);
+
// Any present extensions are mapped to sub-dictionary values with the
// key equal to the extension name.
SYNC_EXPORT_PRIVATE base::DictionaryValue* EntitySpecificsToValue(
diff --git a/sync/protocol/proto_value_conversions_unittest.cc b/sync/protocol/proto_value_conversions_unittest.cc
index 664a6ab..76aab6a 100644
--- a/sync/protocol/proto_value_conversions_unittest.cc
+++ b/sync/protocol/proto_value_conversions_unittest.cc
@@ -35,6 +35,7 @@
#include "sync/protocol/sync.pb.h"
#include "sync/protocol/theme_specifics.pb.h"
#include "sync/protocol/typed_url_specifics.pb.h"
+#include "sync/protocol/wifi_credential_specifics.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
@@ -55,7 +56,7 @@ 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(32, MODEL_TYPE_COUNT);
+ EXPECT_EQ(33, 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
@@ -264,6 +265,10 @@ TEST_F(ProtoValueConversionsTest, ArticleSpecificsToValue) {
TestSpecificsToValue(ArticleSpecificsToValue);
}
+TEST_F(ProtoValueConversionsTest, WifiCredentialSpecificsToValue) {
+ TestSpecificsToValue(WifiCredentialSpecificsToValue);
+}
+
// TODO(akalin): Figure out how to better test EntitySpecificsToValue.
TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) {
@@ -301,6 +306,7 @@ TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) {
SET_FIELD(synced_notification_app_info);
SET_FIELD(theme);
SET_FIELD(typed_url);
+ SET_FIELD(wifi_credential);
#undef SET_FIELD
diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto
index 8f27850..53c99ec 100644
--- a/sync/protocol/sync.proto
+++ b/sync/protocol/sync.proto
@@ -49,6 +49,7 @@ import "synced_notification_specifics.proto";
import "theme_specifics.proto";
import "typed_url_specifics.proto";
import "unique_position.proto";
+import "wifi_credential_specifics.proto";
// Used for inspecting how long we spent performing operations in different
// backends. All times must be in millis.
@@ -129,6 +130,7 @@ message EntitySpecifics {
202026;
optional ArticleSpecifics article = 223759;
optional AppListSpecifics app_list = 229170;
+ optional WifiCredentialSpecifics wifi_credential = 218175;
}
message SyncEntity {
diff --git a/sync/protocol/wifi_credential_specifics.proto b/sync/protocol/wifi_credential_specifics.proto
new file mode 100644
index 0000000..29f9f07
--- /dev/null
+++ b/sync/protocol/wifi_credential_specifics.proto
@@ -0,0 +1,58 @@
+// Copyright 2014 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.
+//
+// Sync protocol datatype extension for WiFi credentials.
+
+// Update proto_{value,enum}_conversions{.h,.cc,_unittest.cc} if you change
+// any fields in this file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option retain_unknown_fields = true;
+
+package sync_pb;
+
+// Properties of WiFi credential objects.
+message WifiCredentialSpecifics {
+ optional bytes ssid = 1; // Not necessarily UTF-8. May contain NUL.
+
+ enum SecurityClass {
+ SECURITY_CLASS_INVALID = 0;
+ SECURITY_CLASS_NONE = 1;
+ SECURITY_CLASS_WEP = 2;
+ SECURITY_CLASS_PSK = 3; // WPA-PSK or RSN-PSK
+ // 802.1X is omittted, as we do not support syncing 802.1X
+ // credentials.
+ }
+ optional SecurityClass security_class = 2;
+
+ // Network passphrase.
+ //
+ // For SECURITY_CLASS_NONE, the passphrase should be ignored.
+ //
+ // For SECURITY_CLASS_WEP, the passphrase should have one of the
+ // following formats:
+ // - WEP-40:
+ // - 5 character ASCII string. Each character maps one byte of the key.
+ // - 10 character hex string. The string maps to the WEP key by simple
+ // hex decoding.
+ // - WEP-104:
+ // - 13 character ASCII string. Each character maps one byte of the key.
+ // - 26 character hex string. The string maps to the WEP key by simple
+ // hex decoding.
+ //
+ // For SECURITY_CLASS_PSK, the passphrase should have one of the
+ // following two formats:
+ // - An 8-63 character ASCII string. The string maps to the
+ // WPA/WPA-2 PSK as per IEEE 802.11i.
+ // - A 64 character hex string. The string maps to the PSK per
+ // simple hex decoding.
+ //
+ // Note that, although the passphrase "should" contain only ASCII
+ // characters, we represent |passphrase| as |bytes| rather than
+ // |string|. This is to accomodate networks that use non-ASCII
+ // passphrases.
+ optional bytes passphrase = 3;
+}
diff --git a/sync/sync.gyp b/sync/sync.gyp
index 73beca3..cfb658f 100644
--- a/sync/sync.gyp
+++ b/sync/sync.gyp
@@ -506,6 +506,7 @@
'protocol/theme_specifics.proto',
'protocol/typed_url_specifics.proto',
'protocol/unique_position.proto',
+ 'protocol/wifi_credential_specifics.proto',
],
'variables': {
'enable_wexit_time_destructors': 1,
diff --git a/sync/syncable/model_type.cc b/sync/syncable/model_type.cc
index 59be6f2..5b92c78 100644
--- a/sync/syncable/model_type.cc
+++ b/sync/syncable/model_type.cc
@@ -25,6 +25,28 @@
namespace syncer {
+// Notes:
+// 1) This list must contain exactly the same elements as the set returned by
+// UserSelectableTypes().
+// 2) This list must be in the same order as the respective values in the
+// ModelType enum.
+const char* kUserSelectableDataTypeNames[] = {
+ "bookmarks",
+ "preferences",
+ "passwords",
+ "autofill",
+ "themes",
+ "typedUrls",
+ "extensions",
+ "apps",
+ "wifiCredentials",
+ "tabs",
+};
+
+COMPILE_ASSERT(
+ 33 == MODEL_TYPE_COUNT,
+ update_kUserSelectableDataTypeNames_to_match_UserSelectableTypes);
+
void AddDefaultFieldValue(ModelType datatype,
sync_pb::EntitySpecifics* specifics) {
if (!ProtocolTypes().Has(datatype)) {
@@ -119,6 +141,9 @@ void AddDefaultFieldValue(ModelType datatype,
case ARTICLES:
specifics->mutable_article();
break;
+ case WIFI_CREDENTIALS:
+ specifics->mutable_wifi_credential();
+ break;
default:
NOTREACHED() << "No known extension for model type.";
}
@@ -196,6 +221,8 @@ int GetSpecificsFieldNumberFromModelType(ModelType model_type) {
return sync_pb::EntitySpecifics::kManagedUserSharedSettingFieldNumber;
case ARTICLES:
return sync_pb::EntitySpecifics::kArticleFieldNumber;
+ case WIFI_CREDENTIALS:
+ return sync_pb::EntitySpecifics::kWifiCredentialFieldNumber;
default:
NOTREACHED() << "No known extension for model type.";
return 0;
@@ -324,6 +351,9 @@ ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
if (specifics.has_article())
return ARTICLES;
+ if (specifics.has_wifi_credential())
+ return WIFI_CREDENTIALS;
+
return UNSPECIFIED;
}
@@ -356,6 +386,7 @@ ModelTypeSet UserSelectableTypes() {
set.Put(TYPED_URLS);
set.Put(EXTENSIONS);
set.Put(APPS);
+ set.Put(WIFI_CREDENTIALS);
set.Put(PROXY_TABS);
return set;
}
@@ -364,6 +395,18 @@ bool IsUserSelectableType(ModelType model_type) {
return UserSelectableTypes().Has(model_type);
}
+ModelTypeNameMap GetUserSelectableTypeNameMap() {
+ ModelTypeNameMap type_names;
+ ModelTypeSet type_set = UserSelectableTypes();
+ ModelTypeSet::Iterator it = type_set.First();
+ DCHECK_EQ(arraysize(kUserSelectableDataTypeNames), type_set.Size());
+ for (size_t i = 0; i < arraysize(kUserSelectableDataTypeNames) && it.Good();
+ ++i, it.Inc()) {
+ type_names[it.Get()] = kUserSelectableDataTypeNames[i];
+ }
+ return type_names;
+}
+
ModelTypeSet EncryptableUserTypes() {
ModelTypeSet encryptable_user_types = UserTypes();
// We never encrypt history delete directives.
@@ -524,6 +567,8 @@ const char* ModelTypeToString(ModelType model_type) {
return "Managed User Shared Settings";
case ARTICLES:
return "Articles";
+ case WIFI_CREDENTIALS:
+ return "WiFi Credentials";
case PROXY_TABS:
return "Tabs";
default:
@@ -603,6 +648,8 @@ int ModelTypeToHistogramInt(ModelType model_type) {
return 30;
case SYNCED_NOTIFICATION_APP_INFO:
return 31;
+ case WIFI_CREDENTIALS:
+ return 32;
// Silence a compiler warning.
case MODEL_TYPE_COUNT:
return 0;
@@ -696,6 +743,8 @@ ModelType ModelTypeFromString(const std::string& model_type_string) {
return SUPERVISED_USER_SHARED_SETTINGS;
else if (model_type_string == "Articles")
return ARTICLES;
+ else if (model_type_string == "WiFi Credentials")
+ return WIFI_CREDENTIALS;
else if (model_type_string == "Tabs")
return PROXY_TABS;
else
@@ -820,6 +869,8 @@ std::string ModelTypeToRootTag(ModelType type) {
return "google_chrome_managed_user_shared_settings";
case ARTICLES:
return "google_chrome_articles";
+ case WIFI_CREDENTIALS:
+ return "google_chrome_wifi_credentials";
case PROXY_TABS:
return std::string();
default:
diff --git a/sync/syncable/nigori_util.cc b/sync/syncable/nigori_util.cc
index 63dbcac..eaf9eca 100644
--- a/sync/syncable/nigori_util.cc
+++ b/sync/syncable/nigori_util.cc
@@ -243,7 +243,7 @@ void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types,
bool encrypt_everything,
sync_pb::NigoriSpecifics* nigori) {
nigori->set_encrypt_everything(encrypt_everything);
- COMPILE_ASSERT(32 == MODEL_TYPE_COUNT, UpdateEncryptedTypes);
+ COMPILE_ASSERT(33 == MODEL_TYPE_COUNT, UpdateEncryptedTypes);
nigori->set_encrypt_bookmarks(
encrypted_types.Has(BOOKMARKS));
nigori->set_encrypt_preferences(
@@ -279,7 +279,7 @@ ModelTypeSet GetEncryptedTypesFromNigori(
return ModelTypeSet::All();
ModelTypeSet encrypted_types;
- COMPILE_ASSERT(32 == MODEL_TYPE_COUNT, UpdateEncryptedTypes);
+ COMPILE_ASSERT(33 == MODEL_TYPE_COUNT, UpdateEncryptedTypes);
if (nigori.encrypt_bookmarks())
encrypted_types.Put(BOOKMARKS);
if (nigori.encrypt_preferences())
diff --git a/sync/tools/testserver/chromiumsync.py b/sync/tools/testserver/chromiumsync.py
index 2930059..5487ef2 100644
--- a/sync/tools/testserver/chromiumsync.py
+++ b/sync/tools/testserver/chromiumsync.py
@@ -55,6 +55,7 @@ import synced_notification_render_pb2
import synced_notification_specifics_pb2
import theme_specifics_pb2
import typed_url_specifics_pb2
+import wifi_credential_specifics_pb2
# An enumeration of the various kinds of data that can be synced.
# Over the wire, this enumeration is not used: a sync object's type is
@@ -90,7 +91,8 @@ ALL_TYPES = (
TYPED_URL,
EXTENSION_SETTINGS,
FAVICON_IMAGES,
- FAVICON_TRACKING) = range(30)
+ FAVICON_TRACKING,
+ WIFI_CREDENTIAL) = range(31)
# An enumeration on the frequency at which the server should send errors
# to the client. This would be specified by the url that triggers the error.
@@ -138,6 +140,7 @@ SYNC_TYPE_TO_DESCRIPTOR = {
SYNC_TYPE_FIELDS["synced_notification_app_info"],
THEME: SYNC_TYPE_FIELDS['theme'],
TYPED_URL: SYNC_TYPE_FIELDS['typed_url'],
+ WIFI_CREDENTIAL: SYNC_TYPE_FIELDS["wifi_credential"],
}
# The parent ID used to indicate a top-level node.
@@ -560,6 +563,8 @@ class SyncDataModel(object):
parent_tag=ROOT_ID, sync_type=THEME),
PermanentItem('google_chrome_typed_urls', name='Typed URLs',
parent_tag=ROOT_ID, sync_type=TYPED_URL),
+ PermanentItem('google_chrome_wifi_credentials', name='WiFi Credentials',
+ parent_tag=ROOT_ID, sync_type=WIFI_CREDENTIAL),
PermanentItem('google_chrome_dictionary', name='Dictionary',
parent_tag=ROOT_ID, sync_type=DICTIONARY),
PermanentItem('google_chrome_articles', name='Articles',
diff --git a/sync/util/data_type_histogram.h b/sync/util/data_type_histogram.h
index b5e0eda..1baaba3 100644
--- a/sync/util/data_type_histogram.h
+++ b/sync/util/data_type_histogram.h
@@ -123,6 +123,9 @@
case ::syncer::ARTICLES: \
PER_DATA_TYPE_MACRO("Article"); \
break; \
+ case ::syncer::WIFI_CREDENTIALS: \
+ PER_DATA_TYPE_MACRO("WifiCredentials"); \
+ break; \
case ::syncer::PROXY_TABS: \
PER_DATA_TYPE_MACRO("Tabs"); \
break; \
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8565b25..69bccd1 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -34957,6 +34957,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for apps. Used as estimate of datatype commit frequency.
+ Logged when a sync cycle is performed for apps.
</summary>
</histogram>
@@ -34964,7 +34965,8 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for autofill entries. Used as estimate of datatype
- commit frequency.
+ commit frequency. Logged when a sync cycle is performed for autofill
+ entries.
</summary>
</histogram>
@@ -34972,7 +34974,8 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for autofill profiles. Used as estimate of datatype
- commit frequency.
+ commit frequency. Logged when a sync cycle is performed for autofill
+ profiles.
</summary>
</histogram>
@@ -34980,7 +34983,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for bookmarks. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for boomarks.
</summary>
</histogram>
@@ -34988,7 +34991,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for dictionary. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for dictionary.
</summary>
</histogram>
@@ -34996,7 +34999,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for extensions. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for extensions.
</summary>
</histogram>
@@ -35004,7 +35007,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for favicon images. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for favicon images.
</summary>
</histogram>
@@ -35012,7 +35015,8 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for favicon tracking. Used as estimate of datatype
- commit frequency.
+ commit frequency. Logged when a sync cycle is performed for favicon
+ tracking.
</summary>
</histogram>
@@ -35020,7 +35024,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for nigori. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for nigori.
</summary>
</histogram>
@@ -35028,7 +35032,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for passwords. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for passwords.
</summary>
</histogram>
@@ -35036,7 +35040,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for preferences. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for preferences.
</summary>
</histogram>
@@ -35044,7 +35048,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for search engines. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for search engines.
</summary>
</histogram>
@@ -35052,7 +35056,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for sessions. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for sessions.
</summary>
</histogram>
@@ -35060,7 +35064,8 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for synced notifications. Used as estimate of datatype
- commit frequency.
+ commit frequency. Logged when a sync cycle is performed for synced
+ notifications.
</summary>
</histogram>
@@ -35068,7 +35073,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for themes. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for themes.
</summary>
</histogram>
@@ -35076,7 +35081,16 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<owner>zea@chromium.org</owner>
<summary>
Time between nudges for typed urls. Used as estimate of datatype commit
- frequency.
+ frequency. Logged when a sync cycle is performed for typed urls.
+ </summary>
+</histogram>
+
+<histogram name="Sync.FreqWifiCredentials" units="milliseconds">
+ <owner>zea@chromium.org</owner>
+ <summary>
+ Time between nudges for WiFi credentials. Used as estimate of datatype
+ commit frequency. Logged when a sync cycle is performed for WiFi
+ credentials.
</summary>
</histogram>
@@ -35536,6 +35550,18 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<summary>Time taken during bookmark association.</summary>
</histogram>
+<histogram name="Sync.WifiCredentialsAssociationTime" units="milliseconds">
+ <owner>zea@chromium.org</owner>
+ <summary>Time taken during WiFi credentials association.</summary>
+</histogram>
+
+<histogram name="Sync.WifiCredentialsStartFailure" enum="SyncStartResult">
+ <owner>zea@chromium.org</owner>
+ <summary>
+ Enumeration of types of WiFi credentials association failures.
+ </summary>
+</histogram>
+
<histogram name="SyncedNotifications.Actions"
enum="SyncedNotificationActionType">
<owner>petewil@chromium.org</owner>