summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordzhioev@chromium.org <dzhioev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-13 05:41:09 +0000
committerdzhioev@chromium.org <dzhioev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-13 05:41:09 +0000
commitd4839996076e54913112005350125d159f04898e (patch)
tree072b98029a76020a21bd6b6fb7a2002c433c7004
parent913d2062c2ee21100a2b03bd72f36c50c3b09cbe (diff)
downloadchromium_src-d4839996076e54913112005350125d159f04898e.zip
chromium_src-d4839996076e54913112005350125d159f04898e.tar.gz
chromium_src-d4839996076e54913112005350125d159f04898e.tar.bz2
Implemented sync of user account images in ChromeOS.
Currently not works for custom images, only for default and G+ picture. Sync can be disabled by --disable-user-image-sync or by flag. BUG=130302 TBR=darin,mnissler Review URL: https://chromiumcodereview.appspot.com/22295011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217210 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc9
-rw-r--r--chrome/browser/automation/testing_automation_provider_chromeos.cc4
-rw-r--r--chrome/browser/chromeos/login/screens/user_image_screen.cc103
-rw-r--r--chrome/browser/chromeos/login/screens/user_image_screen.h35
-rw-r--r--chrome/browser/chromeos/login/screens/user_image_screen_actor.h11
-rw-r--r--chrome/browser/chromeos/login/user.cc9
-rw-r--r--chrome/browser/chromeos/login/user.h3
-rw-r--r--chrome/browser/chromeos/login/user_image_manager.h8
-rw-r--r--chrome/browser/chromeos/login/user_image_manager_impl.cc23
-rw-r--r--chrome/browser/chromeos/login/user_image_manager_impl.h9
-rw-r--r--chrome/browser/chromeos/login/user_image_sync_observer.cc210
-rw-r--r--chrome/browser/chromeos/login/user_image_sync_observer.h104
-rw-r--r--chrome/browser/chromeos/login/user_manager_impl.cc1
-rw-r--r--chrome/browser/prefs/browser_prefs.cc2
-rw-r--r--chrome/browser/resources/chromeos/login/oobe_screen_user_image.css5
-rw-r--r--chrome/browser/resources/chromeos/login/oobe_screen_user_image.js15
-rw-r--r--chrome/browser/resources/chromeos/login/screen_gaia_signin.css7
-rw-r--r--chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc21
-rw-r--r--chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h11
-rw-r--r--chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc7
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chromeos/chromeos_switches.cc3
-rw-r--r--chromeos/chromeos_switches.h1
24 files changed, 573 insertions, 36 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b705b3f..9c7a809 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6443,6 +6443,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_ENABLE_DEVICE_MOTION_DESCRIPTION" desc="Description for the flag to enable device motion.">
Enables device motion DOM events in JavaScript.
</message>
+ <message name="IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_NAME" desc="Name of the flag to disable sync of user account image.">
+ Disable avatar sync.
+ </message>
+ <message name="IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_DESCRIPTION" desc="Description for the flag to disable sync of user account image.">
+ Disables sync of user account image between different ChromeOS devices.
+ </message>
<!-- Crashes -->
<message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f4ff5a2..0e93c9e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1610,6 +1610,15 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(switches::kDisableAppList)
},
#endif
+#if defined(OS_CHROMEOS)
+ {
+ "disable-user-image-sync",
+ IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_NAME,
+ IDS_FLAGS_DISABLE_USER_IMAGE_SYNC_DESCRIPTION,
+ kOsCrOS,
+ SINGLE_VALUE_TYPE(chromeos::switches::kDisableUserImageSync)
+ },
+#endif
};
const Experiment* experiments = kExperiments;
diff --git a/chrome/browser/automation/testing_automation_provider_chromeos.cc b/chrome/browser/automation/testing_automation_provider_chromeos.cc
index 598da8d..f1b91d5 100644
--- a/chrome/browser/automation/testing_automation_provider_chromeos.cc
+++ b/chrome/browser/automation/testing_automation_provider_chromeos.cc
@@ -338,12 +338,12 @@ void TestingAutomationProvider::PickUserImage(DictionaryValue* args,
WizardControllerObserver* observer =
new WizardControllerObserver(wizard_controller, this, reply_message);
if (image_type == "profile") {
- image_screen->OnImageSelected("", image_type);
+ image_screen->OnImageSelected("", image_type, true);
image_screen->OnImageAccepted();
} else if (image_type.empty() && image_number >= 0 &&
image_number < chromeos::kDefaultImagesCount) {
image_screen->OnImageSelected(
- chromeos::GetDefaultImageUrl(image_number), image_type);
+ chromeos::GetDefaultImageUrl(image_number), image_type, true);
image_screen->OnImageAccepted();
} else {
AutomationJSONReply(this, reply_message).SendError(
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index 9542be3..a245be3 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -6,6 +6,7 @@
#include "base/compiler_specific.h"
#include "base/metrics/histogram.h"
+#include "base/timer/timer.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/camera_detector.h"
@@ -36,6 +37,10 @@ namespace {
// Time histogram suffix for profile image download.
const char kProfileDownloadReason[] = "OOBE";
+// Maximum ammount of time to wait for the user image to sync.
+// The screen is shown iff sync failed or time limit exceeded.
+const int kSyncTimeoutSeconds = 10;
+
} // namespace
UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
@@ -47,9 +52,13 @@ UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
selected_image_(User::kInvalidImageIndex),
profile_picture_enabled_(false),
profile_picture_data_url_(content::kAboutBlankURL),
- profile_picture_absent_(false) {
+ profile_picture_absent_(false),
+ is_screen_ready_(false),
+ user_has_selected_image_(false) {
actor_->SetDelegate(this);
SetProfilePictureEnabled(true);
+ registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
+ content::NotificationService::AllSources());
}
UserImageScreen::~UserImageScreen() {
@@ -59,17 +68,10 @@ UserImageScreen::~UserImageScreen() {
image_decoder_->set_delegate(NULL);
}
-void UserImageScreen::CheckCameraPresence() {
- CameraDetector::StartPresenceCheck(
- base::Bind(&UserImageScreen::OnCameraPresenceCheckDone,
- weak_factory_.GetWeakPtr()));
-}
-
-void UserImageScreen::OnCameraPresenceCheckDone() {
- if (actor_) {
- actor_->SetCameraPresent(
- CameraDetector::camera_presence() == CameraDetector::kCameraPresent);
- }
+void UserImageScreen::OnScreenReady() {
+ is_screen_ready_ = true;
+ if (actor_ && !IsWaitingForSync())
+ actor_->HideCurtain();
}
void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
@@ -84,6 +86,20 @@ void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
image_decoder_->Start(task_runner);
}
+void UserImageScreen::CheckCameraPresence() {
+ CameraDetector::StartPresenceCheck(
+ base::Bind(&UserImageScreen::OnCameraPresenceCheckDone,
+ weak_factory_.GetWeakPtr()));
+}
+
+void UserImageScreen::OnCameraPresenceCheckDone() {
+ if (actor_) {
+ actor_->SetCameraPresent(
+ CameraDetector::camera_presence() == CameraDetector::kCameraPresent);
+ }
+}
+
+
void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
const SkBitmap& decoded_image) {
DCHECK_EQ(image_decoder_.get(), decoder);
@@ -96,8 +112,38 @@ void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
NOTREACHED() << "Failed to decode PNG image from WebUI";
}
+void UserImageScreen::OnInitialSync(bool local_image_updated) {
+ DCHECK(sync_timer_.get());
+ sync_timer_->Stop();
+ sync_timer_.reset();
+ UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
+ RemoveObserver(this);
+ if (!local_image_updated) {
+ if (is_screen_ready_ && actor_)
+ actor_->HideCurtain();
+ return;
+ }
+ get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
+}
+
+void UserImageScreen::OnSyncTimeout() {
+ sync_timer_.reset();
+ UserManager::Get()->GetUserImageManager()->GetSyncObserver()->
+ RemoveObserver(this);
+ if (is_screen_ready_ && actor_)
+ actor_->HideCurtain();
+}
+
+bool UserImageScreen::IsWaitingForSync() const {
+ return sync_timer_.get() && sync_timer_->IsRunning();
+}
+
void UserImageScreen::OnImageSelected(const std::string& image_type,
- const std::string& image_url) {
+ const std::string& image_url,
+ bool is_user_selection) {
+ if (is_user_selection) {
+ user_has_selected_image_ = true;
+ }
if (image_url.empty())
return;
int user_image_index = User::kInvalidImageIndex;
@@ -139,9 +185,11 @@ void UserImageScreen::OnImageAccepted() {
uma_index = GetDefaultImageHistogramValue(selected_image_);
break;
}
- UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
- uma_index,
- kHistogramImagesCount);
+ if (user_has_selected_image_) {
+ UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
+ uma_index,
+ kHistogramImagesCount);
+ }
get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
}
@@ -186,7 +234,23 @@ const User* UserImageScreen::GetUser() {
void UserImageScreen::Show() {
if (!actor_)
return;
-
+ if (GetUser()->CanSyncImage()) {
+ if (UserImageSyncObserver* sync_observer =
+ UserManager::Get()->GetUserImageManager()->GetSyncObserver()) {
+ // We have synced image already.
+ if (sync_observer->is_synced()) {
+ get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
+ return;
+ }
+ sync_observer->AddObserver(this);
+ sync_timer_.reset(new base::Timer(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
+ base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
+ false));
+ sync_timer_->Reset();
+ }
+ }
actor_->Show();
actor_->SetProfilePictureEnabled(profile_picture_enabled_);
@@ -237,6 +301,11 @@ void UserImageScreen::Observe(int type,
actor_->OnProfileImageAbsent();
break;
}
+ case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
+ if (actor_)
+ actor_->SelectImage(GetUser()->image_index());
+ break;
+ }
default:
NOTREACHED();
}
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.h b/chrome/browser/chromeos/login/screens/user_image_screen.h
index 28c61df..21780cc 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.h
@@ -10,16 +10,22 @@
#include "chrome/browser/chromeos/login/screens/user_image_screen_actor.h"
#include "chrome/browser/chromeos/login/screens/wizard_screen.h"
#include "chrome/browser/chromeos/login/user.h"
+#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
#include "chrome/browser/image_decoder.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+namespace base {
+class Timer;
+};
+
namespace chromeos {
class UserImageScreen: public WizardScreen,
public UserImageScreenActor::Delegate,
public ImageDecoder::Delegate,
- public content::NotificationObserver {
+ public content::NotificationObserver,
+ public UserImageSyncObserver::Observer {
public:
UserImageScreen(ScreenObserver* screen_observer,
UserImageScreenActor* actor);
@@ -37,10 +43,12 @@ class UserImageScreen: public WizardScreen,
virtual std::string GetName() const OVERRIDE;
// UserImageScreenActor::Delegate implementation:
- virtual void CheckCameraPresence() OVERRIDE;
+ virtual void OnScreenReady() OVERRIDE;
virtual void OnPhotoTaken(const std::string& raw_data) OVERRIDE;
+ virtual void CheckCameraPresence() OVERRIDE;
virtual void OnImageSelected(const std::string& image_url,
- const std::string& image_type) OVERRIDE;
+ const std::string& image_type,
+ bool is_user_selection) OVERRIDE;
virtual void OnImageAccepted() OVERRIDE;
virtual void OnActorDestroyed(UserImageScreenActor* actor) OVERRIDE;
@@ -53,12 +61,22 @@ class UserImageScreen: public WizardScreen,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
- // Overriden from ImageDecoder::Delegate:
+ // ImageDecoder::Delegate implementation:
virtual void OnImageDecoded(const ImageDecoder* decoder,
const SkBitmap& decoded_image) OVERRIDE;
virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE;
+ // UserImageSyncObserver::Observer implementation:
+ virtual void OnInitialSync(bool local_image_updated) OVERRIDE;
+
+ bool user_selected_image() const { return user_has_selected_image_; }
+
private:
+ // Called when whaiting for sync timed out.
+ void OnSyncTimeout();
+
+ bool IsWaitingForSync() const;
+
const User* GetUser();
// Called when the camera presence check has been completed.
@@ -95,6 +113,15 @@ class UserImageScreen: public WizardScreen,
std::string user_id_;
+ // Timer used for waiting for user image sync.
+ scoped_ptr<base::Timer> sync_timer_;
+
+ // If screen ready to be shown.
+ bool is_screen_ready_;
+
+ // True if user has explicitly selected some image.
+ bool user_has_selected_image_;
+
DISALLOW_COPY_AND_ASSIGN(UserImageScreen);
};
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen_actor.h b/chrome/browser/chromeos/login/screens/user_image_screen_actor.h
index 8f99e98..5e5d073 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen_actor.h
+++ b/chrome/browser/chromeos/login/screens/user_image_screen_actor.h
@@ -23,13 +23,17 @@ class UserImageScreenActor {
public:
virtual ~Delegate() {}
+ // Called when UI ready to be shown.
+ virtual void OnScreenReady() = 0;
// Called when user accepts photo as login user image.
virtual void OnPhotoTaken(const std::string& raw_data) = 0;
// Called to check camera presence.
virtual void CheckCameraPresence() = 0;
- // Called when user selects some image
+ // Called when some image was selected. |is_user_selection| indicates if
+ // it was user selection or image was selected programmatically.
virtual void OnImageSelected(const std::string& image_url,
- const std::string& image_type) = 0;
+ const std::string& image_type,
+ bool is_user_selection) = 0;
// Called when user accepts currently selected image
virtual void OnImageAccepted() = 0;
@@ -70,6 +74,9 @@ class UserImageScreenActor {
// Sends result of camera check
virtual void SetCameraPresent(bool enabled) = 0;
+
+ // Hides curtain with spinner.
+ virtual void HideCurtain() = 0;
};
} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/user.cc b/chrome/browser/chromeos/login/user.cc
index 5e47a5a..ffe6c47 100644
--- a/chrome/browser/chromeos/login/user.cc
+++ b/chrome/browser/chromeos/login/user.cc
@@ -34,6 +34,7 @@ class RegularUser : public User {
// Overridden from User:
virtual UserType GetType() const OVERRIDE;
+ virtual bool CanSyncImage() const OVERRIDE;
virtual bool can_lock() const OVERRIDE;
private:
@@ -151,6 +152,10 @@ bool User::HasDefaultImage() const {
return image_index_ >= 0 && image_index_ < kDefaultImagesCount;
}
+bool User::CanSyncImage() const {
+ return false;
+}
+
std::string User::display_email() const {
return display_email_;
}
@@ -238,6 +243,10 @@ User::UserType RegularUser::GetType() const {
return USER_TYPE_REGULAR;
}
+bool RegularUser::CanSyncImage() const {
+ return true;
+}
+
bool RegularUser::can_lock() const {
return true;
}
diff --git a/chrome/browser/chromeos/login/user.h b/chrome/browser/chromeos/login/user.h
index 35911bb..4465aab 100644
--- a/chrome/browser/chromeos/login/user.h
+++ b/chrome/browser/chromeos/login/user.h
@@ -112,6 +112,9 @@ class User {
// Whether the user has a default image.
bool HasDefaultImage() const;
+ // True if user image can be synced.
+ virtual bool CanSyncImage() const;
+
int image_index() const { return image_index_; }
bool has_raw_image() const { return user_image_.has_raw_image(); }
// Returns raw representation of static user image.
diff --git a/chrome/browser/chromeos/login/user_image_manager.h b/chrome/browser/chromeos/login/user_image_manager.h
index eeec8a0..383fafe 100644
--- a/chrome/browser/chromeos/login/user_image_manager.h
+++ b/chrome/browser/chromeos/login/user_image_manager.h
@@ -22,6 +22,7 @@ class ImageSkia;
namespace chromeos {
class UserImage;
+class UserImageSyncObserver;
// Base class that provides a mechanism for updating user images.
class UserImageManager {
@@ -74,6 +75,13 @@ class UserImageManager {
// Returns the result of the last successful profile image download, if any.
// Otherwise, returns an empty bitmap.
virtual const gfx::ImageSkia& DownloadedProfileImage() const = 0;
+
+ // Returns sync observer attached to current user. Returns NULL if current
+ // user can't sync images or user is not logged in.
+ virtual UserImageSyncObserver* GetSyncObserver() const = 0;
+
+ // Unregisters preference observers before browser process shutdown.
+ virtual void Shutdown() = 0;
};
} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.cc b/chrome/browser/chromeos/login/user_image_manager_impl.cc
index f66f0f0..ec9f7d6 100644
--- a/chrome/browser/chromeos/login/user_image_manager_impl.cc
+++ b/chrome/browser/chromeos/login/user_image_manager_impl.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
@@ -22,11 +23,14 @@
#include "chrome/browser/chromeos/login/default_user_images.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/user_image.h"
+#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile_downloader.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/url_constants.h"
@@ -266,12 +270,11 @@ void UserImageManagerImpl::LoadUserImages(const UserList& users) {
void UserImageManagerImpl::UserLoggedIn(const std::string& email,
bool user_is_new,
bool user_is_local) {
+ User* user = UserManager::Get()->GetLoggedInUser();
if (user_is_new) {
if (!user_is_local)
SetInitialUserImage(email);
} else {
- User* user = UserManager::Get()->GetLoggedInUser();
-
if (!user_is_local) {
// If current user image is profile image, it needs to be refreshed.
bool download_profile_image =
@@ -334,6 +337,14 @@ void UserImageManagerImpl::UserLoggedIn(const std::string& email,
FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec),
this, &UserImageManagerImpl::DownloadProfileDataScheduled);
}
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (user->CanSyncImage() &&
+ !command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) {
+ if (user_image_sync_observer_.get() &&
+ !command_line->HasSwitch(::switches::kMultiProfiles))
+ NOTREACHED() << "User logged in second time.";
+ user_image_sync_observer_.reset(new UserImageSyncObserver(user));
+ }
}
void UserImageManagerImpl::SaveUserDefaultImageIndex(
@@ -401,6 +412,14 @@ void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
DownloadProfileData(reason, true);
}
+UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
+ return user_image_sync_observer_.get();
+}
+
+void UserImageManagerImpl::Shutdown() {
+ user_image_sync_observer_.reset();
+}
+
const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return downloaded_profile_image_;
diff --git a/chrome/browser/chromeos/login/user_image_manager_impl.h b/chrome/browser/chromeos/login/user_image_manager_impl.h
index ef6da72..3b09c47 100644
--- a/chrome/browser/chromeos/login/user_image_manager_impl.h
+++ b/chrome/browser/chromeos/login/user_image_manager_impl.h
@@ -26,6 +26,10 @@ class FilePath;
}
namespace chromeos {
+class UserImageSyncObserver;
+}
+
+namespace chromeos {
class UserImageManagerImpl : public UserImageManager,
public ProfileDownloaderDelegate {
@@ -49,6 +53,8 @@ class UserImageManagerImpl : public UserImageManager,
virtual void DeleteUserImage(const std::string& username) OVERRIDE;
virtual void DownloadProfileImage(const std::string& reason) OVERRIDE;
virtual const gfx::ImageSkia& DownloadedProfileImage() const OVERRIDE;
+ virtual UserImageSyncObserver* GetSyncObserver() const OVERRIDE;
+ virtual void Shutdown() OVERRIDE;
private:
friend class UserImageManagerTest;
@@ -175,6 +181,9 @@ class UserImageManagerImpl : public UserImageManager,
// If |true|, current user image should be migrated right after it is loaded.
bool migrate_current_user_on_load_;
+ // Sync observer attached to current user.
+ scoped_ptr<UserImageSyncObserver> user_image_sync_observer_;
+
DISALLOW_COPY_AND_ASSIGN(UserImageManagerImpl);
};
diff --git a/chrome/browser/chromeos/login/user_image_sync_observer.cc b/chrome/browser/chromeos/login/user_image_sync_observer.cc
new file mode 100644
index 0000000..a20f6e9
--- /dev/null
+++ b/chrome/browser/chromeos/login/user_image_sync_observer.cc
@@ -0,0 +1,210 @@
+// Copyright 2013 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 "chrome/browser/chromeos/login/user_image_sync_observer.h"
+
+#include "base/bind.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/default_user_images.h"
+#include "chrome/browser/chromeos/login/screens/user_image_screen.h"
+#include "chrome/browser/chromeos/login/user.h"
+#include "chrome/browser/chromeos/login/user_image_manager.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+
+namespace chromeos {
+namespace {
+
+// A dictionary containing info about user image.
+const char kUserImageInfo[] = "user_image_info";
+// Path to value with image index.
+const char kImageIndex[] = "image_index";
+
+bool IsIndexSupported(int index) {
+ return (index >= kFirstDefaultImageIndex && index < kDefaultImagesCount) ||
+ (index == User::kProfileImageIndex);
+}
+
+Profile* GetUserProfile() {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (!profile_manager)
+ return NULL;
+ base::FilePath profile_path = profile_manager->GetInitialProfileDir();
+ return profile_manager->GetProfileByPath(profile_path);
+}
+
+} // anonymous namespace
+
+UserImageSyncObserver::Observer::~Observer() {}
+
+UserImageSyncObserver::UserImageSyncObserver(const User* user)
+ : user_(user),
+ prefs_(NULL),
+ is_synced_(false),
+ local_image_changed_(false) {
+ notification_registrar_.reset(new content::NotificationRegistrar);
+ notification_registrar_->Add(this,
+ chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
+ content::NotificationService::AllSources());
+ Profile* profile = GetUserProfile();
+ if (profile) {
+ OnProfileGained(profile);
+ } else {
+ notification_registrar_->Add(this,
+ chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+ content::NotificationService::AllSources());
+ }
+}
+
+UserImageSyncObserver::~UserImageSyncObserver() {
+ if (!is_synced_ && prefs_)
+ prefs_->RemoveObserver(this);
+ if (pref_change_registrar_)
+ pref_change_registrar_->RemoveAll();
+}
+
+// static
+void UserImageSyncObserver::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry_) {
+ registry_->RegisterDictionaryPref(
+ kUserImageInfo,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
+}
+
+void UserImageSyncObserver::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void UserImageSyncObserver::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void UserImageSyncObserver::OnProfileGained(Profile* profile) {
+ prefs_ = PrefServiceSyncable::FromProfile(profile);
+ pref_change_registrar_.reset(new PrefChangeRegistrar);
+ pref_change_registrar_->Init(prefs_);
+ pref_change_registrar_->Add(kUserImageInfo,
+ base::Bind(&UserImageSyncObserver::OnPreferenceChanged,
+ base::Unretained(this)));
+ is_synced_ = prefs_->IsPrioritySyncing();
+ if (!is_synced_) {
+ prefs_->AddObserver(this);
+ } else {
+ OnInitialSync();
+ }
+}
+
+void UserImageSyncObserver::OnInitialSync() {
+ int synced_index;
+ bool local_image_updated = false;
+ if (!GetSyncedImageIndex(&synced_index) || local_image_changed_) {
+ UpdateSyncedImageFromLocal();
+ } else if (IsIndexSupported(synced_index) && CanUpdateLocalImageNow()) {
+ UpdateLocalImageFromSynced();
+ local_image_updated = true;
+ }
+ FOR_EACH_OBSERVER(UserImageSyncObserver::Observer, observer_list_,
+ OnInitialSync(local_image_updated));
+}
+
+void UserImageSyncObserver::OnPreferenceChanged(const std::string& pref_name) {
+ // OnPreferenceChanged can be called before OnIsSyncingChanged.
+ if (!is_synced_) {
+ is_synced_ = true;
+ prefs_->RemoveObserver(this);
+ OnInitialSync();
+ } else if (CanUpdateLocalImageNow()) {
+ UpdateLocalImageFromSynced();
+ }
+}
+
+void UserImageSyncObserver::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
+ Profile* profile = content::Details<Profile>(details).ptr();
+ if (GetUserProfile() != profile)
+ return;
+ notification_registrar_->Remove(
+ this,
+ chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+ content::NotificationService::AllSources());
+ OnProfileGained(profile);
+ } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
+ if (is_synced_)
+ UpdateSyncedImageFromLocal();
+ else
+ local_image_changed_ = true;
+ } else {
+ NOTREACHED();
+ }
+}
+
+void UserImageSyncObserver::OnIsSyncingChanged() {
+ is_synced_ = prefs_->IsPrioritySyncing();
+ if (is_synced_) {
+ prefs_->RemoveObserver(this);
+ OnInitialSync();
+ }
+}
+
+void UserImageSyncObserver::UpdateSyncedImageFromLocal() {
+ int local_index = user_->image_index();
+ if (!IsIndexSupported(local_index)) {
+ local_index = User::kInvalidImageIndex;
+ }
+ int synced_index;
+ if (GetSyncedImageIndex(&synced_index) && (synced_index == local_index))
+ return;
+ DictionaryPrefUpdate update(prefs_, kUserImageInfo);
+ DictionaryValue* dict = update.Get();
+ dict->SetInteger(kImageIndex, local_index);
+ LOG(INFO) << "Saved avatar index " << local_index << " to sync.";
+}
+
+void UserImageSyncObserver::UpdateLocalImageFromSynced() {
+ int synced_index;
+ GetSyncedImageIndex(&synced_index);
+ int local_index = user_->image_index();
+ if ((synced_index == local_index) || !IsIndexSupported(synced_index))
+ return;
+ UserImageManager* image_manager = UserManager::Get()->GetUserImageManager();
+ if (synced_index == User::kProfileImageIndex) {
+ image_manager->SaveUserImageFromProfileImage(user_->email());
+ } else {
+ image_manager->SaveUserDefaultImageIndex(user_->email(), synced_index);
+ }
+ LOG(INFO) << "Loaded avatar index " << synced_index << " from sync.";
+}
+
+bool UserImageSyncObserver::GetSyncedImageIndex(int* index) {
+ *index = User::kInvalidImageIndex;
+ const DictionaryValue* dict = prefs_->GetDictionary(kUserImageInfo);
+ return dict && dict->GetInteger(kImageIndex, index);
+}
+
+bool UserImageSyncObserver::CanUpdateLocalImageNow() {
+ if (WizardController* wizard_controller =
+ WizardController::default_controller()) {
+ UserImageScreen* screen = wizard_controller->GetUserImageScreen();
+ if (wizard_controller->current_screen() == screen) {
+ if (screen->user_selected_image())
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace chromeos
+
diff --git a/chrome/browser/chromeos/login/user_image_sync_observer.h b/chrome/browser/chromeos/login/user_image_sync_observer.h
new file mode 100644
index 0000000..16a9eb7
--- /dev/null
+++ b/chrome/browser/chromeos/login/user_image_sync_observer.h
@@ -0,0 +1,104 @@
+// Copyright 2013 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 CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/prefs/pref_service_syncable_observer.h"
+#include "content/public/browser/notification_observer.h"
+
+class PrefChangeRegistrar;
+class PrefServiceSyncable;
+class Profile;
+namespace chromeos {
+class User;
+}
+namespace content {
+class NotificationRegistrar;
+}
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace chromeos {
+
+// This class is responsible for keeping local user image synced with
+// image saved in syncable preference.
+class UserImageSyncObserver: public PrefServiceSyncableObserver,
+ public content::NotificationObserver {
+ public:
+ class Observer {
+ public:
+ // Called right after image info synced (i.e. |is_synced| became |true|).
+ // |local_image_updated| indicates if we desided to update local image in
+ // result of sync.
+ virtual void OnInitialSync(bool local_image_updated) = 0;
+ virtual ~Observer();
+ };
+
+ public:
+ explicit UserImageSyncObserver(const User* user);
+ virtual ~UserImageSyncObserver();
+
+ // Register syncable preference for profile.
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Returns |true| if sync was initialized and prefs have actual state.
+ bool is_synced() const { return is_synced_; }
+
+ // Adds |observer| into observers list.
+ void AddObserver(Observer* observer);
+ // Removes |observer| from observers list.
+ void RemoveObserver(Observer* observer);
+
+ private:
+ // PrefServiceSyncableObserver implementation.
+ virtual void OnIsSyncingChanged() OVERRIDE;
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Called after user profile was loaded.
+ void OnProfileGained(Profile* profile);
+
+ // Called when sync servise started it's work and we are able to sync needed
+ // preferences.
+ void OnInitialSync();
+
+ // Called when preference |pref_name| was changed.j
+ void OnPreferenceChanged(const std::string& pref_name);
+
+ // Saves local image preferences to sync.
+ void UpdateSyncedImageFromLocal();
+
+ // Saves sync preferences to local state and updates user image.
+ void UpdateLocalImageFromSynced();
+
+ // Gets synced image index. Returns false if user has no needed preferences.
+ bool GetSyncedImageIndex(int* result);
+
+ // If it is allowed to change user image now.
+ bool CanUpdateLocalImageNow();
+
+ const User* user_;
+ scoped_ptr<PrefChangeRegistrar> pref_change_registrar_;
+ scoped_ptr<content::NotificationRegistrar> notification_registrar_;
+ PrefServiceSyncable* prefs_;
+ bool is_synced_;
+ // Indicates if local user image changed during initialization.
+ bool local_image_changed_;
+ ObserverList<Observer> observer_list_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_LOGIN_USER_IMAGE_SYNC_OBSERVER_H_
+
diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc
index cdcf4bb..90ed473 100644
--- a/chrome/browser/chromeos/login/user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/user_manager_impl.cc
@@ -269,6 +269,7 @@ void UserManagerImpl::Shutdown() {
if (observed_sync_service_)
observed_sync_service_->RemoveObserver(this);
+ user_image_manager_->Shutdown();
}
UserImageManager* UserManagerImpl::GetUserImageManager() {
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 8e3fcb3..12365c3 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -131,6 +131,7 @@
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/user_image_manager.h"
+#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/wallpaper_manager.h"
#include "chrome/browser/chromeos/net/proxy_config_handler.h"
@@ -387,6 +388,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
extensions::EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
RegisterProfilePrefs(registry);
FlagsUI::RegisterProfilePrefs(registry);
+ chromeos::UserImageSyncObserver::RegisterProfilePrefs(registry);
#endif
#if defined(OS_WIN)
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css
index 94578bd..9c69a8f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.css
@@ -9,6 +9,11 @@
width: 702px;
}
+#user-image.loading {
+ min-height: 609px; /* Should be the same as #gaia-signin height. */
+ width: 722px; /* Should be the same as #gaia-signin width. */
+}
+
#user-image-screen-curtain {
-webkit-margin-start: 8px;
}
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js
index 2785605..be2f1a8 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js
@@ -199,7 +199,8 @@ cr.define('login', function() {
} else {
$('ok-button').disabled = false;
chrome.send('selectImage',
- [imageGrid.selectedItemUrl, imageGrid.selectionType]);
+ [imageGrid.selectedItemUrl, imageGrid.selectionType,
+ !imageGrid.inProgramSelection]);
}
// Start/stop camera on (de)selection.
if (!imageGrid.inProgramSelection &&
@@ -384,7 +385,7 @@ cr.define('login', function() {
item.author = data.author || '';
item.website = data.website || '';
}
- this.classList.remove('loading');
+ chrome.send('screenReady');
},
/**
@@ -399,6 +400,15 @@ cr.define('login', function() {
},
/**
+ * Hides curtain with spinner.
+ * @private
+ */
+ hideCurtain_: function() {
+ this.classList.remove('loading');
+ Oobe.getInstance().updateScreenSize(this);
+ },
+
+ /**
* Updates the image preview caption.
* @private
*/
@@ -432,6 +442,7 @@ cr.define('login', function() {
'setProfilePictureEnabled',
'setProfileImage',
'setSelectedImage',
+ 'hideCurtain'
].forEach(function(name) {
UserImageScreen[name] = function(value) {
$('user-image')[name + '_'](value);
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
index af26cc3..c5891a4 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
@@ -4,9 +4,9 @@
*/
#gaia-signin {
- height: 609px;
+ height: 609px; /* Should be the same as #user-image.loading min-heigth. */
padding: 70px 17px 69px; /* Screen has no controls. */
- width: 722px;
+ width: 722px; /* Should be the same as #user-image.loading width. */
}
#gaia-signin.no-right-panel {
@@ -118,4 +118,5 @@
#createManagedUserLinkPlaceholder,
#createManagedUserNoManagerText {
margin-top: 10px;
-} \ No newline at end of file
+}
+
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
index 44cb0181..75b3152 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -30,7 +30,8 @@ namespace chromeos {
UserImageScreenHandler::UserImageScreenHandler()
: BaseScreenHandler(kJsScreenPath),
screen_(NULL),
- show_on_init_(false) {
+ show_on_init_(false),
+ is_ready_(false) {
}
UserImageScreenHandler::~UserImageScreenHandler() {
@@ -62,6 +63,8 @@ void UserImageScreenHandler::Show() {
if (!screen_)
return;
screen_->CheckCameraPresence();
+ if (is_ready_)
+ screen_->OnScreenReady();
}
void UserImageScreenHandler::Hide() {
@@ -94,6 +97,7 @@ void UserImageScreenHandler::DeclareLocalizedValues(
void UserImageScreenHandler::RegisterMessages() {
AddCallback("getImages", &UserImageScreenHandler::HandleGetImages);
+ AddCallback("screenReady", &UserImageScreenHandler::HandleScreenReady);
AddCallback("photoTaken", &UserImageScreenHandler::HandlePhotoTaken);
AddCallback("selectImage", &UserImageScreenHandler::HandleSelectImage);
AddCallback("checkCameraPresence",
@@ -146,6 +150,12 @@ void UserImageScreenHandler::HandleGetImages() {
OnProfileImageAbsent();
}
+void UserImageScreenHandler::HandleScreenReady() {
+ is_ready_ = true;
+ if (screen_)
+ screen_->OnScreenReady();
+}
+
void UserImageScreenHandler::HandlePhotoTaken(const std::string& image_url) {
std::string mime_type, charset, raw_data;
if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
@@ -163,9 +173,10 @@ void UserImageScreenHandler::HandleCheckCameraPresence() {
}
void UserImageScreenHandler::HandleSelectImage(const std::string& image_url,
- const std::string& image_type) {
+ const std::string& image_type,
+ bool is_user_selection) {
if (screen_)
- screen_->OnImageSelected(image_type, image_url);
+ screen_->OnImageSelected(image_type, image_url, is_user_selection);
}
void UserImageScreenHandler::HandleImageAccepted() {
@@ -185,6 +196,10 @@ void UserImageScreenHandler::SetCameraPresent(bool present) {
CallJS("setCameraPresent", present);
}
+void UserImageScreenHandler::HideCurtain() {
+ CallJS("hideCurtain");
+}
+
void UserImageScreenHandler::SetProfilePictureEnabled(bool enabled) {
CallJS("setProfilePictureEnabled", enabled);
}
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
index 9ac7c64..8a0959c9 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
@@ -47,11 +47,16 @@ class UserImageScreenHandler : public UserImageScreenActor,
virtual void SetCameraPresent(bool enabled) OVERRIDE;
+ virtual void HideCurtain() OVERRIDE;
+
private:
// Sends image data to the page.
void HandleGetImages();
+ // Screen ready to be shown.
+ void HandleScreenReady();
+
// Handles photo taken with WebRTC UI.
void HandlePhotoTaken(const std::string& image_url);
@@ -60,7 +65,8 @@ class UserImageScreenHandler : public UserImageScreenActor,
// Handles clicking on default user image.
void HandleSelectImage(const std::string& image_url,
- const std::string& image_type);
+ const std::string& image_type,
+ bool is_user_selection);
// Called when user accept the image closing the screen.
void HandleImageAccepted();
@@ -73,6 +79,9 @@ class UserImageScreenHandler : public UserImageScreenActor,
// Keeps whether screen should be shown right after initialization.
bool show_on_init_;
+ // Keeps whether screen has loaded all default images and redy to be shown.
+ bool is_ready_;
+
base::Time screen_show_time_;
DISALLOW_COPY_AND_ASSIGN(UserImageScreenHandler);
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
index e75e1e9..537feab 100644
--- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
@@ -79,6 +79,8 @@ ChangePictureOptionsHandler::ChangePictureOptionsHandler()
content::NotificationService::AllSources());
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
content::NotificationService::AllSources());
+ registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
+ content::NotificationService::AllSources());
}
ChangePictureOptionsHandler::~ChangePictureOptionsHandler() {
@@ -411,6 +413,11 @@ void ChangePictureOptionsHandler::Observe(
// User profile image has been updated.
SendProfileImage(*content::Details<const gfx::ImageSkia>(details).ptr(),
false);
+ } else if (type == chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) {
+ // Not initialized yet.
+ if (previous_image_index_ == User::kInvalidImageIndex)
+ return;
+ SendSelectedImage();
}
}
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 51c3e7b..0a282de 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -572,6 +572,8 @@
'browser/chromeos/login/user_image_manager.cc',
'browser/chromeos/login/user_image_manager_impl.cc',
'browser/chromeos/login/user_image_manager_impl.h',
+ 'browser/chromeos/login/user_image_sync_observer.cc',
+ 'browser/chromeos/login/user_image_sync_observer.h',
'browser/chromeos/login/user_flow.cc',
'browser/chromeos/login/user_flow.h',
'browser/chromeos/login/user_manager.cc',
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 63945db..c4bec2a 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -195,5 +195,8 @@ const char kStubCrosSettings[] = "stub-cros-settings";
const char kUseNewNetworkConfigurationHandlers[] =
"use-new-network-configuration-handlers";
+// Disables user image sync.
+const char kDisableUserImageSync[] = "disable-user-image-sync";
+
} // namespace switches
} // namespace chromeos
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 4d4b57f..1e99663 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -72,6 +72,7 @@ CHROMEOS_EXPORT extern const char kSkipHWIDCheck[];
CHROMEOS_EXPORT extern const char kSmsTestMessages[];
CHROMEOS_EXPORT extern const char kStubCrosSettings[];
CHROMEOS_EXPORT extern const char kUseNewNetworkConfigurationHandlers[];
+CHROMEOS_EXPORT extern const char kDisableUserImageSync[];
} // namespace switches
} // namespace chromeos