Commit f4f799ec authored by Michael Hansen's avatar Michael Hansen Committed by Commit Bot

[Nearby] Validate device name.

This adds support for validating the device name that a user enters.
Validation happens as the user types and also when they try to save the
name. If an invalid name is detected, an error message will appear and
the "Done" button will be disabled.

Screenshots:
  https://screenshot.googleplex.com/3gnC6aQVyj4YmkX.png
  https://screenshot.googleplex.com/5arMAbok2MgfioU.png
  https://screenshot.googleplex.com/BEbCJ9LteRAYFpv.png

Bug: b:161297140
Change-Id: I3290eb92222cd796a1dede3ce603da5cc44940bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2424675
Commit-Queue: Michael Hansen <hansenmichael@google.com>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarAlex Gough <ajgo@chromium.org>
Reviewed-by: default avatarJames Vecore <vecore@google.com>
Cr-Commit-Position: refs/heads/master@{#811482}
parent 003f6f3f
...@@ -53,6 +53,17 @@ ...@@ -53,6 +53,17 @@
Your device visibility controls who can share with you while your screen is unlocked Your device visibility controls who can share with you while your screen is unlocked
</message> </message>
<!-- Device name validation -->
<message name="IDS_NEARBY_DEVICE_NAME_EMPTY_ERROR" desc="Error message when the user has left the device name empty to indicate that they are required to provide a name.">
Add a device name to continue
</message>
<message name="IDS_NEARBY_DEVICE_NAME_TOO_LONG_ERROR" desc="Error message when the user has entered a device name that is too long and must enter a shorter one.">
Device name too long
</message>
<message name="IDS_NEARBY_DEVICE_NAME_INVALID_CHARACTERS_ERROR" desc="Error message when the user has entered a device name that contains invalid characters.">
Device name contains invalid characters
</message>
<!-- Discovery page --> <!-- Discovery page -->
<message name="IDS_NEARBY_DISCOVERY_PAGE_INFO" desc="Help text on how to use the Nearby Share feature. Explains how to enable it on a nearby Chromebook to share with it."> <message name="IDS_NEARBY_DISCOVERY_PAGE_INFO" desc="Help text on how to use the Nearby Share feature. Explains how to enable it on a nearby Chromebook to share with it.">
Make sure both devices are unlocked, close together, and have Bluetooth turned on. If you’re sharing with a Chromebook, make sure it has Nearby Sharing turned on (open the status area by selecting the time, then select Nearby Share). Make sure both devices are unlocked, close together, and have Bluetooth turned on. If you’re sharing with a Chromebook, make sure it has Nearby Sharing turned on (open the status area by selecting the time, then select Nearby Share).
......
ff767fad53b3e885935a6414a000338ec704c2b2
\ No newline at end of file
7a6874c1390be0538bf041fe4cf71d4bf406cbc8
\ No newline at end of file
59f33a02cc8311d5d4ea79f7a4c7c3a675481fae
\ No newline at end of file
...@@ -16,6 +16,8 @@ source_set("local_device_data") { ...@@ -16,6 +16,8 @@ source_set("local_device_data") {
"nearby_share_local_device_data_manager_impl.h", "nearby_share_local_device_data_manager_impl.h",
] ]
public_deps = [ "//chrome/browser/ui/webui/nearby_share/public/mojom" ]
deps = [ deps = [
"//base", "//base",
"//chrome/browser/nearby_sharing/client", "//chrome/browser/nearby_sharing/client",
......
...@@ -77,16 +77,27 @@ base::Optional<std::string> FakeNearbyShareLocalDeviceDataManager::GetIconUrl() ...@@ -77,16 +77,27 @@ base::Optional<std::string> FakeNearbyShareLocalDeviceDataManager::GetIconUrl()
return icon_url_; return icon_url_;
} }
void FakeNearbyShareLocalDeviceDataManager::SetDeviceName( nearby_share::mojom::DeviceNameValidationResult
FakeNearbyShareLocalDeviceDataManager::ValidateDeviceName(
const std::string& name) { const std::string& name) {
if (device_name_ == name) return next_validation_result_;
return; }
nearby_share::mojom::DeviceNameValidationResult
FakeNearbyShareLocalDeviceDataManager::SetDeviceName(const std::string& name) {
if (next_validation_result_ !=
nearby_share::mojom::DeviceNameValidationResult::kValid)
return next_validation_result_;
if (device_name_ != name) {
device_name_ = name; device_name_ = name;
NotifyLocalDeviceDataChanged( NotifyLocalDeviceDataChanged(
/*did_device_name_change=*/true, /*did_device_name_change=*/true,
/*did_full_name_change=*/false, /*did_full_name_change=*/false,
/*did_icon_url_change=*/false); /*did_icon_url_change=*/false);
}
return nearby_share::mojom::DeviceNameValidationResult::kValid;
} }
void FakeNearbyShareLocalDeviceDataManager::DownloadDeviceData() { void FakeNearbyShareLocalDeviceDataManager::DownloadDeviceData() {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h" #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h" #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h" #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
class NearbyShareClientFactory; class NearbyShareClientFactory;
class PrefService; class PrefService;
...@@ -84,7 +85,10 @@ class FakeNearbyShareLocalDeviceDataManager ...@@ -84,7 +85,10 @@ class FakeNearbyShareLocalDeviceDataManager
std::string GetDeviceName() const override; std::string GetDeviceName() const override;
base::Optional<std::string> GetFullName() const override; base::Optional<std::string> GetFullName() const override;
base::Optional<std::string> GetIconUrl() const override; base::Optional<std::string> GetIconUrl() const override;
void SetDeviceName(const std::string& name) override; nearby_share::mojom::DeviceNameValidationResult ValidateDeviceName(
const std::string& name) override;
nearby_share::mojom::DeviceNameValidationResult SetDeviceName(
const std::string& name) override;
void DownloadDeviceData() override; void DownloadDeviceData() override;
void UploadContacts(std::vector<nearbyshare::proto::Contact> contacts, void UploadContacts(std::vector<nearbyshare::proto::Contact> contacts,
UploadCompleteCallback callback) override; UploadCompleteCallback callback) override;
...@@ -112,6 +116,11 @@ class FakeNearbyShareLocalDeviceDataManager ...@@ -112,6 +116,11 @@ class FakeNearbyShareLocalDeviceDataManager
return upload_certificates_calls_; return upload_certificates_calls_;
} }
void set_next_validation_result(
nearby_share::mojom::DeviceNameValidationResult result) {
next_validation_result_ = result;
}
private: private:
// NearbyShareLocalDeviceDataManager: // NearbyShareLocalDeviceDataManager:
void OnStart() override; void OnStart() override;
...@@ -124,6 +133,8 @@ class FakeNearbyShareLocalDeviceDataManager ...@@ -124,6 +133,8 @@ class FakeNearbyShareLocalDeviceDataManager
size_t num_download_device_data_calls_ = 0; size_t num_download_device_data_calls_ = 0;
std::vector<UploadContactsCall> upload_contacts_calls_; std::vector<UploadContactsCall> upload_contacts_calls_;
std::vector<UploadCertificatesCall> upload_certificates_calls_; std::vector<UploadCertificatesCall> upload_certificates_calls_;
nearby_share::mojom::DeviceNameValidationResult next_validation_result_ =
nearby_share::mojom::DeviceNameValidationResult::kValid;
}; };
#endif // CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_FAKE_NEARBY_SHARE_LOCAL_DEVICE_DATA_MANAGER_H_ #endif // CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_FAKE_NEARBY_SHARE_LOCAL_DEVICE_DATA_MANAGER_H_
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/observer_list_types.h" #include "base/observer_list_types.h"
#include "base/optional.h" #include "base/optional.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h" #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
// Manages local device data related to the UpdateDevice RPC such as the device // Manages local device data related to the UpdateDevice RPC such as the device
// ID, name, and icon url; provides the user's full name and icon URL returned // ID, name, and icon url; provides the user's full name and icon URL returned
...@@ -58,11 +59,18 @@ class NearbyShareLocalDeviceDataManager { ...@@ -58,11 +59,18 @@ class NearbyShareLocalDeviceDataManager {
// not yet been set from an UpdateDevice RPC response. // not yet been set from an UpdateDevice RPC response.
virtual base::Optional<std::string> GetIconUrl() const = 0; virtual base::Optional<std::string> GetIconUrl() const = 0;
// Sets and persists the device name in prefs. The device name is *not* // Validates the provided device name and returns an error if validation
// uploaded to the Nearby Share server; the UpdateDevice proto device_name // fails. This is just a check and the device name is not persisted.
// field in an artifact. Observers are notified via OnLocalDeviceDataChanged() virtual nearby_share::mojom::DeviceNameValidationResult ValidateDeviceName(
// if the device name changes. const std::string& name) = 0;
virtual void SetDeviceName(const std::string& name) = 0;
// Sets and persists the device name in prefs. The device name is first
// validated and if validation fails and error is returned and the device name
// is not persisted. The device name is *not* uploaded to the Nearby Share
// server; the UpdateDevice proto device_name field in an artifact. Observers
// are notified via OnLocalDeviceDataChanged() if the device name changes.
virtual nearby_share::mojom::DeviceNameValidationResult SetDeviceName(
const std::string& name) = 0;
// Makes an UpdateDevice RPC call to the Nearby Share server to retrieve all // Makes an UpdateDevice RPC call to the Nearby Share server to retrieve all
// available device data, which includes the full name and icon URL for now. // available device data, which includes the full name and icon URL for now.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h" #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h" #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater_impl.h" #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater_impl.h"
...@@ -37,6 +38,10 @@ constexpr base::TimeDelta kUpdateDeviceDataTimeout = ...@@ -37,6 +38,10 @@ constexpr base::TimeDelta kUpdateDeviceDataTimeout =
constexpr base::TimeDelta kDeviceDataDownloadPeriod = constexpr base::TimeDelta kDeviceDataDownloadPeriod =
base::TimeDelta::FromHours(1); base::TimeDelta::FromHours(1);
// The maximum length allowed for a device name, as encoded in UTF-8 in a
// std::string, which will not contain a null terminator.
size_t kDeviceNameMaxByteLength = 32;
} // namespace } // namespace
// static // static
...@@ -131,17 +136,37 @@ base::Optional<std::string> NearbyShareLocalDeviceDataManagerImpl::GetIconUrl() ...@@ -131,17 +136,37 @@ base::Optional<std::string> NearbyShareLocalDeviceDataManagerImpl::GetIconUrl()
return url; return url;
} }
void NearbyShareLocalDeviceDataManagerImpl::SetDeviceName( nearby_share::mojom::DeviceNameValidationResult
NearbyShareLocalDeviceDataManagerImpl::ValidateDeviceName(
const std::string& name) { const std::string& name) {
if (name.empty())
return nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty;
if (!base::IsStringUTF8(name))
return nearby_share::mojom::DeviceNameValidationResult::kErrorNotValidUtf8;
if (name.length() > kDeviceNameMaxByteLength)
return nearby_share::mojom::DeviceNameValidationResult::kErrorTooLong;
return nearby_share::mojom::DeviceNameValidationResult::kValid;
}
nearby_share::mojom::DeviceNameValidationResult
NearbyShareLocalDeviceDataManagerImpl::SetDeviceName(const std::string& name) {
if (name == GetDeviceName()) if (name == GetDeviceName())
return; return nearby_share::mojom::DeviceNameValidationResult::kValid;
auto error = ValidateDeviceName(name);
if (error != nearby_share::mojom::DeviceNameValidationResult::kValid)
return error;
// TODO(b/161297140): Perform input validation.
pref_service_->SetString(prefs::kNearbySharingDeviceNamePrefName, name); pref_service_->SetString(prefs::kNearbySharingDeviceNamePrefName, name);
NotifyLocalDeviceDataChanged(/*did_device_name_change=*/true, NotifyLocalDeviceDataChanged(/*did_device_name_change=*/true,
/*did_full_name_change=*/false, /*did_full_name_change=*/false,
/*did_icon_url_change=*/false); /*did_icon_url_change=*/false);
return nearby_share::mojom::DeviceNameValidationResult::kValid;
} }
void NearbyShareLocalDeviceDataManagerImpl::DownloadDeviceData() { void NearbyShareLocalDeviceDataManagerImpl::DownloadDeviceData() {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h" #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h" #include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h" #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
class NearbyShareClientFactory; class NearbyShareClientFactory;
class NearbyShareDeviceDataUpdater; class NearbyShareDeviceDataUpdater;
...@@ -60,7 +61,10 @@ class NearbyShareLocalDeviceDataManagerImpl ...@@ -60,7 +61,10 @@ class NearbyShareLocalDeviceDataManagerImpl
std::string GetDeviceName() const override; std::string GetDeviceName() const override;
base::Optional<std::string> GetFullName() const override; base::Optional<std::string> GetFullName() const override;
base::Optional<std::string> GetIconUrl() const override; base::Optional<std::string> GetIconUrl() const override;
void SetDeviceName(const std::string& name) override; nearby_share::mojom::DeviceNameValidationResult ValidateDeviceName(
const std::string& name) override;
nearby_share::mojom::DeviceNameValidationResult SetDeviceName(
const std::string& name) override;
void DownloadDeviceData() override; void DownloadDeviceData() override;
void UploadContacts(std::vector<nearbyshare::proto::Contact> contacts, void UploadContacts(std::vector<nearbyshare::proto::Contact> contacts,
UploadCompleteCallback callback) override; UploadCompleteCallback callback) override;
......
...@@ -25,6 +25,9 @@ namespace { ...@@ -25,6 +25,9 @@ namespace {
const char kFakeDefaultDeviceName[] = "Barack's Chromebook"; const char kFakeDefaultDeviceName[] = "Barack's Chromebook";
const char kFakeDeviceName[] = "My Cool Chromebook"; const char kFakeDeviceName[] = "My Cool Chromebook";
const char kFakeEmptyDeviceName[] = "";
const char kFakeTooLongDeviceName[] = "this string is 33 bytes in UTF-8!";
const char kFakeInvalidDeviceName[] = {0xC0};
const char kFakeFullName[] = "Barack Obama"; const char kFakeFullName[] = "Barack Obama";
const char kFakeIconUrl[] = "https://www.google.com"; const char kFakeIconUrl[] = "https://www.google.com";
...@@ -256,6 +259,19 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, DeviceId) { ...@@ -256,6 +259,19 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, DeviceId) {
EXPECT_EQ(id, manager()->GetId()); EXPECT_EQ(id, manager()->GetId());
} }
TEST_F(NearbyShareLocalDeviceDataManagerImplTest, ValidateDeviceName) {
CreateManager();
EXPECT_EQ(manager()->ValidateDeviceName(kFakeDeviceName),
nearby_share::mojom::DeviceNameValidationResult::kValid);
EXPECT_EQ(manager()->ValidateDeviceName(kFakeEmptyDeviceName),
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
EXPECT_EQ(manager()->ValidateDeviceName(kFakeTooLongDeviceName),
nearby_share::mojom::DeviceNameValidationResult::kErrorTooLong);
EXPECT_EQ(
manager()->ValidateDeviceName(kFakeInvalidDeviceName),
nearby_share::mojom::DeviceNameValidationResult::kErrorNotValidUtf8);
}
TEST_F(NearbyShareLocalDeviceDataManagerImplTest, SetDeviceName) { TEST_F(NearbyShareLocalDeviceDataManagerImplTest, SetDeviceName) {
CreateManager(); CreateManager();
...@@ -265,7 +281,27 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, SetDeviceName) { ...@@ -265,7 +281,27 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, SetDeviceName) {
EXPECT_EQ(kFakeDefaultDeviceName, manager()->GetDeviceName()); EXPECT_EQ(kFakeDefaultDeviceName, manager()->GetDeviceName());
EXPECT_TRUE(notifications().empty()); EXPECT_TRUE(notifications().empty());
manager()->SetDeviceName(kFakeDeviceName); auto error = manager()->SetDeviceName(kFakeEmptyDeviceName);
EXPECT_EQ(error,
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
EXPECT_EQ(kFakeDefaultDeviceName, manager()->GetDeviceName());
EXPECT_TRUE(notifications().empty());
error = manager()->SetDeviceName(kFakeTooLongDeviceName);
EXPECT_EQ(error,
nearby_share::mojom::DeviceNameValidationResult::kErrorTooLong);
EXPECT_EQ(kFakeDefaultDeviceName, manager()->GetDeviceName());
EXPECT_TRUE(notifications().empty());
error = manager()->SetDeviceName(kFakeInvalidDeviceName);
EXPECT_EQ(
error,
nearby_share::mojom::DeviceNameValidationResult::kErrorNotValidUtf8);
EXPECT_EQ(kFakeDefaultDeviceName, manager()->GetDeviceName());
EXPECT_TRUE(notifications().empty());
error = manager()->SetDeviceName(kFakeDeviceName);
EXPECT_EQ(error, nearby_share::mojom::DeviceNameValidationResult::kValid);
EXPECT_EQ(kFakeDeviceName, manager()->GetDeviceName()); EXPECT_EQ(kFakeDeviceName, manager()->GetDeviceName());
EXPECT_EQ(1u, notifications().size()); EXPECT_EQ(1u, notifications().size());
EXPECT_EQ(ObserverNotification(/*did_device_name_change=*/true, EXPECT_EQ(ObserverNotification(/*did_device_name_change=*/true,
......
...@@ -96,8 +96,20 @@ void NearbyShareSettings::GetDeviceName( ...@@ -96,8 +96,20 @@ void NearbyShareSettings::GetDeviceName(
std::move(callback).Run(GetDeviceName()); std::move(callback).Run(GetDeviceName());
} }
void NearbyShareSettings::SetDeviceName(const std::string& device_name) { void NearbyShareSettings::ValidateDeviceName(
local_device_data_manager_->SetDeviceName(device_name); const std::string& device_name,
base::OnceCallback<void(nearby_share::mojom::DeviceNameValidationResult)>
callback) {
std::move(callback).Run(
local_device_data_manager_->ValidateDeviceName(device_name));
}
void NearbyShareSettings::SetDeviceName(
const std::string& device_name,
base::OnceCallback<void(nearby_share::mojom::DeviceNameValidationResult)>
callback) {
return std::move(callback).Run(
local_device_data_manager_->SetDeviceName(device_name));
} }
void NearbyShareSettings::GetDataUsage( void NearbyShareSettings::GetDataUsage(
......
...@@ -61,7 +61,14 @@ class NearbyShareSettings : public nearby_share::mojom::NearbyShareSettings, ...@@ -61,7 +61,14 @@ class NearbyShareSettings : public nearby_share::mojom::NearbyShareSettings,
void SetEnabled(bool enabled) override; void SetEnabled(bool enabled) override;
void GetDeviceName( void GetDeviceName(
base::OnceCallback<void(const std::string&)> callback) override; base::OnceCallback<void(const std::string&)> callback) override;
void SetDeviceName(const std::string& device_name) override; void ValidateDeviceName(
const std::string& device_name,
base::OnceCallback<void(nearby_share::mojom::DeviceNameValidationResult)>
callback) override;
void SetDeviceName(
const std::string& device_name,
base::OnceCallback<void(nearby_share::mojom::DeviceNameValidationResult)>
callback) override;
void GetDataUsage(base::OnceCallback<void(nearby_share::mojom::DataUsage)> void GetDataUsage(base::OnceCallback<void(nearby_share::mojom::DataUsage)>
callback) override; callback) override;
void SetDataUsage(nearby_share::mojom::DataUsage data_usage) override; void SetDataUsage(nearby_share::mojom::DataUsage data_usage) override;
......
...@@ -119,13 +119,43 @@ TEST_F(NearbyShareSettingsTest, GetAndSetEnabled) { ...@@ -119,13 +119,43 @@ TEST_F(NearbyShareSettingsTest, GetAndSetEnabled) {
EXPECT_EQ(true, observer_.enabled); EXPECT_EQ(true, observer_.enabled);
} }
TEST_F(NearbyShareSettingsTest, ValidateDeviceName) {
auto result = nearby_share::mojom::DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
nearby_share_settings_waiter_.ValidateDeviceName("", &result);
EXPECT_EQ(result,
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
local_device_data_manager_.set_next_validation_result(
nearby_share::mojom::DeviceNameValidationResult::kValid);
nearby_share_settings_waiter_.ValidateDeviceName(
"this string is 32 bytes in UTF-8", &result);
EXPECT_EQ(result, nearby_share::mojom::DeviceNameValidationResult::kValid);
}
TEST_F(NearbyShareSettingsTest, GetAndSetDeviceName) { TEST_F(NearbyShareSettingsTest, GetAndSetDeviceName) {
std::string name = "not_the_default"; std::string name = "not_the_default";
nearby_share_settings_waiter_.GetDeviceName(&name); nearby_share_settings_waiter_.GetDeviceName(&name);
EXPECT_EQ(kDefaultDeviceName, name); EXPECT_EQ(kDefaultDeviceName, name);
// When we get a validation error, setting the name should not succeed.
EXPECT_EQ("uncalled", observer_.device_name);
auto result = nearby_share::mojom::DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
nearby_share_settings_waiter_.SetDeviceName("", &result);
EXPECT_EQ(result,
nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
EXPECT_EQ(kDefaultDeviceName, nearby_share_settings_.GetDeviceName());
// When the name is valid, setting should succeed.
EXPECT_EQ("uncalled", observer_.device_name); EXPECT_EQ("uncalled", observer_.device_name);
nearby_share_settings_.SetDeviceName("d"); result = nearby_share::mojom::DeviceNameValidationResult::kValid;
local_device_data_manager_.set_next_validation_result(
nearby_share::mojom::DeviceNameValidationResult::kValid);
nearby_share_settings_waiter_.SetDeviceName("d", &result);
EXPECT_EQ(result, nearby_share::mojom::DeviceNameValidationResult::kValid);
EXPECT_EQ("d", nearby_share_settings_.GetDeviceName()); EXPECT_EQ("d", nearby_share_settings_.GetDeviceName());
EXPECT_EQ("uncalled", observer_.device_name); EXPECT_EQ("uncalled", observer_.device_name);
......
...@@ -299,6 +299,7 @@ nearby_shared_auto_imports_closure_fix = [ ...@@ -299,6 +299,7 @@ nearby_shared_auto_imports_closure_fix = [
# does not exist on disk there. The actual resources are in # does not exist on disk there. The actual resources are in
# c/b/r/nearby_share/shared and are re-hosted in the chrome://os-settings # c/b/r/nearby_share/shared and are re-hosted in the chrome://os-settings
# webui at the chrome://os-settings/shared/* prefix. # webui at the chrome://os-settings/shared/* prefix.
"chrome/browser/resources/settings/shared/nearby_share_settings.html|getNearbyShareSettings",
"chrome/browser/resources/settings/shared/nearby_share_settings_behavior.html|NearbyShareSettingsBehavior,NearbySettings", "chrome/browser/resources/settings/shared/nearby_share_settings_behavior.html|NearbyShareSettingsBehavior,NearbySettings",
] ]
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../shared/nearby_share_settings.html">
<link rel="import" href="../../shared/nearby_share_settings_behavior.html"> <link rel="import" href="../../shared/nearby_share_settings_behavior.html">
<dom-module id="nearby-share-device-name-dialog"> <dom-module id="nearby-share-device-name-dialog">
...@@ -16,14 +17,17 @@ ...@@ -16,14 +17,17 @@
$i18n{nearbyShareDeviceNameDialogTitle} $i18n{nearbyShareDeviceNameDialogTitle}
</div> </div>
<div slot="body"> <div slot="body">
<cr-input value="[[settings.deviceName]]" autofocus> <cr-input value="[[settings.deviceName]]" on-input="onDeviceNameInput_"
error-message="[[errorMessage]]"
invalid="[[hasErrorMessage_(errorMessage)]]" autofocus>
</cr-input> </cr-input>
</div> </div>
<div class="layout horizontal center" slot="button-container"> <div class="layout horizontal center" slot="button-container">
<cr-button class="cancel-button" on-click="onCancelTap_"> <cr-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel} $i18n{cancel}
</cr-button> </cr-button>
<cr-button class="action-button" on-click="onDoneTap_"> <cr-button id="doneButton" class="action-button" on-click="onDoneTap_"
disabled="[[hasErrorMessage_(errorMessage)]]">
$i18n{done} $i18n{done}
</cr-button> </cr-button>
</div> </div>
......
...@@ -19,6 +19,12 @@ Polymer({ ...@@ -19,6 +19,12 @@ Polymer({
settings: { settings: {
type: Object, type: Object,
}, },
/** @type {string} */
errorMessage: {
type: String,
value: '',
},
}, },
attached() { attached() {
...@@ -39,6 +45,15 @@ Polymer({ ...@@ -39,6 +45,15 @@ Polymer({
} }
}, },
/** @private */
onDeviceNameInput_() {
nearby_share.getNearbyShareSettings()
.validateDeviceName(this.getEditInputValue_())
.then((result) => {
this.updateErrorMessage_(result.result);
});
},
/** @private */ /** @private */
onCancelTap_() { onCancelTap_() {
this.close(); this.close();
...@@ -46,7 +61,57 @@ Polymer({ ...@@ -46,7 +61,57 @@ Polymer({
/** @private */ /** @private */
onDoneTap_() { onDoneTap_() {
this.set('settings.deviceName', this.$$('cr-input').value); nearby_share.getNearbyShareSettings()
.setDeviceName(this.getEditInputValue_())
.then((result) => {
this.updateErrorMessage_(result.result);
if (result.result ===
nearbyShare.mojom.DeviceNameValidationResult.kValid) {
this.close(); this.close();
}
});
},
/**
* @private
*
* @param {!nearbyShare.mojom.DeviceNameValidationResult} validationResult The
* error status from validating the provided device name.
*/
updateErrorMessage_(validationResult) {
switch (validationResult) {
case nearbyShare.mojom.DeviceNameValidationResult.kErrorEmpty:
this.errorMessage = this.i18n('nearbyShareDeviceNameEmptyError');
break;
case nearbyShare.mojom.DeviceNameValidationResult.kErrorTooLong:
this.errorMessage = this.i18n('nearbyShareDeviceNameTooLongError');
break;
case nearbyShare.mojom.DeviceNameValidationResult.kErrorNotValidUtf8:
this.errorMessage =
this.i18n('nearbyShareDeviceNameInvalidCharactersError');
break;
default:
this.errorMessage = '';
break;
}
},
/**
* @private
*
* @return {!string}
*/
getEditInputValue_() {
return this.$$('cr-input').value;
}, },
/**
* @private
*
* @param {!string} errorMessage The error message.
* @return {boolean} Whether or not the error message exists.
*/
hasErrorMessage_(errorMessage) {
return errorMessage !== '';
}
}); });
...@@ -30,6 +30,18 @@ enum Visibility { ...@@ -30,6 +30,18 @@ enum Visibility {
kSelectedContacts = 3 kSelectedContacts = 3
}; };
// Represents the error result when validating the device name.
enum DeviceNameValidationResult {
// The device name was valid.
kValid = 0,
// The device name must not be empty.
kErrorEmpty = 1,
// The device name is too long.
kErrorTooLong = 2,
// The device name is not valid UTF-8.
kErrorNotValidUtf8 = 3
};
// This observer interface allows clients to be notified whenever key Nearby // This observer interface allows clients to be notified whenever key Nearby
// Share settings are changed. // Share settings are changed.
interface NearbyShareSettingsObserver { interface NearbyShareSettingsObserver {
...@@ -60,9 +72,14 @@ interface NearbyShareSettings { ...@@ -60,9 +72,14 @@ interface NearbyShareSettings {
// Get the device name shown to a sender when this device is available as // Get the device name shown to a sender when this device is available as
// a share target. |device_name| is the new device name. // a share target. |device_name| is the new device name.
GetDeviceName() => (string device_name); GetDeviceName() => (string device_name);
// Validate the device name shown to a sender when this device is available
// as a share target. This is a separate function so that we can call it to
// validate input as a user types.
ValidateDeviceName(string device_name) => (DeviceNameValidationResult result);
// Set the device name shown to a sender when this device is available as // Set the device name shown to a sender when this device is available as
// a share target. // a share target. Before saving the new name this will validate it and
SetDeviceName(string device_name); // respond with an error if it is invalid.
SetDeviceName(string device_name) => (DeviceNameValidationResult result);
// Get which type of network connectivity Nearby Share can operate on. // Get which type of network connectivity Nearby Share can operate on.
GetDataUsage() => (DataUsage data_usage); GetDataUsage() => (DataUsage data_usage);
......
...@@ -114,6 +114,11 @@ void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) { ...@@ -114,6 +114,11 @@ void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
IDS_NEARBY_CONTACT_VISIBILITY_ZERO_STATE_INFO}, IDS_NEARBY_CONTACT_VISIBILITY_ZERO_STATE_INFO},
{"nearbyShareContactVisibilityZeroStateText", {"nearbyShareContactVisibilityZeroStateText",
IDS_NEARBY_CONTACT_VISIBILITY_ZERO_STATE_TEXT}, IDS_NEARBY_CONTACT_VISIBILITY_ZERO_STATE_TEXT},
{"nearbyShareDeviceNameEmptyError", IDS_NEARBY_DEVICE_NAME_EMPTY_ERROR},
{"nearbyShareDeviceNameTooLongError",
IDS_NEARBY_DEVICE_NAME_TOO_LONG_ERROR},
{"nearbyShareDeviceNameInvalidCharactersError",
IDS_NEARBY_DEVICE_NAME_INVALID_CHARACTERS_ERROR},
{"nearbyShareDiscoveryPageInfo", IDS_NEARBY_DISCOVERY_PAGE_INFO}, {"nearbyShareDiscoveryPageInfo", IDS_NEARBY_DISCOVERY_PAGE_INFO},
{"nearbyShareDiscoveryPageSubtitle", IDS_NEARBY_DISCOVERY_PAGE_SUBTITLE}, {"nearbyShareDiscoveryPageSubtitle", IDS_NEARBY_DISCOVERY_PAGE_SUBTITLE},
{"nearbyShareDiscoveryPageTitle", IDS_NEARBY_DISCOVERY_PAGE_TITLE}, {"nearbyShareDiscoveryPageTitle", IDS_NEARBY_DISCOVERY_PAGE_TITLE},
......
...@@ -25,6 +25,9 @@ cr.define('nearby_share', function() { ...@@ -25,6 +25,9 @@ cr.define('nearby_share', function() {
this.allowedContacts_ = []; this.allowedContacts_ = [];
/** @private {!nearbyShare.mojom.NearbyShareSettingsObserverInterface} */ /** @private {!nearbyShare.mojom.NearbyShareSettingsObserverInterface} */
this.observer_; this.observer_;
/** @private {!nearbyShare.mojom.DeviceNameValidationResult} */
this.nextDeviceNameResult_ =
nearbyShare.mojom.DeviceNameValidationResult.kValid;
/** @private {Object} */ /** @private {Object} */
this.$ = { this.$ = {
close() {}, close() {},
...@@ -40,6 +43,15 @@ cr.define('nearby_share', function() { ...@@ -40,6 +43,15 @@ cr.define('nearby_share', function() {
this.observer_ = observer; this.observer_ = observer;
} }
/**
* @param { !nearbyShare.mojom.DeviceNameValidationResult } result
*/
setNextDeviceNameResult(result) {
// Set the next result to be used when calling ValidateDeviceName() or
// SetDeviceName().
this.nextDeviceNameResult_ = result;
}
/** /**
* @return {!Promise<{enabled: !boolean}>} * @return {!Promise<{enabled: !boolean}>}
*/ */
...@@ -66,12 +78,32 @@ cr.define('nearby_share', function() { ...@@ -66,12 +78,32 @@ cr.define('nearby_share', function() {
/** /**
* @param { !string } deviceName * @param { !string } deviceName
* @return {!Promise<{
result: !nearbyShare.mojom.DeviceNameValidationResult,
* }>}
*/
async validateDeviceName(deviceName) {
return {result: this.nextDeviceNameResult_};
}
/**
* @param { !string } deviceName
* @return {!Promise<{
result: !nearbyShare.mojom.DeviceNameValidationResult,
* }>}
*/ */
setDeviceName(deviceName) { async setDeviceName(deviceName) {
if (this.nextDeviceNameResult_ !==
nearbyShare.mojom.DeviceNameValidationResult.kValid) {
return {result: this.nextDeviceNameResult_};
}
this.deviceName_ = deviceName; this.deviceName_ = deviceName;
if (this.observer_) { if (this.observer_) {
this.observer_.onDeviceNameChanged(deviceName); this.observer_.onDeviceNameChanged(deviceName);
} }
return {result: this.nextDeviceNameResult_};
} }
/** /**
......
...@@ -56,6 +56,8 @@ suite('NearbyShare', function() { ...@@ -56,6 +56,8 @@ suite('NearbyShare', function() {
let accountManagerBrowserProxy = null; let accountManagerBrowserProxy = null;
/** @type {!nearby_share.FakeContactManager} */ /** @type {!nearby_share.FakeContactManager} */
const fakeContactManager = new nearby_share.FakeContactManager(); const fakeContactManager = new nearby_share.FakeContactManager();
/** @type {!nearby_share.FakeNearbyShareSettings} */
let fakeSettings = null;
setup(function() { setup(function() {
accountManagerBrowserProxy = new TestAccountManagerBrowserProxy(); accountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
...@@ -68,8 +70,7 @@ suite('NearbyShare', function() { ...@@ -68,8 +70,7 @@ suite('NearbyShare', function() {
nearby_share.setContactManagerForTesting(fakeContactManager); nearby_share.setContactManagerForTesting(fakeContactManager);
fakeContactManager.setupContactRecords(); fakeContactManager.setupContactRecords();
/** @type {!nearbyShare.mojom.NearbyShareSettingsInterface} */ fakeSettings = new nearby_share.FakeNearbyShareSettings();
const fakeSettings = new nearby_share.FakeNearbyShareSettings();
fakeSettings.setEnabled(true); fakeSettings.setEnabled(true);
nearby_share.setNearbyShareSettingsForTesting(fakeSettings); nearby_share.setNearbyShareSettingsForTesting(fakeSettings);
...@@ -162,6 +163,31 @@ suite('NearbyShare', function() { ...@@ -162,6 +163,31 @@ suite('NearbyShare', function() {
subpage.set('settings.deviceName', oldName); subpage.set('settings.deviceName', oldName);
}); });
test('validate device name preference', async () => {
subpage.$$('#editDeviceNameButton').click();
Polymer.dom.flush();
const dialog = subpage.$$('nearby-share-device-name-dialog');
const input = dialog.$$('cr-input');
const doneButton = dialog.$$('#doneButton');
fakeSettings.setNextDeviceNameResult(
nearbyShare.mojom.DeviceNameValidationResult.kErrorEmpty);
input.fire('input');
// Allow the validation promise to resolve.
await test_util.waitAfterNextRender();
Polymer.dom.flush();
assertTrue(input.invalid);
assertTrue(doneButton.disabled);
fakeSettings.setNextDeviceNameResult(
nearbyShare.mojom.DeviceNameValidationResult.kValid);
input.fire('input');
await test_util.waitAfterNextRender();
Polymer.dom.flush();
assertFalse(input.invalid);
assertFalse(doneButton.disabled);
});
test('update data usage preference', function() { test('update data usage preference', function() {
assertEquals(3, subpage.prefs.nearby_sharing.data_usage.value); assertEquals(3, subpage.prefs.nearby_sharing.data_usage.value);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment